// -*- c -*-
/*=========================================================================
 *
 *  Program:   Visualization Toolkit
 *  Module:    cdi.h
 *
 *  Copyright (c) 2018 Niklas Roeber, DKRZ Hamburg
 *  All rights reserved.
 *  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
 *
 *     This software is distributed WITHOUT ANY WARRANTY; without even
 *     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 *     PURPOSE.  See the above copyright notice for more information.
 *
 *  =========================================================================*/
// .NAME cdilib.c - part of the ICON/CDI netCDF reader

#if defined(_WIN32) || defined(_WIN64)
#define restrict
#define ssize_t long
#define __func__ __FUNCTION__
#define inline __inline
#else
#define HAVE_UNISTD_H
#endif

#ifdef _ARCH_PWR6
#pragma options nostrict
#endif

#ifdef  HAVE_CONFIG_H
#include "config.h"
#endif

#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif

#ifdef  HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <float.h>
#include <math.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdbool.h>
#include <assert.h>

#ifdef HAVE_LIBGRIB_API
#include <grib_api.h>
#endif

#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif

#ifdef HAVE_LIBPTHREAD
#include <pthread.h>
#endif

#ifdef HAVE_LIBSZ
#include <szlib.h>
#endif

static void tableDefault(void) {}



#ifndef  CDI_H_
#define  CDI_H_

#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>

#ifdef __cplusplus
extern "C" {
#endif


#define  CDI_MAX_NAME             256

#define  CDI_UNDEFID               -1
#define  CDI_GLOBAL                -1



#define  CDI_BIGENDIAN              0
#define  CDI_LITTLEENDIAN           1
#define  CDI_PDPENDIAN              2

#define  CDI_REAL                   1
#define  CDI_COMP                   2
#define  CDI_BOTH                   3



#define	 CDI_NOERR        	    0
#define  CDI_EEOF                  -1
#define  CDI_ETMOF                 -9
#define  CDI_ESYSTEM              -10
#define  CDI_EINVAL               -20
#define  CDI_EISDIR               -21
#define  CDI_EISEMPTY             -22
#define  CDI_EUFTYPE              -23
#define  CDI_ELIBNAVAIL           -24
#define  CDI_EUFSTRUCT            -25
#define  CDI_EUNC4                -26
#define  CDI_EDIMSIZE             -27
#define  CDI_ELIMIT               -99



#define  CDI_FILETYPE_GRB           1
#define  CDI_FILETYPE_GRB2          2
#define  CDI_FILETYPE_NC            3
#define  CDI_FILETYPE_NC2           4
#define  CDI_FILETYPE_NC4           5
#define  CDI_FILETYPE_NC4C          6
#define  CDI_FILETYPE_NC5           7
#define  CDI_FILETYPE_SRV           8
#define  CDI_FILETYPE_EXT           9
#define  CDI_FILETYPE_IEG          10



#define  CDI_COMPRESS_NONE          0
#define  CDI_COMPRESS_SZIP          1
#define  CDI_COMPRESS_AEC           2
#define  CDI_COMPRESS_ZIP           3
#define  CDI_COMPRESS_JPEG          4



#define  CDI_DATATYPE_PACK          0
#define  CDI_DATATYPE_PACK1         1
#define  CDI_DATATYPE_PACK2         2
#define  CDI_DATATYPE_PACK3         3
#define  CDI_DATATYPE_PACK4         4
#define  CDI_DATATYPE_PACK5         5
#define  CDI_DATATYPE_PACK6         6
#define  CDI_DATATYPE_PACK7         7
#define  CDI_DATATYPE_PACK8         8
#define  CDI_DATATYPE_PACK9         9
#define  CDI_DATATYPE_PACK10       10
#define  CDI_DATATYPE_PACK11       11
#define  CDI_DATATYPE_PACK12       12
#define  CDI_DATATYPE_PACK13       13
#define  CDI_DATATYPE_PACK14       14
#define  CDI_DATATYPE_PACK15       15
#define  CDI_DATATYPE_PACK16       16
#define  CDI_DATATYPE_PACK17       17
#define  CDI_DATATYPE_PACK18       18
#define  CDI_DATATYPE_PACK19       19
#define  CDI_DATATYPE_PACK20       20
#define  CDI_DATATYPE_PACK21       21
#define  CDI_DATATYPE_PACK22       22
#define  CDI_DATATYPE_PACK23       23
#define  CDI_DATATYPE_PACK24       24
#define  CDI_DATATYPE_PACK25       25
#define  CDI_DATATYPE_PACK26       26
#define  CDI_DATATYPE_PACK27       27
#define  CDI_DATATYPE_PACK28       28
#define  CDI_DATATYPE_PACK29       29
#define  CDI_DATATYPE_PACK30       30
#define  CDI_DATATYPE_PACK31       31
#define  CDI_DATATYPE_PACK32       32
#define  CDI_DATATYPE_CPX32        64
#define  CDI_DATATYPE_CPX64       128
#define  CDI_DATATYPE_FLT32       132
#define  CDI_DATATYPE_FLT64       164
#define  CDI_DATATYPE_INT8        208
#define  CDI_DATATYPE_INT16       216
#define  CDI_DATATYPE_INT32       232
#define  CDI_DATATYPE_UINT8       308
#define  CDI_DATATYPE_UINT16      316
#define  CDI_DATATYPE_UINT32      332


#define  CDI_DATATYPE_INT         251
#define  CDI_DATATYPE_FLT         252
#define  CDI_DATATYPE_TXT         253
#define  CDI_DATATYPE_CPX         254
#define  CDI_DATATYPE_UCHAR       255
#define  CDI_DATATYPE_LONG        256
#define  CDI_DATATYPE_UINT        257



#define  CDI_CHUNK_AUTO             1
#define  CDI_CHUNK_GRID             2
#define  CDI_CHUNK_LINES            3



#define  GRID_GENERIC               1
#define  GRID_GAUSSIAN              2
#define  GRID_GAUSSIAN_REDUCED      3
#define  GRID_LONLAT                4
#define  GRID_SPECTRAL              5
#define  GRID_FOURIER               6
#define  GRID_GME                   7
#define  GRID_TRAJECTORY            8
#define  GRID_UNSTRUCTURED          9
#define  GRID_CURVILINEAR          10
#define  GRID_PROJECTION           12
#define  GRID_CHARXY               13

#define  CDI_PROJ_RLL              21
#define  CDI_PROJ_LCC              22
#define  CDI_PROJ_LAEA             23
#define  CDI_PROJ_SINU             24
#define  CDI_PROJ_STERE            25



#define  ZAXIS_SURFACE              0
#define  ZAXIS_GENERIC              1
#define  ZAXIS_HYBRID               2
#define  ZAXIS_HYBRID_HALF          3
#define  ZAXIS_PRESSURE             4
#define  ZAXIS_HEIGHT               5
#define  ZAXIS_DEPTH_BELOW_SEA      6
#define  ZAXIS_DEPTH_BELOW_LAND     7
#define  ZAXIS_ISENTROPIC           8
#define  ZAXIS_TRAJECTORY           9
#define  ZAXIS_ALTITUDE            10
#define  ZAXIS_SIGMA               11
#define  ZAXIS_MEANSEA             12
#define  ZAXIS_TOA                 13
#define  ZAXIS_SEA_BOTTOM          14
#define  ZAXIS_ATMOSPHERE          15
#define  ZAXIS_CLOUD_BASE          16
#define  ZAXIS_CLOUD_TOP           17
#define  ZAXIS_ISOTHERM_ZERO       18
#define  ZAXIS_SNOW                19
#define  ZAXIS_LAKE_BOTTOM         20
#define  ZAXIS_SEDIMENT_BOTTOM     21
#define  ZAXIS_SEDIMENT_BOTTOM_TA  22
#define  ZAXIS_SEDIMENT_BOTTOM_TW  23
#define  ZAXIS_MIX_LAYER           24
#define  ZAXIS_REFERENCE           25
#define  ZAXIS_CHAR                26



enum {
SUBTYPE_TILES                   = 0
};

#define MAX_KV_PAIRS_MATCH 10


typedef struct  {
int nAND;
int key_value_pairs[2][MAX_KV_PAIRS_MATCH];
} subtype_query_t;





#define  TIME_CONSTANT              0
#define  TIME_VARYING               1
#define  TIME_VARIABLE              1



#define  TSTEP_INSTANT              1
#define  TSTEP_AVG                  2
#define  TSTEP_ACCUM                3
#define  TSTEP_MAX                  4
#define  TSTEP_MIN                  5
#define  TSTEP_DIFF                 6
#define  TSTEP_RMS                  7
#define  TSTEP_SD                   8
#define  TSTEP_COV                  9
#define  TSTEP_RATIO               10
#define  TSTEP_RANGE               11
#define  TSTEP_INSTANT2            12
#define  TSTEP_INSTANT3            13



#define  TAXIS_ABSOLUTE           1
#define  TAXIS_RELATIVE           2
#define  TAXIS_FORECAST           3



#define  TUNIT_SECOND             1
#define  TUNIT_MINUTE             2
#define  TUNIT_QUARTER            3
#define  TUNIT_30MINUTES          4
#define  TUNIT_HOUR               5
#define  TUNIT_3HOURS             6
#define  TUNIT_6HOURS             7
#define  TUNIT_12HOURS            8
#define  TUNIT_DAY                9
#define  TUNIT_MONTH             10
#define  TUNIT_YEAR              11



#define  CALENDAR_STANDARD        0
#define  CALENDAR_GREGORIAN       1
#define  CALENDAR_PROLEPTIC       2
#define  CALENDAR_360DAYS         3
#define  CALENDAR_365DAYS         4
#define  CALENDAR_366DAYS         5
#define  CALENDAR_NONE            6


#define  CDI_UUID_SIZE           16



typedef struct CdiParam { int discipline; int category; int number; } CdiParam;



typedef struct CdiIterator CdiIterator;
typedef struct CdiGribIterator CdiGribIterator;



void    cdiReset(void);

const char *cdiStringError(int cdiErrno);

void    cdiDebug(int debug);

const char *cdiLibraryVersion(void);
void    cdiPrintVersion(void);

int     cdiHaveFiletype(int filetype);

void    cdiDefMissval(double missval);
double  cdiInqMissval(void);
double  cdiInqGridMissval(void);
void    cdiDefGlobal(const char *string, int val);

int     namespaceNew(void);
void    namespaceSetActive(int namespaceID);
int     namespaceGetActive(void);
void    namespaceDelete(int namespaceID);






void    cdiParamToString(int param, char *paramstr, int maxlen);

void    cdiDecodeParam(int param, int *pnum, int *pcat, int *pdis);
int     cdiEncodeParam(int pnum, int pcat, int pdis);




void    cdiDecodeDate(int64_t date, int *year, int *month, int *day);
int64_t cdiEncodeDate(int year, int month, int day);

void    cdiDecodeTime(int time, int *hour, int *minute, int *second);
int     cdiEncodeTime(int hour, int minute, int second);




int     cdiGetFiletype(const char *path, int *byteorder);


int     streamOpenRead(const char *path);


int     streamOpenWrite(const char *path, int filetype);

int     streamOpenAppend(const char *path);


void    streamClose(int streamID);


void    streamSync(int streamID);


void    streamDefVlist(int streamID, int vlistID);


int     streamInqVlist(int streamID);


int     streamInqFiletype(int streamID);


void    streamDefByteorder(int streamID, int byteorder);


int     streamInqByteorder(int streamID);


void    streamDefCompType(int streamID, int comptype);


int     streamInqCompType(int streamID);


void    streamDefCompLevel(int streamID, int complevel);


int     streamInqCompLevel(int streamID);


int     streamDefTimestep(int streamID, int tsID);


int     streamInqTimestep(int streamID, int tsID);


int     streamInqCurTimestepID(int streamID);

const char *streamFilename(int streamID);
const char *streamFilesuffix(int filetype);

size_t  streamNvals(int streamID);

int     streamInqNvars(int streamID);




void    streamWriteVar(int streamID, int varID, const double data[], size_t nmiss);
void    streamWriteVarF(int streamID, int varID, const float data[], size_t nmiss);


void    streamReadVar(int streamID, int varID, double data[], size_t *nmiss);
void    streamReadVarF(int streamID, int varID, float data[], size_t *nmiss);
void    streamReadVarPart(int streamID, int varID, int varType, int start, size_t size, void *data, size_t *nmiss, int memtype);


void    streamWriteVarSlice(int streamID, int varID, int levelID, const double data[], size_t nmiss);
void    streamWriteVarSliceF(int streamID, int varID, int levelID, const float data[], size_t nmiss);
void    streamReadVarSlicePart(int streamID, int varID, int levelID, int varType, int start, size_t size, void *data, size_t *nmiss, int memtype);


void    streamReadVarSlice(int streamID, int varID, int levelID, double data[], size_t *nmiss);
void    streamReadVarSliceF(int streamID, int varID, int levelID, float data[], size_t *nmiss);

void    streamWriteVarChunk(int streamID, int varID, const int rect[3][2], const double data[], size_t nmiss);




void    streamDefRecord(int streamID, int  varID, int  levelID);
void    streamInqRecord(int streamID, int *varID, int *levelID);
void    streamWriteRecord(int streamID, const double data[], size_t nmiss);
void    streamWriteRecordF(int streamID, const float data[], size_t nmiss);
void    streamReadRecord(int streamID, double data[], size_t *nmiss);
void    streamReadRecordF(int streamID, float data[], size_t *nmiss);
void    streamCopyRecord(int streamIDdest, int streamIDsrc);

void    streamInqGRIBinfo(int streamID, int *intnum, float *fltnum, off_t *bignum);





CdiIterator *cdiIterator_new(const char *path);
CdiIterator *cdiIterator_clone(CdiIterator *me);
char *cdiIterator_serialize(CdiIterator *me);
CdiIterator *cdiIterator_deserialize(const char *description);
void cdiIterator_print(CdiIterator *me, FILE *stream);
void cdiIterator_delete(CdiIterator *me);


int cdiIterator_nextField(CdiIterator *me);



char *cdiIterator_inqStartTime(CdiIterator *me);
char *cdiIterator_inqEndTime(CdiIterator *me);
char *cdiIterator_inqRTime(CdiIterator *me);
char *cdiIterator_inqVTime(CdiIterator *me);
int cdiIterator_inqLevelType(CdiIterator *me, int levelSelector, char **outName_optional, char **outLongName_optional, char **outStdName_optional, char **outUnit_optional);
int cdiIterator_inqLevel(CdiIterator *me, int levelSelector, double *outValue1_optional, double *outValue2_optional);
int cdiIterator_inqLevelUuid(CdiIterator *me, int *outVgridNumber_optional, int *outLevelCount_optional, unsigned char outUuid_optional[CDI_UUID_SIZE]);
int cdiIterator_inqTile(CdiIterator *me, int *outTileIndex, int *outTileAttribute);
int cdiIterator_inqTileCount(CdiIterator *me, int *outTileCount, int *outTileAttributeCount);
CdiParam cdiIterator_inqParam(CdiIterator *me);
void cdiIterator_inqParamParts(CdiIterator *me, int *outDiscipline, int *outCategory, int *outNumber);
int cdiIterator_inqDatatype(CdiIterator *me);
int cdiIterator_inqFiletype(CdiIterator *me);
int cdiIterator_inqTsteptype(CdiIterator *me);
char *cdiIterator_inqVariableName(CdiIterator *me);
int cdiIterator_inqGridId(CdiIterator *me);


void cdiIterator_readField(CdiIterator *me, double data[], size_t *nmiss_optional);
void cdiIterator_readFieldF(CdiIterator *me, float data[], size_t *nmiss_optional);




CdiGribIterator *cdiGribIterator_clone(CdiIterator *me);
void cdiGribIterator_delete(CdiGribIterator *me);


int cdiGribIterator_getLong(CdiGribIterator *me, const char *key, long *value);
int cdiGribIterator_getDouble(CdiGribIterator *me, const char *key, double *value);
int cdiGribIterator_getLength(CdiGribIterator *me, const char *key, size_t *value);
int cdiGribIterator_getString(CdiGribIterator *me, const char *key, char *value, size_t *length);
int cdiGribIterator_getSize(CdiGribIterator *me, const char *key, size_t *value);
int cdiGribIterator_getLongArray(CdiGribIterator *me, const char *key, long *value, size_t *array_size);
int cdiGribIterator_getDoubleArray(CdiGribIterator *me, const char *key, double *value, size_t *array_size);


int cdiGribIterator_inqEdition(CdiGribIterator *me);
long cdiGribIterator_inqLongValue(CdiGribIterator *me, const char *key);
long cdiGribIterator_inqLongDefaultValue(CdiGribIterator *me, const char *key, long defaultValue);
double cdiGribIterator_inqDoubleValue(CdiGribIterator *me, const char *key);
double cdiGribIterator_inqDoubleDefaultValue(CdiGribIterator *me, const char *key, double defaultValue);
char *cdiGribIterator_inqStringValue(CdiGribIterator *me, const char *key);




int     vlistCreate(void);


void    vlistDestroy(int vlistID);


int     vlistDuplicate(int vlistID);


void    vlistCopy(int vlistID2, int vlistID1);


void    vlistCopyFlag(int vlistID2, int vlistID1);

void    vlistClearFlag(int vlistID);


void    vlistCat(int vlistID2, int vlistID1);


void    vlistMerge(int vlistID2, int vlistID1);

void    vlistPrint(int vlistID);


int     vlistNumber(int vlistID);


int     vlistNvars(int vlistID);


int     vlistNgrids(int vlistID);


int     vlistNzaxis(int vlistID);


int     vlistNsubtypes(int vlistID);

void    vlistDefNtsteps(int vlistID, int nts);
int     vlistNtsteps(int vlistID);
size_t  vlistGridsizeMax(int vlistID);
int     vlistGrid(int vlistID, int index);
int     vlistGridIndex(int vlistID, int gridID);
void    vlistChangeGridIndex(int vlistID, int index, int gridID);
void    vlistChangeGrid(int vlistID, int gridID1, int gridID2);
int     vlistZaxis(int vlistID, int index);
int     vlistZaxisIndex(int vlistID, int zaxisID);
void    vlistChangeZaxisIndex(int vlistID, int index, int zaxisID);
void    vlistChangeZaxis(int vlistID, int zaxisID1, int zaxisID2);
int     vlistNrecs(int vlistID);
int     vlistSubtype(int vlistID, int index);
int     vlistSubtypeIndex(int vlistID, int subtypeID);


void    vlistDefTaxis(int vlistID, int taxisID);


int     vlistInqTaxis(int vlistID);

void    vlistDefTable(int vlistID, int tableID);
int     vlistInqTable(int vlistID);
void    vlistDefInstitut(int vlistID, int instID);
int     vlistInqInstitut(int vlistID);
void    vlistDefModel(int vlistID, int modelID);
int     vlistInqModel(int vlistID);





int     vlistDefVarTiles(int vlistID, int gridID, int zaxisID, int timetype, int tilesetID);


int     vlistDefVar(int vlistID, int gridID, int zaxisID, int timetype);

void    vlistChangeVarGrid(int vlistID, int varID, int gridID);
void    vlistChangeVarZaxis(int vlistID, int varID, int zaxisID);

void    vlistInqVar(int vlistID, int varID, int *gridID, int *zaxisID, int *timetype);
int     vlistInqVarGrid(int vlistID, int varID);
int     vlistInqVarZaxis(int vlistID, int varID);


int     vlistInqVarID(int vlistID, int code);

void    vlistDefVarTimetype(int vlistID, int varID, int timetype);
int     vlistInqVarTimetype(int vlistID, int varID);

void    vlistDefVarTsteptype(int vlistID, int varID, int tsteptype);


int     vlistInqVarTsteptype(int vlistID, int varID);

void    vlistDefVarCompType(int vlistID, int varID, int comptype);
int     vlistInqVarCompType(int vlistID, int varID);
void    vlistDefVarCompLevel(int vlistID, int varID, int complevel);
int     vlistInqVarCompLevel(int vlistID, int varID);


void    vlistDefVarParam(int vlistID, int varID, int param);


int     vlistInqVarParam(int vlistID, int varID);


void    vlistDefVarCode(int vlistID, int varID, int code);


int     vlistInqVarCode(int vlistID, int varID);


void    vlistDefVarDatatype(int vlistID, int varID, int datatype);


int     vlistInqVarDatatype(int vlistID, int varID);

void    vlistDefVarChunkType(int vlistID, int varID, int chunktype);
int     vlistInqVarChunkType(int vlistID, int varID);

void    vlistDefVarXYZ(int vlistID, int varID, int xyz);
int     vlistInqVarXYZ(int vlistID, int varID);

int     vlistInqVarNumber(int vlistID, int varID);

void    vlistDefVarInstitut(int vlistID, int varID, int instID);
int     vlistInqVarInstitut(int vlistID, int varID);
void    vlistDefVarModel(int vlistID, int varID, int modelID);
int     vlistInqVarModel(int vlistID, int varID);
void    vlistDefVarTable(int vlistID, int varID, int tableID);
int     vlistInqVarTable(int vlistID, int varID);


void    vlistDefVarName(int vlistID, int varID, const char *name);


void    vlistInqVarName(int vlistID, int varID, char *name);


char   *vlistCopyVarName(int vlistId, int varId);


void    vlistDefVarStdname(int vlistID, int varID, const char *stdname);


void    vlistInqVarStdname(int vlistID, int varID, char *stdname);


void    vlistDefVarLongname(int vlistID, int varID, const char *longname);


void    vlistInqVarLongname(int vlistID, int varID, char *longname);


void    vlistDefVarUnits(int vlistID, int varID, const char *units);


void    vlistInqVarUnits(int vlistID, int varID, char *units);


void    vlistDefVarMissval(int vlistID, int varID, double missval);


double  vlistInqVarMissval(int vlistID, int varID);


void    vlistDefVarExtra(int vlistID, int varID, const char *extra);


void    vlistInqVarExtra(int vlistID, int varID, char *extra);

void    vlistDefVarScalefactor(int vlistID, int varID, double scalefactor);
double  vlistInqVarScalefactor(int vlistID, int varID);
void    vlistDefVarAddoffset(int vlistID, int varID, double addoffset);
double  vlistInqVarAddoffset(int vlistID, int varID);

void    vlistDefVarTimave(int vlistID, int varID, int timave);
int     vlistInqVarTimave(int vlistID, int varID);

size_t  vlistInqVarSize(int vlistID, int varID);

void    vlistDefIndex(int vlistID, int varID, int levID, int index);
int     vlistInqIndex(int vlistID, int varID, int levID);
void    vlistDefFlag(int vlistID, int varID, int levID, int flag);
int     vlistInqFlag(int vlistID, int varID, int levID);
int     vlistFindVar(int vlistID, int fvarID);
int     vlistFindLevel(int vlistID, int fvarID, int flevelID);
int     vlistMergedVar(int vlistID, int varID);
int     vlistMergedLevel(int vlistID, int varID, int levelID);


void    cdiClearAdditionalKeys(void);

void    cdiDefAdditionalKey(const char *string);


void    vlistDefVarIntKey(int vlistID, int varID, const char *name, int value);

void    vlistDefVarDblKey(int vlistID, int varID, const char *name, double value);


int     vlistHasVarKey(int vlistID, int varID, const char *name);

double  vlistInqVarDblKey(int vlistID, int varID, const char *name);

int     vlistInqVarIntKey(int vlistID, int varID, const char *name);




int     cdiInqNatts(int cdiID, int varID, int *nattsp);

int     cdiInqAtt(int cdiID, int varID, int attrnum, char *name, int *typep, int *lenp);
int     cdiDelAtt(int cdiID, int varID, const char *name);

int     cdiCopyAtts(int cdiID1, int varID1, int cdiID2, int varID2);


int     cdiDefAttInt(int cdiID, int varID, const char *name, int type, int len, const int ip[]);

int     cdiDefAttFlt(int cdiID, int varID, const char *name, int type, int len, const double dp[]);

int     cdiDefAttTxt(int cdiID, int varID, const char *name, int len, const char *tp_cbuf);


int     cdiInqAttInt(int cdiID, int varID, const char *name, int mlen, int ip[]);

int     cdiInqAttFlt(int cdiID, int varID, const char *name, int mlen, double dp[]);

int     cdiInqAttTxt(int cdiID, int varID, const char *name, int mlen, char *tp_cbuf);




void    gridName(int gridtype, char *gridname);
const char *gridNamePtr(int gridtype);

void    gridCompress(int gridID);

void    gridDefMaskGME(int gridID, const int mask[]);
int     gridInqMaskGME(int gridID, int mask[]);

void    gridDefMask(int gridID, const int mask[]);
int     gridInqMask(int gridID, int mask[]);

void    gridPrint(int gridID, int opt);


int     gridCreate(int gridtype, size_t size);


void    gridDestroy(int gridID);


int     gridDuplicate(int gridID);


void    gridDefProj(int gridID, int projID);


int     gridInqProj(int gridID);


int     gridInqProjType(int gridID);


int     gridInqType(int gridID);


size_t  gridInqSize(int gridID);


void    gridDefXsize(int gridID, size_t xsize);


size_t  gridInqXsize(int gridID);


void    gridDefYsize(int gridID, size_t ysize);


size_t  gridInqYsize(int gridID);


void    gridDefNP(int gridID, int np);


int     gridInqNP(int gridID);


void    gridDefXvals(int gridID, const double xvals[]);


size_t  gridInqXvals(int gridID, double xvals[]);
size_t  gridInqXvalsPart(int gridID, int start, size_t size, double xvals[]);


int     gridInqXIsc(int gridID);


size_t  gridInqXCvals(int gridID, char *xcvals[]);


void    gridDefYvals(int gridID, const double yvals[]);


size_t  gridInqYvals(int gridID, double yvals[]);
size_t  gridInqYvalsPart(int gridID, int start, size_t size, double yvals[]);


int     gridInqYIsc(int gridID);


size_t  gridInqYCvals(int gridID, char *ycvals[]);


#define  CDI_KEY_TABLESVERSION                 801
#define  CDI_KEY_LOCALTABLESVERSION            802
#define  CDI_KEY_TYPEOFGENERATINGPROCESS       803
#define  CDI_KEY_PRODUCTDEFINITIONTEMPLATE     804
#define  CDI_KEY_TYPEOFPROCESSEDDATA           805
#define  CDI_KEY_SHAPEOFTHEEARTH               806
#define  CDI_KEY_BACKGROUNDPROCESS             807
#define  CDI_KEY_TYPEOFENSEMBLEFORECAST        808
#define  CDI_KEY_NUMBEROFFORECASTSINENSEMBLE   809
#define  CDI_KEY_PERTURBATIONNUMBER            810
#define  CDI_KEY_CENTRE                        811
#define  CDI_KEY_SUBCENTRE                     812
#define  CDI_KEY_MPIMTYPE                      813
#define  CDI_KEY_MPIMCLASS                     814
#define  CDI_KEY_MPIMUSER                      815
#define  CDI_KEY_REVSTATUS                     816
#define  CDI_KEY_REVNUMBER                     817
#define  CDI_KEY_GRIB2LOCALSECTIONNUMBER       818
#define  CDI_KEY_SECTION2PADDINGLENGTH         819
#define  CDI_KEY_SECTION2PADDING               820


int     cdiDefKeyInt(int cdiID, int varID, int key, int value);


int     cdiInqKeyInt(int cdiID, int varID, int key, int *value);


int     cdiDefKeyBytes(int cdiID, int varID, int key, const unsigned char bytes[], int length);


int     cdiInqKeyBytes(int cdiID, int varID, int key, unsigned char bytes[], int *length);


int     cdiDefKeyString(int cdiID, int varID, int key, const char *string);


int     cdiInqKeyString(int cdiID, int varID, int key, char *string, int *length);


int     cdiInqKeyLen(int cdiID, int varID, int key, int *length);

int     cdiCopyKeys(int cdiID1, int varID1, int cdiID2, int varID2);


#define  CDI_KEY_XNAME       901
#define  CDI_KEY_XDIMNAME    902
#define  CDI_KEY_XLONGNAME   903
#define  CDI_KEY_XUNITS      904
#define  CDI_KEY_YNAME       911
#define  CDI_KEY_YDIMNAME    912
#define  CDI_KEY_YLONGNAME   913
#define  CDI_KEY_YUNITS      914
#define  CDI_KEY_VDIMNAME    920
#define  CDI_KEY_MAPPING     921
#define  CDI_KEY_MAPNAME     922


#define  CDI_KEY_NAME        941
#define  CDI_KEY_DIMNAME     942
#define  CDI_KEY_LONGNAME    943
#define  CDI_KEY_UNITS       944
#define  CDI_KEY_PSNAME      950
#define  CDI_KEY_P0NAME      951
#define  CDI_KEY_P0VALUE     952


int     cdiGridDefKeyStr(int gridID, int key, int size, const char *mesg);


int     cdiGridInqKeyStr(int gridID, int key, int size, char *mesg);


int     cdiZaxisDefKeyStr(int zaxisID, int key, int size, const char *mesg);


int     cdiZaxisInqKeyStr(int zaxisID, int key, int size, char *mesg);


int     cdiZaxisDefKeyFlt(int zaxisID, int key, double value);


int     cdiZaxisInqKeyFlt(int zaxisID, int key, double *value);


void    gridDefXname(int gridID, const char *xname);


void    gridInqXname(int gridID, char *xname);


void    gridDefXlongname(int gridID, const char *xlongname);


void    gridInqXlongname(int gridID, char *xlongname);


void    gridDefXunits(int gridID, const char *xunits);


void    gridInqXunits(int gridID, char *xunits);


void    gridDefYname(int gridID, const char *yname);


void    gridInqYname(int gridID, char *yname);


void    gridDefYlongname(int gridID, const char *ylongname);


void    gridInqYlongname(int gridID, char *ylongname);


void    gridDefYunits(int gridID, const char *yunits);


void    gridInqYunits(int gridID, char *yunits);


void    gridInqXstdname(int gridID, char *xstdname);


void    gridInqYstdname(int gridID, char *ystdname);


void    gridDefDatatype(int gridID, int prec);


int     gridInqDatatype(int gridID);


double  gridInqXval(int gridID, size_t index);


double  gridInqYval(int gridID, size_t index);

double  gridInqXinc(int gridID);
double  gridInqYinc(int gridID);

int     gridIsCircular(int gridID);

int     gridInqTrunc(int gridID);
void    gridDefTrunc(int gridID, int trunc);




void    gridDefNumber(int gridID, int number);


int     gridInqNumber(int gridID);


void    gridDefPosition(int gridID, int position);


int     gridInqPosition(int gridID);


void    gridDefReference(int gridID, const char *reference);


int     gridInqReference(int gridID, char *reference);


void    gridDefUUID(int gridID, const unsigned char uuid[CDI_UUID_SIZE]);


void    gridInqUUID(int gridID, unsigned char uuid[CDI_UUID_SIZE]);


void    gridDefParamRLL(int gridID, double xpole, double ypole, double angle);
void    gridInqParamRLL(int gridID, double *xpole, double *ypole, double *angle);


void    gridDefParamGME(int gridID, int nd, int ni, int ni2, int ni3);
void    gridInqParamGME(int gridID, int *nd, int *ni, int *ni2, int *ni3);


void gridDefParamLCC(int gridID, double missval, double lon_0, double lat_0, double lat_1, double lat_2, double a, double rf, double xval_0, double yval_0, double x_0, double y_0);
int gridInqParamLCC(int gridID, double missval, double *lon_0, double *lat_0, double *lat_1, double *lat_2, double *a, double *rf, double *xval_0, double *yval_0, double *x_0, double *y_0);


void gridDefParamSTERE(int gridID, double missval, double lon_0, double lat_ts, double lat_0, double a, double xval_0, double yval_0, double x_0, double y_0);
int gridInqParamSTERE(int gridID, double missval, double *lon_0, double *lat_ts, double *lat_0, double *a, double *xval_0, double *yval_0, double *x_0, double *y_0);

void    gridDefArea(int gridID, const double area[]);
void    gridInqArea(int gridID, double area[]);
int     gridHasArea(int gridID);


void    gridDefNvertex(int gridID, int nvertex);


int     gridInqNvertex(int gridID);


void    gridDefXbounds(int gridID, const double xbounds[]);


size_t  gridInqXbounds(int gridID, double xbounds[]);
size_t  gridInqXboundsPart(int gridID, int start, size_t size, double xbounds[]);


void    gridDefYbounds(int gridID, const double ybounds[]);


size_t  gridInqYbounds(int gridID, double ybounds[]);
size_t  gridInqYboundsPart(int gridID, int start, size_t size, double ybounds[]);

void    gridDefRowlon(int gridID, int nrowlon, const int rowlon[]);
void    gridInqRowlon(int gridID, int rowlon[]);
void    gridChangeType(int gridID, int gridtype);

void    gridDefComplexPacking(int gridID, int lpack);
int     gridInqComplexPacking(int gridID);

void    gridDefUvRelativeToGrid(int gridID, int uvRelativeToGrid);
int     gridInqUvRelativeToGrid(int gridID);

void    gridDefScanningMode(int gridID, int mode);
int     gridInqScanningMode(int gridID);



void    zaxisName(int zaxistype, char *zaxisname);
const char *zaxisNamePtr(int leveltype);


int     zaxisCreate(int zaxistype, int size);


void    zaxisDestroy(int zaxisID);


int     zaxisInqType(int zaxisID);


int     zaxisInqSize(int zaxisID);


int     zaxisDuplicate(int zaxisID);

void    zaxisPrint(int zaxisID);


void    zaxisDefLevels(int zaxisID, const double levels[]);


void    zaxisDefCvals(int zaxisID, const char *cvals[], int clength);


int     zaxisInqLevels(int zaxisID, double levels[]);


int     zaxisInqCLen(int zaxisID);


int     zaxisInqCVals(int zaxisID, char ***clevels);


void    zaxisDefLevel(int zaxisID, int levelID, double levels);


double  zaxisInqLevel(int zaxisID, int levelID);


void    zaxisDefNlevRef(int gridID, int nhlev);


int     zaxisInqNlevRef(int gridID);


void    zaxisDefNumber(int gridID, int number);


int     zaxisInqNumber(int gridID);


void    zaxisDefUUID(int zaxisID, const unsigned char uuid[CDI_UUID_SIZE]);


void    zaxisInqUUID(int zaxisID, unsigned char uuid[CDI_UUID_SIZE]);


void    zaxisDefName(int zaxisID, const char *name_optional);


void    zaxisInqName(int zaxisID, char *name);


void    zaxisDefLongname(int zaxisID, const char *longname_optional);


void    zaxisInqLongname(int zaxisID, char *longname);


void    zaxisDefUnits(int zaxisID, const char *units_optional);


void    zaxisInqUnits(int zaxisID, char *units);


void    zaxisInqStdname(int zaxisID, char *stdname);

void    zaxisDefDatatype(int zaxisID, int prec);
int     zaxisInqDatatype(int zaxisID);

void    zaxisDefPositive(int zaxisID, int positive);
int     zaxisInqPositive(int zaxisID);

void    zaxisDefScalar(int zaxisID);
int     zaxisInqScalar(int zaxisID);

void    zaxisDefLtype(int zaxisID, int ltype);
int     zaxisInqLtype(int zaxisID);

void    zaxisDefVct(int zaxisID, int size, const double vct[]);
void    zaxisInqVct(int zaxisID, double vct[]);
int     zaxisInqVctSize(int zaxisID);
const double *zaxisInqVctPtr(int zaxisID);
void    zaxisDefLbounds(int zaxisID, const double lbounds[]);
int     zaxisInqLbounds(int zaxisID, double lbounds_optional[]);
double  zaxisInqLbound(int zaxisID, int index);
void    zaxisDefUbounds(int zaxisID, const double ubounds[]);
int     zaxisInqUbounds(int zaxisID, double ubounds_optional[]);
double  zaxisInqUbound(int zaxisID, int index);
void    zaxisDefWeights(int zaxisID, const double weights[]);
int     zaxisInqWeights(int zaxisID, double weights_optional[]);
void    zaxisChangeType(int zaxisID, int zaxistype);




int     taxisCreate(int taxistype);


void    taxisDestroy(int taxisID);

int     taxisDuplicate(int taxisID);

void    taxisCopyTimestep(int taxisIDdes, int taxisIDsrc);

void    taxisDefType(int taxisID, int taxistype);
int     taxisInqType(int taxisID);


void    taxisDefVdate(int taxisID, int64_t date);


void    taxisDefVtime(int taxisID, int time);


int64_t taxisInqVdate(int taxisID);


int     taxisInqVtime(int taxisID);


void    taxisDefRdate(int taxisID, int64_t date);


void    taxisDefRtime(int taxisID, int time);


int64_t taxisInqRdate(int taxisID);


int     taxisInqRtime(int taxisID);


void    taxisDefFdate(int taxisID, int64_t date);


void    taxisDefFtime(int taxisID, int time);


int64_t taxisInqFdate(int taxisID);


int     taxisInqFtime(int taxisID);

int     taxisHasBounds(int taxisID);
void    taxisWithBounds(int taxisID);

void    taxisDeleteBounds(int taxisID);

void    taxisDefVdateBounds(int taxisID, int64_t vdate_lb, int64_t vdate_ub);

void    taxisDefVtimeBounds(int taxisID, int vtime_lb, int vtime_ub);

void    taxisInqVdateBounds(int taxisID, int64_t *vdate_lb, int64_t *vdate_ub);

void    taxisInqVtimeBounds(int taxisID, int *vtime_lb, int *vtime_ub);


void    taxisDefCalendar(int taxisID, int calendar);


int     taxisInqCalendar(int taxisID);

void    taxisDefTunit(int taxisID, int tunit);
int     taxisInqTunit(int taxisID);

void    taxisDefForecastTunit(int taxisID, int tunit);
int     taxisInqForecastTunit(int taxisID);

void    taxisDefForecastPeriod(int taxisID, double fc_period);
double  taxisInqForecastPeriod(int taxisID);

void    taxisDefNumavg(int taxisID, int numavg);
int     taxisInqNumavg(int taxisID);

const char *tunitNamePtr(int tunitID);




int     institutDef(int center, int subcenter, const char *name, const char *longname);
int     institutInq(int center, int subcenter, const char *name, const char *longname);
int     institutInqNumber(void);
int     institutInqCenter(int instID);
int     institutInqSubcenter(int instID);
const char *institutInqNamePtr(int instID);
const char *institutInqLongnamePtr(int instID);



int     modelDef(int instID, int modelgribID, const char *name);
int     modelInq(int instID, int modelgribID, const char *name);
int     modelInqInstitut(int modelID) ;
int     modelInqGribID(int modelID);
const char *modelInqNamePtr(int modelID);




void    tableFWriteC(FILE *ptfp, int tableID);

void    tableWrite(const char *filename, int tableID);

int     tableRead(const char *tablefile);
int     tableDef(int modelID, int tablenum, const char *tablename);

const char *tableInqNamePtr(int tableID);

int     tableInq(int modelID, int tablenum, const char *tablename);
int     tableInqNumber(void);

int     tableInqNum(int tableID);
int     tableInqModel(int tableID);

void    tableInqEntry(int tableID, int id, int ltype, char *name, char *longname, char *units);



void    streamDefHistory(int streamID, int size, const char *history);
int     streamInqHistorySize(int streamID);
void    streamInqHistoryString(int streamID, char *history);




int     subtypeCreate(int subtype);


void    subtypePrint(int subtypeID);


int     subtypeCompare(int subtypeID1, int subtypeID2);


int     subtypeInqSize(int subtypeID);


int     subtypeInqActiveIndex(int subtypeID);


void    subtypeDefActiveIndex(int subtypeID, int index);


subtype_query_t keyValuePair(const char *key, int value);


subtype_query_t matchAND(subtype_query_t q1, subtype_query_t q2);


int     subtypeInqSubEntry(int subtypeID, subtype_query_t criterion);


int     subtypeInqTile(int subtypeID, int tileindex, int attribute);


int     subtypeInqAttribute(int subtypeID, int index, const char *key, int *outValue);


int     vlistInqVarSubtype(int vlistID, int varID);

void gribapiLibraryVersion(int *major_version, int *minor_version, int *revision_version);


#if defined (__cplusplus)
}
#endif

#endif

#ifndef _BASETIME_H
#define _BASETIME_H

#include <stdbool.h>


#define MAX_TIMECACHE_SIZE 1024

typedef struct {
int size;
int startid;
int maxvals;
double cache[MAX_TIMECACHE_SIZE];
}
timecache_t;

typedef struct {
int   ncvarid;
int   ncdimid;
int   ncvarboundsid;
int   leadtimeid;
bool  lwrf;
timecache_t *timevar_cache;
}
basetime_t;

void basetimeInit(basetime_t *basetime);

#endif

#ifndef ERROR_H
#define ERROR_H

#include <stdarg.h>
#include <stdlib.h>

#ifndef  WITH_CALLER_NAME
#define  WITH_CALLER_NAME
#endif

#ifdef __cplusplus
extern "C" {
#endif

extern int _ExitOnError;
extern int _Verbose;
extern int _Debug;

void SysError_(const char *caller, const char *fmt, ...);
void    Error_(const char *caller, const char *fmt, ...);
void  Warning_(const char *caller, const char *fmt, ...);

void cdiWarning(const char *caller, const char *fmt, va_list ap);
void  Message_(const char *caller, const char *fmt, ...);

#if  defined  WITH_CALLER_NAME
#  define  SysError(...)  SysError_(__func__, __VA_ARGS__)
#  define    Errorc(...)     Error_(  caller, __VA_ARGS__)
#  define     Error(...)     Error_(__func__, __VA_ARGS__)
#  define   Warning(...)   Warning_(__func__, __VA_ARGS__)
#  define  Messagec(...)   Message_(  caller, __VA_ARGS__)
#  define   Message(...)   Message_(__func__, __VA_ARGS__)
#else
#  define  SysError(...)  SysError_((void *), __VA_ARGS__)
#  define    Errorc(...)     Error_((void *), __VA_ARGS__)
#  define     Error(...)     Error_((void *), __VA_ARGS__)
#  define   Warning(...)   Warning_((void *), __VA_ARGS__)
#  define  Messagec(...)   Message_((void *), __VA_ARGS__)
#  define   Message(...)   Message_((void *), __VA_ARGS__)
#endif


#ifndef __GNUC__
#  define  __attribute__(x)
#endif

void cdiAbortC(const char *caller, const char *filename,
            const char *functionname, int line,
            const char *errorString, ... )
__attribute__((noreturn));
#define xabortC(caller, ...)                                    \
cdiAbortC(caller, __FILE__, __func__, __LINE__, __VA_ARGS__ )
#define xabort(...)                                             \
cdiAbortC(NULL, __FILE__, __func__, __LINE__, __VA_ARGS__ )
#define cdiAbort(file, func, line, ...)                 \
cdiAbortC(NULL, (file), (func), (line), __VA_ARGS__)

#define xassert(arg) do {                       \
    if ((arg)) { } else {                       \
    xabort("assertion `" #arg "` failed");}   \
} while(0)

void
cdiAbortC_serial(const char *caller, const char *filename,
                const char *functionname, int line,
                const char *errorString, va_list ap)
__attribute__((noreturn));

#if defined (__cplusplus)
}
#endif

#endif

#include <stddef.h>


void basetimeInit(basetime_t *basetime)
{
if ( basetime == NULL )
    Error("Internal problem! Basetime not allocated.");

basetime->ncvarid       = CDI_UNDEFID;
basetime->ncdimid       = CDI_UNDEFID;
basetime->ncvarboundsid = CDI_UNDEFID;
basetime->leadtimeid    = CDI_UNDEFID;
basetime->lwrf          = false;
basetime->timevar_cache = NULL;
}


#ifndef CALENDAR_H
#define CALENDAR_H

#include <inttypes.h>

#ifdef __cplusplus
extern "C" {
#endif

void encode_caldaysec(int calendar, int year, int month, int day, int hour, int minute, int second,
                    int64_t *julday, int *secofday);
void decode_caldaysec(int calendar, int64_t julday, int secofday,
                    int *year, int *month, int *day, int *hour, int *minute, int *second);

int calendar_dpy(int calendar);
int days_per_year(int calendar, int year);
int days_per_month(int calendar, int year, int month);

#ifdef __cplusplus
}
#endif

#endif

#ifndef TIMEBASE_H
#define TIMEBASE_H

#include <inttypes.h>

#ifdef __cplusplus
extern "C" {
#endif




void decode_julday(int calendar, int64_t julday, int *year, int *mon, int *day);
int64_t encode_julday(int calendar, int year, int month, int day);

int64_t date_to_julday(int calendar, int64_t date);
int64_t julday_to_date(int calendar, int64_t julday);

int time_to_sec(int time);
int sec_to_time(int secofday);

void   julday_add_seconds(int64_t seconds, int64_t *julday, int *secofday);
void   julday_add(int days, int secs, int64_t *julday, int *secofday);
double julday_sub(int64_t julday1, int secofday1, int64_t julday2, int secofday2, int64_t *days, int *secs);

void encode_juldaysec(int calendar, int year, int month, int day, int hour, int minute, int second, int64_t *julday, int *secofday);
void decode_juldaysec(int calendar, int64_t julday, int secofday, int *year, int *month, int *day, int *hour, int *minute, int *second);

#ifdef __cplusplus
}
#endif

#endif


#include <limits.h>



static const int month_360[12] = {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30};
static const int month_365[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static const int month_366[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};


int calendar_dpy(int calendar)
{
int daysperyear = 0;

if      ( calendar == CALENDAR_360DAYS ) daysperyear = 360;
else if ( calendar == CALENDAR_365DAYS ) daysperyear = 365;
else if ( calendar == CALENDAR_366DAYS ) daysperyear = 366;

return daysperyear;
}


int days_per_month(int calendar, int year, int month)
{
int daysperyear = calendar_dpy(calendar);

const int *dpm;
if      ( daysperyear == 360 ) dpm = month_360;
else if ( daysperyear == 365 ) dpm = month_365;
else                           dpm = month_366;

int dayspermonth = 0;
if ( month >= 1 && month <= 12 )
    dayspermonth = dpm[month-1];

if ( daysperyear == 0 && month == 2 )
    {
    if ( (year%4 == 0 && year%100 != 0) || year%400 == 0 )
        dayspermonth = 29;
    else
        dayspermonth = 28;
    }

return dayspermonth;
}


int days_per_year(int calendar, int year)
{
int daysperyear = calendar_dpy(calendar);

if ( daysperyear == 0 )
    {
    if ( year == 1582 && (calendar == CALENDAR_STANDARD || calendar == CALENDAR_GREGORIAN) )
        daysperyear = 355;
    else if ( (year%4 == 0 && year%100 != 0) || year%400 == 0 )
        daysperyear = 366;
    else
        daysperyear = 365;
    }

return daysperyear;
}


static void decode_day(int dpy, int days, int *year, int *month, int *day)
{
int i = 0;

*year = (days-1) / dpy;
days -= (*year*dpy);

const int *dpm = NULL;
if      ( dpy == 360 ) dpm = month_360;
else if ( dpy == 365 ) dpm = month_365;
else if ( dpy == 366 ) dpm = month_366;

if ( dpm )
    for ( i = 0; i < 12; i++ )
    {
        if ( days > dpm[i] ) days -= dpm[i];
        else break;
    }

*month = i + 1;
*day   = days;
}


static int64_t encode_day(int dpy, int year, int month, int day)
{
int64_t rval = (int64_t)dpy * year + day;

const int *dpm = NULL;
if      ( dpy == 360 ) dpm = month_360;
else if ( dpy == 365 ) dpm = month_365;
else if ( dpy == 366 ) dpm = month_366;

if ( dpm ) for ( int i = 0; i < month-1; i++ ) rval += dpm[i];
if ( rval > LONG_MAX || rval < LONG_MIN ) Error("Unhandled date: %lld", rval);

return rval;
}


void encode_caldaysec(int calendar, int year, int month, int day, int hour, int minute, int second,
                    int64_t *julday, int *secofday)
{
int dpy = calendar_dpy(calendar);

if ( dpy == 360 || dpy == 365 || dpy == 366 )
    *julday = encode_day(dpy, year, month, day);
else
    *julday = encode_julday(calendar, year, month, day);

*secofday = hour*3600 + minute*60 + second;
}


void decode_caldaysec(int calendar, int64_t julday, int secofday,
                    int *year, int *month, int *day, int *hour, int *minute, int *second)
{
int dpy = calendar_dpy(calendar);

if ( dpy == 360 || dpy == 365 || dpy == 366 )
    decode_day(dpy, julday, year, month, day);
else
    decode_julday(calendar, julday, year, month, day);

*hour   = secofday/3600;
*minute = secofday/60 - *hour*60;
*second = secofday - *hour*3600 - *minute*60;
}


#ifdef TEST
static int date_to_calday(int calendar, int date)
{
int dpy = calendar_dpy(calendar);

int year, month, day;
cdiDecodeDate(date, &year, &month, &day);

int calday;
if ( dpy == 360 || dpy == 365 || dpy == 366 )
    calday = encode_day(dpy, year, month, day);
else
    calday = encode_julday(calendar, year, month, day);

return calday;
}


static int calday_to_date(int calendar, int calday)
{
int year, month, day;
int dpy = calendar_dpy(calendar);

if ( dpy == 360 || dpy == 365 || dpy == 366 )
    decode_day(dpy, calday, &year, &month, &day);
else
    decode_julday(calendar, calday, &year, &month, &day);

int date = cdiEncodeDate(year, month, day);

return date;
}


int main(void)
{
int calendar = CALENDAR_STANDARD;
int nmin;
int64_t vdate0, vdate;
int vtime0, vtime;
int ijulinc;
int i, j = 0;
int year, mon, day, hour, minute, second;
int calday, secofday;



nmin = 11000;
vdate0 = -80001201;
vtime0 = 120500;

printf("start time: %8d %4d\n", vdate0, vtime0);

for ( i = 0; i < nmin; i++ )
    {
    cdiDecodeDate(vdate0, &year, &mon, &day);
    cdiDecodeTime(vtime0, &hour, &minute, &second);

    calday  = date_to_calday(calendar, vdate0);
    secofday = time_to_sec(vtime0);

    vdate = calday_to_date(calendar, calday);
    vtime = sec_to_time(secofday);

    if ( vdate0 != vdate || vtime0 != vtime )
        printf("%4d %8d %4d %8d %4d %9d %9d\n",
            ++j, vdate0, vtime0, vdate, vtime, calday, secofday);

    year++;
    vdate0 = cdiEncodeDate(year, mon, day);
    vtime0 = cdiEncodeTime(hour, minute, second);
    }

printf("stop time: %8d %4d\n", vdate0, vtime0);



nmin = 120000;
ijulinc = 60;
vdate0 = 20001201;
vtime0 = 0;

printf("start time: %8d %4d\n", vdate0, vtime0);

calday = date_to_calday(calendar, vdate0);
secofday = time_to_sec(vtime0);
for ( i = 0; i < nmin; i++ )
    {
    cdiDecodeDate(vdate0, &year, &mon, &day);
    cdiDecodeTime(vtime0, &hour, &minute, &second);

    if ( ++minute >= 60 )
        {
        minute = 0;
        if ( ++hour >= 24 )
            {
            hour = 0;
            if ( ++day >= 32 )
                {
                day = 1;
                if ( ++mon >= 13 )
                    {
                    mon = 1;
                    year++;
                    }
                }
            }
        }

    vdate0 = cdiEncodeDate(year, mon, day);
    vtime0 = cdiEncodeTime(hour, minute, second);

    julday_add_seconds(ijulinc, &calday, &secofday);

    vdate = calday_to_date(calendar, calday);
    vtime = sec_to_time(secofday);
    if ( vdate0 != vdate || vtime0 != vtime )
        printf("%4d %8d %4d %8d %4d %9d %9d\n",
            ++j, vdate0, vtime0, vdate, vtime, calday, secofday);
    }

printf("stop time: %8d %4d\n", vdate0, vtime0);

return (0);
}
#endif


#ifdef TEST2
int main(void)
{
int calendar = CALENDAR_STANDARD;
int i;
int calday, secofday;
int year, month, day, hour, minute, second;
int value = 30;
int factor = 86400;

calendar = CALENDAR_360DAYS;

year=1979; month=1; day=15; hour=12; minute=30; second = 0;

printf("calendar = %d\n", calendar);
printf("%d/%02d/%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second);

encode_caldaysec(calendar, year, month, day, hour, minute, second, &calday, &secofday);

decode_caldaysec(calendar, calday, secofday, &year, &month, &day, &hour, &minute, &second);
printf("%d/%02d/%02d %02d:%02d:%02d   %d %d\n", year, month, day, hour, minute, second, calday, secofday);

for ( i = 0; i < 420; i++ )
    {

    decode_caldaysec(calendar, calday, secofday, &year, &month, &day, &hour, &minute, &second);
    printf("%2d %d/%02d/%02d %02d:%02d:%02d\n", i, year, month, day, hour, minute, second);
    julday_add_seconds(value*factor, &calday, &secofday);
    }

return (0);
}
#endif

#ifndef  CDF_H
#define  CDF_H

void cdfDebug(int debug);

extern int CDF_Debug;

const char *cdfLibraryVersion(void);
const char *hdfLibraryVersion(void);

int  cdfOpen(const char *filename, const char *mode, int filetype);
int  cdf4Open(const char *filename, const char *mode, int *filetype);
void cdfClose(int fileID);

#endif

#ifndef  CDF_CONFIG_H_
#define  CDF_CONFIG_H_

#ifdef  HAVE_CONFIG_H
#endif

#ifdef  HAVE_LIBNETCDF

#include  <vtk_netcdf.h>

#ifdef  NC_FORMAT_64BIT_DATA
#define  HAVE_NETCDF5  1
#endif

#endif

#endif
#ifndef RESOURCE_HANDLE_H
#define RESOURCE_HANDLE_H

#ifdef HAVE_CONFIG_H
#endif

#include <stdio.h>




typedef int cdiResH;


typedef int    ( * valCompareFunc     )( void *, void * );
typedef void   ( * valDestroyFunc     )( void * );
typedef void   ( * valPrintFunc       )( void *, FILE * );
typedef int    ( * valGetPackSizeFunc )( void *, void *context );
typedef void   ( * valPackFunc        )( void *, void *buf, int size, int *pos, void *context );
typedef int    ( * valTxCodeFunc      )( void );

typedef struct {
valCompareFunc     valCompare;
valDestroyFunc     valDestroy;
valPrintFunc       valPrint;
valGetPackSizeFunc valGetPackSize;
valPackFunc        valPack;
valTxCodeFunc      valTxCode;
}resOps;

enum {
RESH_IN_USE_BIT = 1 << 0,
RESH_SYNC_BIT = 1 << 1,

RESH_UNUSED = 0,

RESH_DESYNC_DELETED
    = RESH_SYNC_BIT,

RESH_IN_USE
    = RESH_IN_USE_BIT,

RESH_DESYNC_IN_USE
    = RESH_IN_USE_BIT | RESH_SYNC_BIT,
};

void   reshListCreate(int namespaceID);
void   reshListDestruct(int namespaceID);
int    reshPut ( void *, const resOps * );
void reshReplace(cdiResH resH, void *p, const resOps *ops);
void   reshRemove ( cdiResH, const resOps * );

void reshDestroy(cdiResH);

unsigned reshCountType(const resOps *resTypeOps);

void * reshGetValue(const char* caller, const char* expressionString, cdiResH id, const resOps* ops);
#define reshGetVal(resH, ops)  reshGetValue(__func__, #resH, resH, ops)

void reshGetResHListOfType(unsigned numIDs, int IDs[], const resOps *ops);

enum cdiApplyRet {
CDI_APPLY_ERROR = -1,
CDI_APPLY_STOP,
CDI_APPLY_GO_ON,
};
enum cdiApplyRet
cdiResHApply(enum cdiApplyRet (*func)(int id, void *res, const resOps *p,
                                    void *data), void *data);
enum cdiApplyRet
cdiResHFilterApply(const resOps *p,
                enum cdiApplyRet (*func)(int id, void *res,
                                            void *data),
                void *data);

int reshPackBufferCreate(char **packBuf, int *packBufSize, void *context);
void   reshPackBufferDestroy ( char ** );
int    reshResourceGetPackSize_intern(int resh, const resOps *ops, void *context, const char* caller, const char* expressionString);
#define reshResourceGetPackSize(resh, ops, context) reshResourceGetPackSize_intern(resh, ops, context, __func__, #resh)
void   reshPackResource_intern(int resh, const resOps *ops, void *buf, int buf_size, int *position, void *context, const char* caller, const char* expressionString);
#define reshPackResource(resh, ops, buf, buf_size, position, context) reshPackResource_intern(resh, ops, buf, buf_size, position, context, __func__, #resh)

void   reshSetStatus ( cdiResH, const resOps *, int );
int    reshGetStatus ( cdiResH, const resOps * );

void   reshLock   ( void );
void   reshUnlock ( void );

enum reshListMismatch {
cdiResHListOccupationMismatch,
cdiResHListResourceTypeMismatch,
cdiResHListResourceContentMismatch,
};

int reshListCompare(int nsp0, int nsp1);
void reshListPrint(FILE *fp);
int reshGetTxCode(cdiResH resH);

#endif

#ifndef TAXIS_H
#define TAXIS_H

#include <stdbool.h>

#ifndef RESOURCE_HANDLE_H
#endif

typedef struct {


int     self;
bool    used;
short   has_bounds;
int     datatype;
int     type;
int64_t vdate;
int     vtime;
int64_t rdate;
int     rtime;
int64_t fdate;
int     ftime;
int     calendar;
int     unit;
int     numavg;
bool    climatology;
int64_t vdate_lb;
int     vtime_lb;
int64_t vdate_ub;
int     vtime_ub;
int     fc_unit;
double  fc_period;
char   *name;
char   *longname;
char   *units;
}
taxis_t;

void     ptaxisInit(taxis_t* taxis);
void     ptaxisCopy(taxis_t* dest, taxis_t* source);
taxis_t* taxisPtr(int taxisID);
void     cdiSetForecastPeriod(double timevalue, taxis_t *taxis);
void     cdiDecodeTimeval(double timevalue, taxis_t* taxis, int64_t* date, int* time);
double   cdiEncodeTimeval(int64_t date, int time, taxis_t* taxis);
void     timeval2vtime(double timevalue, taxis_t* taxis, int64_t* vdate, int* vtime);
double   vtime2timeval(int64_t vdate, int vtime, taxis_t *taxis);

void    ptaxisDefDatatype(taxis_t *taxisptr, int datatype);
void    ptaxisDefName(taxis_t *taxisptr, const char *name);
void    ptaxisDefLongname(taxis_t *taxisptr, const char *longname);
void    ptaxisDefUnits(taxis_t *taxisptr, const char *units);
void    taxisDestroyKernel(taxis_t *taxisptr);
#if !defined (SX)
extern const resOps taxisOps;
#endif

int
taxisUnpack(char *unpackBuffer, int unpackBufferSize, int *unpackBufferPos,
            int originNamespace, void *context, int checkForSameID);

#endif

#ifndef  CDI_LIMITS_H
#define  CDI_LIMITS_H

#define  MAX_GRIDS_PS    128
#define  MAX_ZAXES_PS    128
#define  MAX_ATTRIBUTES  256
#define  MAX_KEYS         64
#define  MAX_SUBTYPES_PS 128

#endif

#ifndef  CDI_INT_H
#define  CDI_INT_H

#ifdef HAVE_CONFIG_H
#endif

#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <sys/types.h>



#ifndef  UNUSED
#define  UNUSED(x) (void)x
#endif

#ifndef strdupx
#ifndef strdup
char *strdup(const char *s);
#endif
#define strdupx  strdup

#endif

void str_tolower(char *str);
bool str_is_equal(const char *vstr, const char *cstr);

#ifndef  M_PI
#define  M_PI        3.14159265358979323846
#endif


#ifndef  ERROR_H
#endif
#ifndef _BASETIME_H
#endif
#ifndef TIMEBASE_H
#endif
#ifndef TAXIS_H
#endif
#ifndef CDI_LIMITS_H
#endif
#ifndef  _SERVICE_H
#endif
#ifndef  _EXTRA_H
#endif
#ifndef  _IEG_H
#endif
#ifndef RESOURCE_HANDLE_H
#endif


#define check_parg(arg)  if ( arg == 0 ) Warning("Argument '" #arg "' not allocated!")

#if defined (__xlC__)
#ifndef DBL_IS_NAN
#  define DBL_IS_NAN(x)     ((x) != (x))
#endif
#else
#ifndef DBL_IS_NAN
#if  defined  (HAVE_DECL_ISNAN)
#  define DBL_IS_NAN(x)     (isnan(x))
#elif  defined  (FP_NAN)
#  define DBL_IS_NAN(x)     (fpclassify(x) == FP_NAN)
#else
#  define DBL_IS_NAN(x)     ((x) != (x))
#endif
#endif
#endif

#ifndef DBL_IS_EQUAL

#  define DBL_IS_EQUAL(x,y) (DBL_IS_NAN(x)||DBL_IS_NAN(y)?(DBL_IS_NAN(x)&&DBL_IS_NAN(y)):!(x < y || y < x))
#endif

#ifndef IS_EQUAL
#  define IS_NOT_EQUAL(x,y) (x < y || y < x)
#  define IS_EQUAL(x,y)     (!IS_NOT_EQUAL(x,y))
#endif

#define  FALSE  0
#define  TRUE   1

#define  TYPE_REC  0
#define  TYPE_VAR  1

#define  MEMTYPE_DOUBLE  1
#define  MEMTYPE_FLOAT   2

typedef struct
{
void     *buffer;
size_t    buffersize;
off_t     position;
int       param;
int       level;
int       date;
int       time;
int       gridID;
int       varID;
int       levelID;
int       prec;
int       sec0[2];
int       sec1[1024];
int       sec2[4096];
int       sec3[2];
int       sec4[512];
void     *exsep;
}
Record;


typedef struct {
int
    tileindex,
    totalno_of_tileattr_pairs,
    tileClassification,
    numberOfTiles,
    numberOfAttributes,
    attribute;
} var_tile_t;


typedef struct
{
off_t     position;
size_t    size;
size_t    gridsize;
int       zip;
int       param;
int       ilevel;
int       ilevel2;
int       ltype;
short     tsteptype;
#ifdef HIRLAM_EXTENSIONS






#endif
short     varID;
short     levelID;
short     used;
char      varname[32];
var_tile_t tiles;
}
record_t;


typedef struct {
record_t *records;
int      *recIDs;
int       recordSize;
int       nrecs;


int       nallrecs;
int       curRecID;
bool      next;
off_t     position;
taxis_t   taxis;
}
tsteps_t;


typedef struct {
int       nlevs;
int       subtypeIndex;
int      *recordID;
int      *lindex;
} sleveltable_t;


typedef struct {
int            ncvarid;
int            subtypeSize;
sleveltable_t *recordTable;
bool           defmiss;
bool           isUsed;

int            gridID;
int            zaxisID;
int            tsteptype;
int            subtypeID;
}
svarinfo_t;


typedef struct {
int       ilev;
int       mlev;
int       ilevID;
int       mlevID;
}
VCT;

#ifdef HAVE_LIBNETCDF
enum {
CDF_DIMID_X,
CDF_DIMID_Y,
CDF_VARID_X,
CDF_VARID_Y,
CDF_VARID_A,
CDF_SIZE_ncIDs,
};
typedef struct {
int gridID;
int ncIDs[CDF_SIZE_ncIDs];
}
ncgrid_t;
#endif

typedef struct {
int         self;
int         accesstype;
int         accessmode;
int         filetype;
int         byteorder;
int         fileID;
int         filemode;
int         nrecs;
size_t      numvals;
char       *filename;
Record     *record;
svarinfo_t *vars;
int         nvars;
int         varsAllocated;
int         curTsID;
int         rtsteps;
long        ntsteps;
tsteps_t   *tsteps;
int         tstepsTableSize;
int         tstepsNextID;
basetime_t  basetime;
int         ncmode;
int         vlistID;
#ifdef HAVE_LIBNETCDF
ncgrid_t    ncgrid[MAX_GRIDS_PS];
int         zaxisID[MAX_ZAXES_PS];
int         nczvarID[MAX_ZAXES_PS];
VCT         vct;
#endif
int         historyID;
int         globalatts;
int         localatts;
int         unreduced;
int         have_missval;
int         comptype;
int         complevel;
bool        sortname;
bool        sortparam;
#if defined (GRIBCONTAINER2D)
void      **gribContainers;
#else
void       *gribContainers;
#endif

void *gh;
}
stream_t;



#define MAX_OPT_GRIB_ENTRIES 500

enum cdi_convention {CDI_CONVENTION_ECHAM, CDI_CONVENTION_CF};


typedef enum {
t_double = 0,
t_int    = 1
} key_val_pair_datatype;


typedef struct
{
char*                  keyword;
bool                   update;
key_val_pair_datatype  data_type;
double                 dbl_val;
int                    int_val;
int                    subtype_index;
} opt_key_val_pair_t;


typedef enum {
kCdiTimeType_referenceTime,
kCdiTimeType_startTime,
kCdiTimeType_endTime
} CdiTimeType;


#define  CDI_FILETYPE_UNDEF          -1


extern int cdiDebugExt;
extern int CDI_Debug;
extern int CDI_Recopt;
extern int cdiGribApiDebug;
extern double CDI_default_missval;
extern double CDI_grid_missval;
extern int cdiDefaultInstID;
extern int cdiDefaultModelID;
extern int cdiDefaultTableID;
extern int cdiDefaultLeveltype;
extern int cdiDefaultCalendar;

extern int cdiNcChunksizehint;
extern int cdiChunkType;
extern int cdiSplitLtype105;
extern int cdiDataUnreduced;
extern int cdiSortName;
extern int cdiSortParam;
extern int cdiHaveMissval;
extern bool cdiIgnoreAttCoordinates;
extern bool cdiCoordinatesLonLat;
extern bool cdiIgnoreValidRange;
extern int cdiSkipRecords;
extern int CDI_convention;
extern int CDI_inventory_mode;
extern int CDO_version_info;
extern int CDI_read_cell_corners;
extern int CDI_cmor_mode;
extern int CDI_reduce_dim;
extern size_t CDI_netcdf_hdr_pad;
extern bool CDI_netcdf_lazy_grid_load;
extern int STREAM_Debug;


extern char *cdiPartabPath;
extern int   cdiPartabIntern;
extern const resOps streamOps;

static inline stream_t *
stream_to_pointer(int idx)
{
return (stream_t *)reshGetVal(idx, &streamOps);
}

static inline void
stream_check_ptr(const char *caller, stream_t *streamptr)
{
if ( streamptr == NULL )
    Errorc("stream undefined!");
}

int     streamInqFileID(int streamID);

void    gridDefHasDims(int gridID, int hasdims);
int     gridInqHasDims(int gridID);
int     zaxisInqLevelID(int zaxisID, double level);

void    streamCheckID(const char *caller, int streamID);

void    streamDefineTaxis(int streamID);

int     streamsNewEntry(int filetype);
void    streamsInitEntry(int streamID);
void    cdiStreamSetupVlist(stream_t *streamptr, int vlistID);

void    cdiStreamSetupVlist_(stream_t *streamptr, int vlistID);
int     stream_new_var(stream_t *streamptr, int gridID, int zaxisID, int tilesetID);

int     tstepsNewEntry(stream_t *streamptr);

const char *strfiletype(int filetype);

void    cdi_generate_vars(stream_t *streamptr);

void    vlist_check_contents(int vlistID);

void    cdi_create_records(stream_t *streamptr, int tsID);

void streamFCopyRecord(stream_t *streamptr2, stream_t *streamptr1, const char *container_name);

int     recordNewEntry(stream_t *streamptr, int tsID);

void    cdiCreateTimesteps(stream_t *streamptr);

void    recordInitEntry(record_t *record);

void    cdiCheckZaxis(int zaxisID);

void    cdiDefAccesstype(int streamID, int type);
int     cdiInqAccesstype(int streamID);

int     getByteswap(int byteorder);

void cdiStreamGetIndexList(unsigned numIDs, int IDs[]);

void  cdiInitialize(void);

char *cdiEscapeSpaces(const char *string);
char *cdiUnescapeSpaces(const char *string, const char **outStringEnd);

#define CDI_UNIT_PA   1
#define CDI_UNIT_HPA  2
#define CDI_UNIT_MM   3
#define CDI_UNIT_CM   4
#define CDI_UNIT_DM   5
#define CDI_UNIT_M    6

struct streamAssoc
{
int streamID, vlistID;
};

struct streamAssoc
streamUnpack(char *unpackBuffer, int unpackBufferSize,
            int *unpackBufferPos, int originNamespace, void *context);

int
cdiStreamOpenDefaultDelegate(const char *filename, char filemode,
                            int filetype, stream_t *streamptr,
                            int recordBufIsToBeCreated);

int
streamOpenID(const char *filename, char filemode, int filetype,
            int resH);

void
cdiStreamDefVlist_(int streamID, int vlistID);

int
cdiStreamWriteVar_(int streamID, int varID, int memtype, const void *data, size_t nmiss);

void
cdiStreamWriteVarChunk_(int streamID, int varID, int memtype,
                        const int rect[][2], const void *data, size_t nmiss);
void
cdiStreamCloseDefaultDelegate(stream_t *streamptr,
                            int recordBufIsToBeDeleted);

int cdiStreamDefTimestep_(stream_t *streamptr, int tsID);

void cdiStreamSync_(stream_t *streamptr);

const char *cdiUnitNamePtr(int cdi_unit);

enum {

commonPageSize = 8192,
};

size_t cdiGetPageSize(bool largePageAlign);

void zaxisGetIndexList(int nzaxis, int *zaxisIndexList);

void zaxisDefLtype2(int zaxisID, int ltype2);
int  zaxisInqLtype2(int zaxisID);

#ifdef __cplusplus
extern "C" {
#endif



void cdiDefTableID(int tableID);

void gridGenXvals(int xsize, double xfirst, double xlast, double xinc, double *xvals);
void gridGenYvals(int gridtype, int ysize, double yfirst, double ylast, double yinc, double *yvals);

static inline
void cdi_check_gridsize_int_limit(const char *format, size_t gridsize)
{
if ( gridsize > INT_MAX ) Error("%s format grid size (%zu) limit exceeded (%zu)!", format, gridsize, INT_MAX);
}


#if defined (__cplusplus)
}
#endif

#endif

#ifndef  CDF_INT_H
#define  CDF_INT_H

#ifdef HAVE_LIBNETCDF

#include <vtk_netcdf.h>

void cdf_create (const char *path, int cmode, int *idp);
int  cdf_open   (const char *path, int omode, int *idp);
void cdf_close  (int ncid);

void cdf_redef(int ncid);
void cdf_enddef(int ncid);
void cdf__enddef(const int ncid, const size_t hdr_pad);
void cdf_sync(int ncid);

void cdf_inq (int ncid, int *ndimsp, int *nvarsp, int *ngattsp, int *unlimdimidp);

void cdf_def_dim (int ncid, const char *name, size_t len, int *idp);
void cdf_inq_dimid (int ncid, const char *name, int *dimidp);
void cdf_inq_dim (int ncid, int dimid, char *name, size_t * lengthp);
void cdf_inq_dimname (int ncid, int dimid, char *name);
void cdf_inq_dimlen (int ncid, int dimid, size_t * lengthp);
void cdf_def_var (int ncid, const char *name, nc_type xtype, int ndims, const int dimids[], int *varidp);
void cdf_def_var_serial(int ncid, const char *name, nc_type xtype, int ndims, const int dimids[], int *varidp);
void cdf_inq_varid(int ncid, const char *name, int *varidp);
void cdf_inq_nvars(int ncid, int *nvarsp);
void cdf_inq_var(int ncid, int varid, char *name, nc_type *xtypep, int *ndimsp, int dimids[], int *nattsp);
void cdf_inq_varname (int ncid, int varid, char *name);
void cdf_inq_vartype (int ncid, int varid, nc_type *xtypep);
void cdf_inq_varndims (int ncid, int varid, int *ndimsp);
void cdf_inq_vardimid (int ncid, int varid, int dimids[]);
void cdf_inq_varnatts (int ncid, int varid, int *nattsp);

void cdf_copy_att (int ncid_in, int varid_in, const char *name, int ncid_out, int varid_out);
void cdf_put_var_text   (int ncid, int varid, const char *tp);
void cdf_put_var_uchar  (int ncid, int varid, const unsigned char *up);
void cdf_put_var_schar  (int ncid, int varid, const signed char *cp);
void cdf_put_var_short  (int ncid, int varid, const short *sp);
void cdf_put_var_int    (int ncid, int varid, const int *ip);
void cdf_put_var_long   (int ncid, int varid, const long *lp);
void cdf_put_var_float  (int ncid, int varid, const float *fp);
void cdf_put_var_double (int ncid, int varid, const double *dp);

void cdf_get_var_text   (int ncid, int varid, char *tp);
void cdf_get_var_uchar  (int ncid, int varid, unsigned char *up);
void cdf_get_var_schar  (int ncid, int varid, signed char *cp);
void cdf_get_var_short  (int ncid, int varid, short *sp);
void cdf_get_var_int    (int ncid, int varid, int *ip);
void cdf_get_var_long   (int ncid, int varid, long *lp);
void cdf_get_var_float  (int ncid, int varid, float *fp);
void cdf_get_var_double (int ncid, int varid, double *dp);

void cdf_get_var1_text(int ncid, int varid, const size_t index[], char *tp);

void cdf_get_var1_double(int ncid, int varid, const size_t index[], double *dp);
void cdf_put_var1_double(int ncid, int varid, const size_t index[], const double *dp);

void cdf_get_vara_uchar(int ncid, int varid, const size_t start[], const size_t count[], unsigned char *tp);
void cdf_get_vara_text(int ncid, int varid, const size_t start[], const size_t count[], char *tp);

void cdf_get_vara_double(int ncid, int varid, const size_t start[], const size_t count[], double *dp);
void cdf_put_vara_double(int ncid, int varid, const size_t start[], const size_t count[], const double *dp);

void cdf_get_vara_float(int ncid, int varid, const size_t start[], const size_t count[], float *fp);
void cdf_put_vara_float(int ncid, int varid, const size_t start[], const size_t count[], const float *fp);
void  cdf_get_vara_int(int ncid, int varid, const size_t start[],
                    const size_t count[], int *dp);

void cdf_put_att_text(int ncid, int varid, const char *name, size_t len, const char *tp);
void cdf_put_att_int(int ncid, int varid, const char *name, nc_type xtype, size_t len, const int *ip);
void cdf_put_att_float(int ncid, int varid, const char *name, nc_type xtype, size_t len, const float *dp);
void cdf_put_att_double(int ncid, int varid, const char *name, nc_type xtype, size_t len, const double *dp);

void cdf_get_att_string(int ncid, int varid, const char *name, char **tp);
void cdf_get_att_text  (int ncid, int varid, const char *name, char *tp);
void cdf_get_att_int   (int ncid, int varid, const char *name, int *ip);
void cdf_get_att_long  (int ncid, int varid, const char *name, long *ip);
void cdf_get_att_double(int ncid, int varid, const char *name, double *dp);

void cdf_inq_att    (int ncid, int varid, const char *name, nc_type * xtypep, size_t * lenp);
void cdf_inq_atttype(int ncid, int varid, const char *name, nc_type *xtypep);
void cdf_inq_attlen (int ncid, int varid, const char *name, size_t *lenp);
void cdf_inq_attname(int ncid, int varid, int attnum, char *name);
void cdf_inq_attid  (int ncid, int varid, const char *name, int *attnump);

void cdf_def_var_chunking(int ncid, int varid, int storage, const size_t *chunksizesp);

typedef int (*cdi_nc__create_funcp)(const char *path, int cmode,
                                    size_t initialsz, size_t *chunksizehintp,
                                    int *ncidp);

typedef void (*cdi_cdf_def_var_funcp)(int ncid, const char *name,
                                    nc_type xtype, int ndims,
                                    const int dimids[], int *varidp);

#endif

#endif

#ifdef  HAVE_CONFIG_H
#endif

#include <ctype.h>



const char *cdfLibraryVersion(void)
{
#ifdef  HAVE_LIBNETCDF
return nc_inq_libvers();
#else
return "library undefined";
#endif
}

#ifdef  HAVE_H5GET_LIBVERSION
#ifdef  __cplusplus
extern "C" {
#endif
int H5get_libversion(unsigned *, unsigned *, unsigned *);
#ifdef  __cplusplus
}
#endif
#endif

const char *hdfLibraryVersion(void)
{
#ifdef  HAVE_H5GET_LIBVERSION
static char hdf_libvers[256];
unsigned majnum, minnum, relnum;

H5get_libversion(&majnum, &minnum, &relnum);

#ifdef  HAVE_NC4HDF5_THREADSAFE
sprintf(hdf_libvers, "%u.%u.%u threadsafe", majnum, minnum, relnum);
#else
sprintf(hdf_libvers, "%u.%u.%u", majnum, minnum, relnum);
#endif
return hdf_libvers;
#else
return "library undefined";
#endif
}


int CDF_Debug   = 0;


void cdfDebug(int debug)
{
CDF_Debug = debug;

if ( CDF_Debug )
    Message("debug level %d", debug);
}

#ifdef  HAVE_LIBNETCDF
static
void cdfComment(int ncid)
{
static char comment[256] = "Climate Data Interface version ";
static bool init = false;

if ( ! init )
    {
    init = true;
    const char *libvers = cdiLibraryVersion();

    if ( ! isdigit((int) *libvers) )
        strcat(comment, "??");
    else
        strcat(comment, libvers);
    strcat(comment, " (http://mpimet.mpg.de/cdi)");
    }

cdf_put_att_text(ncid, NC_GLOBAL, "CDI", strlen(comment), comment);
}
#endif

static int cdfOpenFile(const char *filename, const char *mode, int *filetype)
{
int ncid = -1;
#ifdef  HAVE_LIBNETCDF
int fmode = tolower(*mode);
int writemode = NC_CLOBBER;
int readmode = NC_NOWRITE;

if ( filename == NULL )
    ncid = CDI_EINVAL;
else
    {
    switch (fmode)
        {
        case 'r':
        {
            int status = cdf_open(filename, readmode, &ncid);
            if ( status > 0 && ncid < 0 ) ncid = CDI_ESYSTEM;
#ifdef  HAVE_NETCDF4
            else
            {
                int format;
                (void) nc_inq_format(ncid, &format);
                if ( format == NC_FORMAT_NETCDF4_CLASSIC )
                *filetype = CDI_FILETYPE_NC4C;
            }
#endif
        }
        break;
        case 'w':
#ifdef  NC_64BIT_OFFSET
        if      ( *filetype == CDI_FILETYPE_NC2  ) writemode |= NC_64BIT_OFFSET;
#endif
#ifdef  NC_64BIT_DATA
        if      ( *filetype == CDI_FILETYPE_NC5  ) writemode |= NC_64BIT_DATA;
#endif
#ifdef  HAVE_NETCDF4
        if      ( *filetype == CDI_FILETYPE_NC4  ) writemode |= NC_NETCDF4;
        else if ( *filetype == CDI_FILETYPE_NC4C ) writemode |= NC_NETCDF4 | NC_CLASSIC_MODEL;
#endif
        cdf_create(filename, writemode, &ncid);
        if ( CDO_version_info ) cdfComment(ncid);
        cdf_put_att_text(ncid, NC_GLOBAL, "Conventions", 6, "CF-1.6");
        break;
        case 'a':
        cdf_open(filename, NC_WRITE, &ncid);
        break;
        default:
        ncid = CDI_EINVAL;
        }
    }
#endif

return ncid;
}


int cdfOpen(const char *filename, const char *mode, int filetype)
{
int fileID = -1;
bool open_file = true;

if ( CDF_Debug )
    Message("Open %s with mode %c", filename, *mode);

#ifdef  HAVE_LIBNETCDF
#ifndef  NC_64BIT_OFFSET
if ( filetype == CDI_FILETYPE_NC2 ) open_file = false;
#endif
#ifndef  NC_64BIT_DATA
if ( filetype == CDI_FILETYPE_NC5 ) open_file = false;
#endif
#endif

if ( open_file )
    {
    fileID = cdfOpenFile(filename, mode, &filetype);

    if ( CDF_Debug )
        Message("File %s opened with id %d", filename, fileID);
    }
else
    {
    fileID = CDI_ELIBNAVAIL;
    }

return fileID;
}


int cdf4Open(const char *filename, const char *mode, int *filetype)
{
int fileID = -1;
bool open_file = false;

if ( CDF_Debug )
    Message("Open %s with mode %c", filename, *mode);

#ifdef  HAVE_NETCDF4
open_file = true;
#endif

if ( open_file )
    {
    fileID = cdfOpenFile(filename, mode, filetype);

    if ( CDF_Debug )
        Message("File %s opened with id %d", filename, fileID);
    }
else
    {
    fileID = CDI_ELIBNAVAIL;
    }

return fileID;
}


static void cdfCloseFile(int fileID)
{
#ifdef  HAVE_LIBNETCDF
cdf_close(fileID);
#endif
}

void cdfClose(int fileID)
{
cdfCloseFile(fileID);
}

#ifndef NAMESPACE_H
#define NAMESPACE_H

#ifdef HAVE_CONFIG_H
#endif


typedef struct {
int idx;
int nsp;
} namespaceTuple_t;

enum namespaceSwitch
{
NSSWITCH_NO_SUCH_SWITCH = -1,
NSSWITCH_ABORT,
NSSWITCH_WARNING,
NSSWITCH_SERIALIZE_GET_SIZE,
NSSWITCH_SERIALIZE_PACK,
NSSWITCH_SERIALIZE_UNPACK,
NSSWITCH_FILE_OPEN,
NSSWITCH_FILE_WRITE,
NSSWITCH_FILE_CLOSE,
NSSWITCH_STREAM_OPEN_BACKEND,
NSSWITCH_STREAM_DEF_VLIST_,
NSSWITCH_STREAM_SETUP_VLIST,
NSSWITCH_STREAM_WRITE_VAR_,
NSSWITCH_STREAM_WRITE_VAR_CHUNK_,
NSSWITCH_STREAM_WRITE_VAR_PART_,
NSSWITCH_STREAM_WRITE_SCATTERED_VAR_PART_,
NSSWITCH_STREAM_CLOSE_BACKEND,
NSSWITCH_STREAM_DEF_TIMESTEP_,
NSSWITCH_STREAM_SYNC,
#ifdef HAVE_LIBNETCDF
NSSWITCH_NC__CREATE,
NSSWITCH_CDF_DEF_VAR,
NSSWITCH_CDF_DEF_TIMESTEP,
NSSWITCH_CDF_STREAM_SETUP,
#endif
NUM_NAMESPACE_SWITCH,
};

union namespaceSwitchValue
{
void *data;
void (*func)();
};

#define NSSW_FUNC(p) ((union namespaceSwitchValue){ .func = (void (*)())(p) })
#define NSSW_DATA(p) ((union namespaceSwitchValue){ .data = (void *)(p) })



void             namespaceCleanup      ( void );
int              namespaceGetNumber    ( void );


int              namespaceIdxEncode    ( namespaceTuple_t );
int              namespaceIdxEncode2   ( int, int );
namespaceTuple_t namespaceResHDecode   ( int );
int              namespaceAdaptKey     ( int originResH, int originNamespace);
int              namespaceAdaptKey2    ( int );
void namespaceSwitchSet(enum namespaceSwitch sw,
                        union namespaceSwitchValue value);
union namespaceSwitchValue namespaceSwitchGet(enum namespaceSwitch sw);


#endif

#ifdef HAVE_CONFIG_H
#endif

#include <sys/stat.h>


#ifdef HAVE_LIBNETCDF

void cdf_create(const char *path, int cmode, int *ncidp)
{
int oldfill;
size_t initialsz = 0, chunksizehint = 0;

#if defined(__SX__) || defined(ES)
chunksizehint = 16777216;
#endif

if ( cdiNcChunksizehint != CDI_UNDEFID )
    chunksizehint = (size_t)cdiNcChunksizehint;

cdi_nc__create_funcp my_nc__create =
    (cdi_nc__create_funcp)namespaceSwitchGet(NSSWITCH_NC__CREATE).func;
int status = my_nc__create(path, cmode, initialsz, &chunksizehint, ncidp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d  mode = %d  file = %s", *ncidp, cmode, path);

if ( CDF_Debug || status != NC_NOERR )
    Message("chunksizehint %d", chunksizehint);

if ( status != NC_NOERR ) Error("%s: %s", path, nc_strerror(status));

status = nc_set_fill(*ncidp, NC_NOFILL, &oldfill);

if ( status != NC_NOERR ) Error("%s: %s", path, nc_strerror(status));
}


int cdf_open(const char *path, int omode, int *ncidp)
{
int status = 0;
bool dapfile = false;
struct stat filestat;
size_t chunksizehint = 0;

#ifdef HAVE_LIBNC_DAP
if ( strncmp(path, "http:", 5) == 0 || strncmp(path, "https:", 6) == 0 ) dapfile = true;
#endif

if ( dapfile )
    {
    status = nc_open(path, omode, ncidp);
    }
else
    {
    if ( stat(path, &filestat) != 0 ) SysError(path);

#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
    chunksizehint = (size_t) filestat.st_blksize * 4;
    if ( chunksizehint > (size_t) filestat.st_size ) chunksizehint = (size_t) filestat.st_size;
#endif

    if ( cdiNcChunksizehint != CDI_UNDEFID )
        chunksizehint = (size_t)cdiNcChunksizehint;


    status = nc__open(path, omode, &chunksizehint, ncidp);

    if ( CDF_Debug ) Message("chunksizehint %zu", chunksizehint);
    }

if ( CDF_Debug )
    Message("ncid = %d  mode = %d  file = %s", *ncidp, omode, path);

if ( CDF_Debug && status != NC_NOERR ) Message("%s", nc_strerror(status));

return status;
}


void cdf_close(int ncid)
{
int status = nc_close(ncid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_redef(int ncid)
{
int status = nc_redef(ncid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_enddef(int ncid)
{
int status = nc_enddef(ncid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf__enddef(const int ncid, const size_t hdr_pad)
{
const size_t v_align   = 4UL;
const size_t v_minfree = 0UL;
const size_t r_align   = 4UL;


int status = nc__enddef(ncid, hdr_pad, v_align, v_minfree, r_align);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_sync(int ncid)
{
int status = nc_sync(ncid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq(int ncid, int *ndimsp, int *nvarsp, int *ngattsp, int *unlimdimidp)
{
int status = nc_inq(ncid, ndimsp, nvarsp, ngattsp, unlimdimidp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d ndims = %d nvars = %d ngatts = %d unlimid = %d",
            ncid, *ndimsp, *nvarsp, *ngattsp, *unlimdimidp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_def_dim(int ncid, const char *name, size_t len, int *dimidp)
{
int status = nc_def_dim(ncid, name, len, dimidp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d  name = %s  len = %d", ncid, name, len);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_dimid(int ncid, const char *name, int *dimidp)
{
int status = nc_inq_dimid(ncid, name, dimidp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d  name = %s  dimid= %d", ncid, name, *dimidp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_dim(int ncid, int dimid, char *name, size_t * lengthp)
{
int status = nc_inq_dim(ncid, dimid, name, lengthp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d  dimid = %d  length = %d name = %s", ncid, dimid, *lengthp, name);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_dimname(int ncid, int dimid, char *name)
{
int status = nc_inq_dimname(ncid, dimid, name);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d  dimid = %d  name = %s", ncid, dimid, name);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_dimlen(int ncid, int dimid, size_t * lengthp)
{
int status = nc_inq_dimlen(ncid, dimid, lengthp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d dimid = %d length = %d", ncid, dimid, *lengthp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_def_var(int ncid, const char *name, nc_type xtype, int ndims,
                const int dimids[], int *varidp)
{
cdi_cdf_def_var_funcp my_cdf_def_var
    = (cdi_cdf_def_var_funcp)namespaceSwitchGet(NSSWITCH_CDF_DEF_VAR).func;
my_cdf_def_var(ncid, name, xtype, ndims, dimids, varidp);
}

void
cdf_def_var_serial(int ncid, const char *name, nc_type xtype, int ndims,
                const int dimids[], int *varidp)
{
int status = nc_def_var(ncid, name, xtype, ndims, dimids, varidp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d  name = %s  xtype = %d  ndims = %d  varid = %d",
            ncid, name, xtype, ndims, *varidp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}



void cdf_inq_varid(int ncid, const char *name, int *varidp)
{
int status = nc_inq_varid(ncid, name, varidp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d  name = %s  varid = %d ", ncid, name, *varidp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_nvars(int ncid, int *nvarsp)
{
int status = nc_inq_nvars(ncid, nvarsp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d  nvars = %d", ncid, *nvarsp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_var(int ncid, int varid, char *name, nc_type *xtypep, int *ndimsp,
                int dimids[], int *nattsp)
{
int status = nc_inq_var(ncid, varid, name, xtypep, ndimsp, dimids, nattsp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d ndims = %d xtype = %d natts = %d name = %s",
            ncid, varid, *ndimsp, *xtypep, *nattsp, name);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_varname(int ncid, int varid, char *name)
{
int status = nc_inq_varname(ncid, varid, name);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d name = %s", ncid, varid, name);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_vartype(int ncid, int varid, nc_type *xtypep)
{
int status = nc_inq_vartype(ncid, varid, xtypep);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d xtype = %s", ncid, varid, *xtypep);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_varndims(int ncid, int varid, int *ndimsp)
{
int status = nc_inq_varndims(ncid, varid, ndimsp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_vardimid(int ncid, int varid, int dimids[])
{
int status = nc_inq_vardimid(ncid, varid, dimids);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_varnatts(int ncid, int varid, int *nattsp)
{
int status = nc_inq_varnatts(ncid, varid, nattsp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d nattsp = %d", ncid, varid, *nattsp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_var_text(int ncid, int varid, const char *tp)
{
int status = nc_put_var_text(ncid, varid, tp);

if ( CDF_Debug || status != NC_NOERR )
    Message("%d %d %s", ncid, varid, tp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_var_short(int ncid, int varid, const short *sp)
{
int status = nc_put_var_short(ncid, varid, sp);

if ( CDF_Debug || status != NC_NOERR )
    Message("%d %d %hd", ncid, varid, *sp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_var_int(int ncid, int varid, const int *ip)
{
int status = nc_put_var_int(ncid, varid, ip);

if ( CDF_Debug || status != NC_NOERR )
    Message("%d %d %d", ncid, varid, *ip);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_var_long(int ncid, int varid, const long *lp)
{
int status = nc_put_var_long(ncid, varid, lp);

if ( CDF_Debug || status != NC_NOERR )
    Message("%d %d %ld", ncid, varid, *lp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_var_float(int ncid, int varid, const float *fp)
{
int status = nc_put_var_float(ncid, varid, fp);

if ( CDF_Debug || status != NC_NOERR )
    Message("%d %d %f", ncid, varid, *fp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}

static
const char *cdf_var_type(nc_type xtype)
{
const char *ctype = "unknown";

if      ( xtype == NC_BYTE   )  ctype = "NC_BYTE";
else if ( xtype == NC_CHAR   )  ctype = "NC_CHAR";
else if ( xtype == NC_SHORT  )  ctype = "NC_SHORT";
else if ( xtype == NC_INT    )  ctype = "NC_INT";
else if ( xtype == NC_FLOAT  )  ctype = "NC_FLOAT";
else if ( xtype == NC_DOUBLE )  ctype = "NC_DOUBLE";
#if  defined  (HAVE_NETCDF4)
else if ( xtype == NC_UBYTE  )  ctype = "NC_UBYTE";
else if ( xtype == NC_LONG   )  ctype = "NC_LONG";
else if ( xtype == NC_USHORT )  ctype = "NC_USHORT";
else if ( xtype == NC_UINT   )  ctype = "NC_UINT";
else if ( xtype == NC_INT64  )  ctype = "NC_INT64";
else if ( xtype == NC_UINT64 )  ctype = "NC_UINT64";
#endif

return ctype;
}

static
void minmaxval(size_t nvals, const double *array, double *minval, double *maxval)
{
*minval = array[0];
*maxval = array[0];
for ( size_t i = 1; i < nvals; ++i )
    {
    if      ( array[i] > *maxval ) *maxval = array[i];
    else if ( array[i] < *minval ) *minval = array[i];
    }
}

static
void minmaxvalf(size_t nvals, const float *array, double *minval, double *maxval)
{
*minval = array[0];
*maxval = array[0];
for ( size_t i = 1; i < nvals; ++i )
    {
    if      ( array[i] > *maxval ) *maxval = array[i];
    else if ( array[i] < *minval ) *minval = array[i];
    }
}

void cdf_put_vara_double(int ncid, int varid, const size_t start[],
                        const size_t count[], const double *dp)
{
int status = nc_put_vara_double(ncid, varid, start, count, dp);

if ( CDF_Debug || status != NC_NOERR )
    {
    char name[256];
    nc_inq_varname(ncid, varid, name);
    nc_type xtype;
    nc_inq_vartype(ncid, varid, &xtype);
    int ndims;
    nc_inq_varndims(ncid, varid, &ndims);
    double minval = 0, maxval = 0;
    size_t nvals = 1;
    for ( int i = 0; i < ndims; ++i ) nvals *= count[i];
    minmaxval(nvals, dp, &minval, &maxval);

    Message("name=%s  type=%s  minval=%f  maxval=%f", name, cdf_var_type(xtype), minval, maxval);
    }

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void  cdf_put_vara_float(int ncid, int varid, const size_t start[],
                        const size_t count[], const float *fp)
{
int status = nc_put_vara_float(ncid, varid, start, count, fp);

if ( CDF_Debug || status != NC_NOERR )
    {
    char name[256];
    nc_inq_varname(ncid, varid, name);
    nc_type xtype;
    nc_inq_vartype(ncid, varid, &xtype);
    int ndims;
    nc_inq_varndims(ncid, varid, &ndims);
    double minval = 0, maxval = 0;
    size_t nvals = 1;
    for ( int i = 0; i < ndims; ++i ) nvals *= count[i];
    minmaxvalf(nvals, fp, &minval, &maxval);

    Message("name=%s  type=%s  minval=%f  maxval=%f", name, cdf_var_type(xtype), minval, maxval);
    }

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void  cdf_get_vara_int(int ncid, int varid, const size_t start[],
                    const size_t count[], int *dp)
{
int status = nc_get_vara_int(ncid, varid, start, count, dp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void  cdf_get_vara_double(int ncid, int varid, const size_t start[],
                        const size_t count[], double *dp)
{
int status = nc_get_vara_double(ncid, varid, start, count, dp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void  cdf_get_vara_float(int ncid, int varid, const size_t start[],
                        const size_t count[], float *fp)
{
int status = nc_get_vara_float(ncid, varid, start, count, fp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void  cdf_get_vara_text(int ncid, int varid, const size_t start[],
                        const size_t count[], char *tp)
{
int status = nc_get_vara_text(ncid, varid, start, count, tp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void  cdf_get_vara_uchar(int ncid, int varid, const size_t start[], const size_t count[], unsigned char *tp)
{
int status = nc_get_vara_uchar(ncid, varid, start, count, tp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_var_double(int ncid, int varid, const double *dp)
{
int status = nc_put_var_double(ncid, varid, dp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d val0 = %f", ncid, varid, *dp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_get_var1_text(int ncid, int varid, const size_t index[], char *tp)
{
int status = nc_get_var1_text(ncid, varid, index, tp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_get_var1_double(int ncid, int varid, const size_t index[], double *dp)
{
int status = nc_get_var1_double(ncid, varid, index, dp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_var1_double(int ncid, int varid, const size_t index[], const double *dp)
{
int status = nc_put_var1_double(ncid, varid, index, dp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d val = %f", ncid, varid, *dp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_get_var_text(int ncid, int varid, char *tp)
{
int status = nc_get_var_text(ncid, varid, tp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_get_var_short(int ncid, int varid, short *sp)
{
int status = nc_get_var_short(ncid, varid, sp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_get_var_int(int ncid, int varid, int *ip)
{
int status = nc_get_var_int(ncid, varid, ip);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_get_var_long(int ncid, int varid, long *lp)
{
int status = nc_get_var_long(ncid, varid, lp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_get_var_float(int ncid, int varid, float *fp)
{
int status = nc_get_var_float(ncid, varid, fp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d", ncid, varid);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_get_var_double(int ncid, int varid, double *dp)
{
int status = nc_get_var_double(ncid, varid, dp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d val[0] = %f", ncid, varid, *dp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_copy_att(int ncid_in, int varid_in, const char *name, int ncid_out,
                int varid_out)
{
int status = nc_copy_att(ncid_in, varid_in, name, ncid_out, varid_out);

if ( CDF_Debug || status != NC_NOERR )
    Message("%d %d %s %d %d", ncid_in, varid_out, name, ncid_out, varid_out);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_att_text(int ncid, int varid, const char *name, size_t len,
                    const char *tp)
{
int status = nc_put_att_text(ncid, varid, name, len, tp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s text = %.*s", ncid, varid, name, (int)len, tp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_att_int(int ncid, int varid, const char *name, nc_type xtype,
                    size_t len, const int *ip)
{
int status = nc_put_att_int(ncid, varid, name, xtype, len, ip);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s val = %d", ncid, varid, name, *ip);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_att_float(int ncid, int varid, const char *name, nc_type xtype,
                    size_t len, const float *dp)
{
int status = nc_put_att_float(ncid, varid, name, xtype, len, dp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s val = %g", ncid, varid, name, *dp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_put_att_double(int ncid, int varid, const char *name, nc_type xtype,
                        size_t len, const double *dp)
{
int status = nc_put_att_double(ncid, varid, name, xtype, len, dp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s val = %g", ncid, varid, name, *dp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_get_att_text(int ncid, int varid, const char *name, char *tp)
{
int status = nc_get_att_text(ncid, varid, name, tp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d name = %s", ncid, varid, name);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}

void cdf_get_att_string(int ncid, int varid, const char *name, char **tp)
{
#if  defined  (HAVE_NETCDF4)
int status = nc_get_att_string(ncid, varid, name, tp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d name = %s", ncid, varid, name);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
#endif
}


void cdf_get_att_int(int ncid, int varid, const char *name, int *ip)
{
int status = nc_get_att_int(ncid, varid, name, ip);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s val = %d", ncid, varid, name, *ip);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_get_att_long(int ncid, int varid, const char *name, long *ip)
{
#if  defined  (HAVE_NETCDF4)
int status = nc_get_att_long(ncid, varid, name, ip);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s val = %ld", ncid, varid, name, *ip);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
#endif
}


void cdf_get_att_double(int ncid, int varid, const char *name, double *dp)
{
int status;

status = nc_get_att_double(ncid, varid, name, dp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s val = %.9g", ncid, varid, name, *dp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_att(int ncid, int varid, const char *name, nc_type *xtypep,
                size_t *lenp)
{
int status = nc_inq_att(ncid, varid, name, xtypep, lenp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s", ncid, varid, name);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_atttype(int ncid, int varid, const char *name, nc_type * xtypep)
{
int status = nc_inq_atttype(ncid, varid, name, xtypep);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s", ncid, varid, name);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_attlen(int ncid, int varid, const char *name, size_t * lenp)
{
int status = nc_inq_attlen(ncid, varid, name, lenp);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s len = %d", ncid, varid, name, *lenp);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_attname(int ncid, int varid, int attnum, char *name)
{
int status = nc_inq_attname(ncid, varid, attnum, name);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d attnum = %d att = %s", ncid, varid, attnum, name);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


void cdf_inq_attid(int ncid, int varid, const char *name, int *attnump)
{
int status = nc_inq_attid(ncid, varid, name, attnump);

if ( CDF_Debug || status != NC_NOERR )
    Message("ncid = %d varid = %d att = %s", ncid, varid, name);

if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}


#if  defined  (HAVE_NETCDF4)
void cdf_def_var_chunking(int ncid, int varid, int storage, const size_t *chunksizesp)
{
int status = nc_def_var_chunking(ncid, varid, storage, chunksizesp);
if ( status != NC_NOERR ) Error("%s", nc_strerror(status));
}
#endif

#endif

#ifndef  _DMEMORY_H
#define  _DMEMORY_H

#include <stdio.h>


#define  DEBUG_MEMORY

#ifndef  WITH_FUNCTION_NAME
#define  WITH_FUNCTION_NAME
#endif

#ifdef __cplusplus
extern "C" {
#endif

extern size_t  memTotal(void);
extern void    memDebug(int debug);
extern void    memExitOnError(void);

#if  defined  DEBUG_MEMORY

extern void   *memRealloc(void *ptr, size_t size, const char *file, const char *functionname, int line);
extern void   *memCalloc (size_t nmemb, size_t size, const char *file, const char *functionname, int line);
extern void   *memMalloc (size_t size, const char *file, const char *functionname, int line);
extern void    memFree   (void *ptr, const char *file, const char *functionname, int line);

#ifdef  __cplusplus
}
#endif

#ifdef  WITH_FUNCTION_NAME
#  define  Realloc(p, s)  memRealloc((p), (s), __FILE__, __func__, __LINE__)
#  define   Calloc(n, s)   memCalloc((n), (s), __FILE__, __func__, __LINE__)
#  define   Malloc(s)      memMalloc((s), __FILE__, __func__, __LINE__)
#  define     Free(p)        memFree((p), __FILE__, __func__, __LINE__)
#else
#  define  Realloc(p, s)  memRealloc((p), (s), __FILE__, (void *) NULL, __LINE__)
#  define   Calloc(n, s)   memCalloc((n), (s), __FILE__, (void *) NULL, __LINE__)
#  define   Malloc(s)      memMalloc((s), __FILE__, (void *) NULL, __LINE__)
#  define     Free(p)        memFree((p), __FILE__, (void *) NULL, __LINE__)
#endif

#endif

#endif

#ifndef  CDF_UTIL_H_
#define  CDF_UTIL_H_

#include <stdbool.h>

int get_timeunit(size_t len, const char *ptu);

bool is_time_units(const char *timeunits);
bool is_timeaxis_units(const char *timeunits);

bool is_height_units(const char *units);
bool is_pressure_units(const char *units);
bool is_DBL_axis(  const char *longname);
bool is_depth_axis(const char *stdname, const char *longname);
bool is_height_axis(const char *stdname, const char *longname);

bool is_lon_axis(const char *units, const char *stdname);
bool is_lat_axis(const char *units, const char *stdname);

bool is_x_axis(const char *units, const char *stdname);
bool is_y_axis(const char *units, const char *stdname);

void set_gridtype(const char *attstring, int *gridtype);
void set_zaxistype(const char *attstring, int *zaxistype);
void set_calendar(const char *attstring, int *calendar);

#endif
#include <string.h>
#include <ctype.h>


void str_tolower(char *str)
{
if ( str )
    for ( size_t i = 0; str[i]; ++i )
    str[i] = (char)tolower((int)str[i]);
}


bool str_is_equal(const char *vstr, const char *cstr)
{
bool is_equal = false;
size_t clen = (cstr != NULL) ? strlen(cstr) : 0;

if ( vstr && *vstr ) is_equal = (memcmp(vstr, cstr, clen) == 0);

return is_equal;
}

int get_timeunit(size_t len, const char *ptu)
{
int timeunit = -1;

while ( isspace(*ptu) && len ) { ptu++; len--; }

if ( len > 2 )
    {
    if      ( str_is_equal(ptu, "sec") )            timeunit = TUNIT_SECOND;
    else if ( str_is_equal(ptu, "minute") )         timeunit = TUNIT_MINUTE;
    else if ( str_is_equal(ptu, "hour") )           timeunit = TUNIT_HOUR;
    else if ( str_is_equal(ptu, "day") )            timeunit = TUNIT_DAY;
    else if ( str_is_equal(ptu, "month") )          timeunit = TUNIT_MONTH;
    else if ( str_is_equal(ptu, "calendar_month") ) timeunit = TUNIT_MONTH;
    else if ( str_is_equal(ptu, "year") )           timeunit = TUNIT_YEAR;
    }
else if ( len == 1 && ptu[0] == 's' )  timeunit = TUNIT_SECOND;

return timeunit;
}


bool is_time_units(const char *timeunits)
{
while ( isspace(*timeunits) ) timeunits++;

bool status = str_is_equal(timeunits, "sec")
            || str_is_equal(timeunits, "minute")
            || str_is_equal(timeunits, "hour")
            || str_is_equal(timeunits, "day")
            || str_is_equal(timeunits, "month")
            || str_is_equal(timeunits, "calendar_month")
            || str_is_equal(timeunits, "year");

return status;
}


bool is_timeaxis_units(const char *timeunits)
{
bool status = false;

size_t len = strlen(timeunits);
char *tu = (char *) Malloc((len+1)*sizeof(char));
memcpy(tu, timeunits, (len+1) * sizeof(char));
char *ptu = tu;

for ( size_t i = 0; i < len; i++ ) ptu[i] = (char)tolower((int)ptu[i]);

int timeunit = get_timeunit(len, ptu);
if ( timeunit != -1 )
    {
    while ( ! isspace(*ptu) && *ptu != 0 ) ptu++;
    if ( *ptu )
        {
        while ( isspace(*ptu) ) ptu++;

        int timetype = str_is_equal(ptu, "as") ? TAXIS_ABSOLUTE :
                        str_is_equal(ptu, "since") ? TAXIS_RELATIVE : -1;

        status = timetype != -1;
        }
    }

Free(tu);

return status;
}


bool is_height_units(const char *units)
{
int u0 = units[0];

bool status
    = (u0=='m' && (!units[1] || strncmp(units, "meter", 5) == 0))
    || (!units[2] && units[1]=='m' && (u0=='c' || u0=='d' || u0=='k'))
    || (strncmp(units, "decimeter", 9) == 0)
    || (strncmp(units, "centimeter", 10) == 0)
    || (strncmp(units, "millimeter", 10) == 0)
    || (strncmp(units, "kilometer", 9) == 0);

return status;
}


bool is_pressure_units(const char *units)
{
bool status = false;

if ( strncmp(units, "millibar", 8) == 0 ||
    strncmp(units, "mb", 2)       == 0 ||
    strncmp(units, "hectopas", 8) == 0 ||
    strncmp(units, "hPa", 3)      == 0 ||
    strncmp(units, "Pa", 2)       == 0 )
    {
    status = true;
    }

return status;
}


bool is_DBL_axis(  const char *longname)
{
bool status = false;

if ( strcmp(longname, "depth below land")         == 0 ||
    strcmp(longname, "depth_below_land")         == 0 ||
    strcmp(longname, "levels below the surface") == 0 )
    {

        status = true;
    }

return status;
}


bool is_depth_axis(const char *stdname, const char *longname)
{
bool status = false;

if ( strcmp(stdname, "depth") == 0 )
    status = true;
else
    if ( strcmp(longname, "depth_below_sea") == 0 ||
        strcmp(longname, "depth below sea") == 0 )
    {
        status = true;
    }

return status;
}


bool is_height_axis(const char *stdname, const char *longname)
{
bool status = false;

if ( strcmp(stdname, "height") == 0 )
    status = true;
else
    if ( strcmp(longname, "height") == 0 ||
        strcmp(longname, "height above the surface") == 0 )
    {
        status = true;
    }

return status;
}


bool is_lon_axis(const char *units, const char *stdname)
{
bool status = false;
char lc_units[16];

memcpy(lc_units, units, 15);
lc_units[15] = 0;
str_tolower(lc_units);

if ( (str_is_equal(lc_units, "degree") || str_is_equal(lc_units, "radian")) &&
    (str_is_equal(stdname, "grid_longitude") || str_is_equal(stdname, "longitude")) )
    {
    status = true;
    }
else if ( str_is_equal(lc_units, "degree")
            && !str_is_equal(stdname, "grid_latitude")
            && !str_is_equal(stdname, "latitude") )
    {
    int ioff = 6;
    if ( lc_units[ioff] == 's' ) ioff++;
    if ( lc_units[ioff] == ' ' ) ioff++;
    if ( lc_units[ioff] == '_' ) ioff++;
    if ( lc_units[ioff] == 'e' ) status = true;
    }

return status;
}


bool is_lat_axis(const char *units, const char *stdname)
{
bool status = false;
char lc_units[16];

memcpy(lc_units, units, 15);
lc_units[15] = 0;
str_tolower(lc_units);

if ( (str_is_equal(lc_units, "degree") || str_is_equal(lc_units, "radian")) &&
        (str_is_equal(stdname, "grid_latitude") || str_is_equal(stdname, "latitude")) )
    {
    status = true;
    }
else if ( str_is_equal(lc_units, "degree")
            && !str_is_equal(stdname, "grid_longitude")
            && !str_is_equal(stdname, "longitude") )
    {
    int ioff = 6;
    if ( lc_units[ioff] == 's' ) ioff++;
    if ( lc_units[ioff] == ' ' ) ioff++;
    if ( lc_units[ioff] == '_' ) ioff++;
    if ( lc_units[ioff] == 'n' || lc_units[ioff] == 's' ) status = true;
    }

return status;
}


bool is_x_axis(const char *units, const char *stdname)
{
(void)units;
return (strcmp(stdname, "projection_x_coordinate") == 0);
}


bool is_y_axis(const char *units, const char *stdname)
{
(void)units;
return (strcmp(stdname, "projection_y_coordinate") == 0);
}


void set_gridtype(const char *attstring, int *gridtype)
{
if      ( strcmp(attstring, "gaussian reduced") == 0 )
    *gridtype = GRID_GAUSSIAN_REDUCED;
else if ( strcmp(attstring, "gaussian") == 0 )
    *gridtype = GRID_GAUSSIAN;
else if ( strncmp(attstring, "spectral", 8) == 0 )
    *gridtype = GRID_SPECTRAL;
else if ( strncmp(attstring, "fourier", 7) == 0 )
    *gridtype = GRID_FOURIER;
else if ( strcmp(attstring, "trajectory") == 0 )
    *gridtype = GRID_TRAJECTORY;
else if ( strcmp(attstring, "generic") == 0 )
    *gridtype = GRID_GENERIC;
else if ( strcmp(attstring, "cell") == 0 )
    *gridtype = GRID_UNSTRUCTURED;
else if ( strcmp(attstring, "unstructured") == 0 )
    *gridtype = GRID_UNSTRUCTURED;
else if ( strcmp(attstring, "curvilinear") == 0 )
    ;
else if ( strcmp(attstring, "characterxy") == 0 )
    *gridtype = GRID_CHARXY;
else if ( strcmp(attstring, "sinusoidal") == 0 )
    ;
else if ( strcmp(attstring, "laea") == 0 )
    ;
else if ( strcmp(attstring, "lcc2") == 0 )
    ;
else if ( strcmp(attstring, "linear") == 0 )
    ;
else
    {
    static bool warn = true;
    if ( warn )
        {
        warn = false;
        Warning("NetCDF attribute grid_type='%s' unsupported!", attstring);
        }
    }
}


void set_zaxistype(const char *attstring, int *zaxistype)
{
if      ( strcmp(attstring, "toa") == 0 ) *zaxistype = ZAXIS_TOA;
else if ( strcmp(attstring, "cloudbase") == 0 ) *zaxistype = ZAXIS_CLOUD_BASE;
else if ( strcmp(attstring, "cloudtop") == 0 ) *zaxistype = ZAXIS_CLOUD_TOP;
else if ( strcmp(attstring, "isotherm0") == 0 ) *zaxistype = ZAXIS_ISOTHERM_ZERO;
else if ( strcmp(attstring, "seabottom") == 0 ) *zaxistype = ZAXIS_SEA_BOTTOM;
else if ( strcmp(attstring, "lakebottom") == 0 ) *zaxistype = ZAXIS_LAKE_BOTTOM;
else if ( strcmp(attstring, "sedimentbottom") == 0 ) *zaxistype = ZAXIS_SEDIMENT_BOTTOM;
else if ( strcmp(attstring, "sedimentbottomta") == 0 ) *zaxistype = ZAXIS_SEDIMENT_BOTTOM_TA;
else if ( strcmp(attstring, "sedimentbottomtw") == 0 ) *zaxistype = ZAXIS_SEDIMENT_BOTTOM_TW;
else if ( strcmp(attstring, "mixlayer") == 0 ) *zaxistype = ZAXIS_MIX_LAYER;
else if ( strcmp(attstring, "atmosphere") == 0 ) *zaxistype = ZAXIS_ATMOSPHERE;
else
    {
    static bool warn = true;
    if ( warn )
        {
        warn = false;
        Warning("NetCDF attribute level_type='%s' unsupported!", attstring);
        }
    }
}


void set_calendar(const char *attstring, int *calendar)
{
if ( str_is_equal(attstring, "standard") )
    *calendar = CALENDAR_STANDARD;
else if ( str_is_equal(attstring, "gregorian") )
    *calendar = CALENDAR_GREGORIAN;
else if ( str_is_equal(attstring, "none") )
    *calendar = CALENDAR_NONE;
else if ( str_is_equal(attstring, "proleptic") )
    *calendar = CALENDAR_PROLEPTIC;
else if ( str_is_equal(attstring, "360") )
    *calendar = CALENDAR_360DAYS;
else if ( str_is_equal(attstring, "365") ||
            str_is_equal(attstring, "noleap") )
    *calendar = CALENDAR_365DAYS;
else if ( str_is_equal(attstring, "366") ||
            str_is_equal(attstring, "all_leap") )
    *calendar = CALENDAR_366DAYS;
else
    Warning("calendar >%s< unsupported!", attstring);
}
#ifndef _STREAM_CDF_H
#define _STREAM_CDF_H


void   cdfDefVars(stream_t *streamptr);
void   cdfDefTimestep(stream_t *streamptr, int tsID);
int    cdfInqTimestep(stream_t *streamptr, int tsID);
int    cdfInqContents(stream_t *streamptr);
void   cdfDefHistory(stream_t *streamptr, int size, const char *history);
int    cdfInqHistorySize(stream_t *streamptr);
void   cdfInqHistoryString(stream_t *streamptr, char *history);

void   cdfEndDef(stream_t * streamptr);
void   cdfDefRecord(stream_t * streamptr);

void   cdfCopyRecord(stream_t *streamptr2, stream_t *streamptr1);

void   cdfDefineAttributes(int vlistID, int varID, int fileID, int ncvarID);

void   cdf_read_record(stream_t *streamptr, int memtype, void *data, size_t *nmiss);
void   cdf_write_record(stream_t *streamptr, int memtype, const void *data, size_t nmiss);

void   cdf_read_var(stream_t *streamptr, int varID, int memtype, void *data, size_t *nmiss);
void   cdf_write_var(stream_t *streamptr, int varID, int memtype, const void *data, size_t nmiss);

void   cdf_read_var_slice(stream_t *streamptr, int varID, int levelID, int memtype, void *data, size_t *nmiss);
void   cdf_write_var_slice(stream_t *streamptr, int varID, int levelID, int memtype, const void *data, size_t nmiss);

void   cdf_write_var_chunk(stream_t *streamptr, int varID, int memtype,
                        const int rect[][2], const void *data, size_t nmiss);

void cdfDefVarDeflate(int ncid, int ncvarid, int deflate_level);
void cdfDefTime(stream_t* streamptr);

void cdf_scale_add(size_t size, double *data, double addoffset, double scalefactor);

int cdfDefDatatype(int datatype, stream_t* streamptr);

#endif

#ifndef  CDI_ATT_H
#define  CDI_ATT_H

#ifdef  HAVE_CONFIG_H
#endif

#ifndef  CDI_LIMITS_H
#endif


typedef struct {
size_t    xsz;
size_t    namesz;
char     *name;
int       indtype;
int       exdtype;




size_t    nelems;
void     *xvalue;
} cdi_att_t;


typedef struct {
size_t     nalloc;
size_t     nelems;
cdi_att_t  value[MAX_ATTRIBUTES];
} cdi_atts_t;


int cdiDeleteAtts(int vlistID, int varID);
int cdiAttsGetSize(void *p, int varID, void *context);
void cdiAttsPack(void *p, int varID, void *buf, int size, int *position, void *context);
void cdiAttsUnpack(int cdiID, int varID, void *buf, int size, int *position, void *context);

#endif


#ifndef  GRID_H
#define  GRID_H

#include <stdbool.h>


extern int (*proj_lonlat_to_lcc_func)();
extern int (*proj_lcc_to_lonlat_func)();
extern int (*proj_lonlat_to_stere_func)();
extern int (*proj_stere_to_lonlat_func)();

typedef unsigned char mask_t;

typedef struct grid_t grid_t;

struct gridVirtTable
{
void (*destroy)(grid_t *gridptr);
grid_t *(*copy)(grid_t *gridptr);
void (*copyScalarFields)(grid_t *gridptrOrig, grid_t *gridptrDup);
void (*copyArrayFields)(grid_t *gridptrOrig, grid_t *gridptrDup);
void (*defXVals)(grid_t *gridptr, const double *xvals);
void (*defYVals)(grid_t *gridptr, const double *yvals);
void (*defMask)(grid_t *gridptr, const int *mask);
void (*defMaskGME)(grid_t *gridptr, const int *mask);
void (*defXBounds)(grid_t *gridptr, const double *xbounds);
void (*defYBounds)(grid_t *gridptr, const double *ybounds);
void (*defArea)(grid_t *gridptr, const double *area);
double (*inqXVal)(grid_t *gridptr, size_t index);
double (*inqYVal)(grid_t *gridptr, size_t index);
size_t (*inqXVals)(grid_t *gridptr, double *xvals);
size_t (*inqXValsPart)(grid_t *gridptr, int start, size_t length, double *xvals);
size_t (*inqXCvals)(grid_t *gridptr, char **xcvals);
int (*inqXIsc)(grid_t *gridptr);
size_t (*inqYVals)(grid_t *gridptr, double *yvals);
size_t (*inqYValsPart)(grid_t *gridptr, int start, size_t length, double *yvals);
size_t (*inqYCvals)(grid_t *gridptr, char **ycvals);
int (*inqYIsc)(grid_t *gridptr);
const double *(*inqXValsPtr)(grid_t *gridptr);
const char **(*inqXCvalsPtr)(grid_t *gridptr);
const double *(*inqYValsPtr)(grid_t *gridptr);
const char **(*inqYCvalsPtr)(grid_t *gridptr);

bool (*compareXYFull)(grid_t *gridRef, grid_t *gridTest);

bool (*compareXYAO)(grid_t *gridRef, grid_t *gridTest);
void (*inqArea)(grid_t *gridptr, double *area);
const double *(*inqAreaPtr)(grid_t *gridptr);
int (*hasArea)(grid_t *gridptr);
size_t (*inqMask)(grid_t *gridptr, int *mask);
int (*inqMaskGME)(grid_t *gridptr, int *mask_gme);
size_t (*inqXBounds)(grid_t *gridptr, double *xbounds);
size_t (*inqYBounds)(grid_t *gridptr, double *ybounds);
const double *(*inqXBoundsPtr)(grid_t *gridptr);
const double *(*inqYBoundsPtr)(grid_t *gridptr);
};

struct gridaxis_t {
char    name[CDI_MAX_NAME];
char    longname[CDI_MAX_NAME];
char    units[CDI_MAX_NAME];
char    dimname[CDI_MAX_NAME];
const char *stdname;
size_t  size;
short   flag;
double  first, last, inc;
double *vals;
int clength;
char  **cvals;
double *bounds;
};


struct grid_gme_t {
int     nd, ni, ni2, ni3;
};

struct grid_t {
char    vdimname[CDI_MAX_NAME];
char    mapname[CDI_MAX_NAME];
char    mapping[CDI_MAX_NAME];
char   *name;
int     self;
size_t  size;
int     type;
int     datatype;
int     proj;
int     projtype;
mask_t *mask;
mask_t *mask_gme;
double *area;
struct grid_gme_t  gme;
int     number, position;
int     trunc;
int     nvertex;
char   *reference;
unsigned char uuid[CDI_UUID_SIZE];
int    *rowlon;
int     nrowlon;
int     np;
signed char isCyclic;
bool    lcomplex;
bool    hasdims;
bool uvRelativeToGrid;
struct gridaxis_t x;
struct gridaxis_t y;
const struct gridVirtTable *vtable;
cdi_atts_t atts;
int  scanningMode;
bool iScansNegatively, jScansPositively, jPointsAreConsecutive;

};


void grid_init(grid_t *gridptr);
void cdiGridTypeInit(grid_t *gridptr, int gridtype, size_t size);
void grid_free(grid_t *gridptr);
grid_t *grid_to_pointer(int gridID);
extern const struct gridVirtTable cdiGridVtable;

unsigned cdiGridCount(void);

void gridVerifyProj(int gridID);

const double *gridInqXvalsPtr(int gridID);
const double *gridInqYvalsPtr(int gridID);

const char **gridInqXCvalsPtr(int gridID);
const char **gridInqYCvalsPtr(int gridID);

const double *gridInqXboundsPtr(int gridID);
const double *gridInqYboundsPtr(int gridID);
const double *gridInqAreaPtr(int gridID);

const char *gridInqReferencePtr(int gridID);

int gridGenerate(const grid_t *grid);



void cdiGridGetIndexList(unsigned, int * );

void
gridUnpack(char * unpackBuffer, int unpackBufferSize,
        int * unpackBufferPos, int originNamespace, void *context,
        int force_id);

struct addIfNewRes
{
int Id;
int isNew;
};

struct addIfNewRes cdiVlistAddGridIfNew(int vlistID, grid_t *grid, int mode);

int gridVerifyGribParamLCC(double missval, double *lon_0, double *lat_0, double *lat_1, double *lat_2,
                        double *a, double *rf, double *xval_0, double *yval_0, double *x_0, double *y_0);
int gridVerifyGribParamSTERE(double missval, double *lon_0, double *lat_ts, double *lat_0,
                            double *a, double *xval_0, double *yval_0, double *x_0, double *y_0);

#endif

#ifndef  CDF_LAZY_GRID_H_
#define  CDF_LAZY_GRID_H_

#ifdef HAVE_CONFIG_H
#endif

#ifdef HAVE_MMAP
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif

#ifdef HAVE_LIBPTHREAD
#include <pthread.h>
#endif

#include <string.h>


struct xyValGet {
double scalefactor, addoffset;
size_t start[3], count[3], size, dimsize;
int datasetNCId, varNCId;
short ndims;
};

struct cdfLazyGridIds {
int datasetNCId, varNCId;
};

struct cdfLazyGrid
{
grid_t base;
const struct gridVirtTable *baseVtable;
struct cdfLazyGridIds cellAreaGet, xBoundsGet, yBoundsGet;
struct xyValGet xValsGet, yValsGet;
#ifdef HAVE_LIBPTHREAD
pthread_mutex_t loadSerialize;
#endif
};


extern double *cdfPendingLoad;

void cdfLazyGridRenew(struct cdfLazyGrid *restrict *restrict gridpptr, int gridtype);
void cdfBaseGridRenew(struct cdfLazyGrid *restrict *restrict gridpptr, int gridtype);

void cdfLazyGridDestroy(struct cdfLazyGrid *lazyGrid);

#endif

#ifdef HAVE_CONFIG_H
#endif

#ifdef HAVE_LIBNETCDF


static struct gridVirtTable cdfLazyGridVtable;
double *cdfPendingLoad;
#ifdef HAVE_LIBPTHREAD
static pthread_once_t cdfLazyInitialized = PTHREAD_ONCE_INIT;
#else
static bool cdfLazyInitialized;
#endif


#ifdef HAVE_LIBPTHREAD
#define lock_lazy_load(plGrid) pthread_mutex_lock(&((plGrid)->loadSerialize))
#define unlock_lazy_load(plGrid) pthread_mutex_unlock(&((plGrid)->loadSerialize))
#define destroy_lazy_load_lock(plGrid) pthread_mutex_destroy(&((plGrid)->loadSerialize))
#define init_lazy_load_lock(plGrid) pthread_mutex_init(&((plGrid)->loadSerialize), NULL)
#else
#define lock_lazy_load(plGrid)
#define unlock_lazy_load(plGrid)
#define destroy_lazy_load_lock(plGrid)
#define init_lazy_load_lock(plGrid)
#endif



void cdfLazyGridDestroy(struct cdfLazyGrid *lazyGrid)
{
if (lazyGrid->base.area == cdfPendingLoad)  lazyGrid->base.area = NULL;
if (lazyGrid->base.x.vals == cdfPendingLoad) lazyGrid->base.x.vals = NULL;
if (lazyGrid->base.y.vals == cdfPendingLoad) lazyGrid->base.y.vals = NULL;
if (lazyGrid->base.x.bounds == cdfPendingLoad) lazyGrid->base.x.bounds = NULL;
if (lazyGrid->base.y.bounds == cdfPendingLoad) lazyGrid->base.y.bounds = NULL;
destroy_lazy_load_lock(lazyGrid);
}

static void cdfLazyGridDelete(grid_t *grid)
{
struct cdfLazyGrid *cdfGrid = (struct cdfLazyGrid *)grid;
void (*baseDestroy)(grid_t *grid) = cdfGrid->baseVtable->destroy;
cdfLazyGridDestroy(cdfGrid);
baseDestroy(grid);
}

static void cdfLazyGridDestroyOnce(void)
{

}

static void
cdfLazyGridDefArea(grid_t *grid, const double *area)
{
struct cdfLazyGrid *cdfGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(cdfGrid);
if (grid->area == cdfPendingLoad) grid->area = NULL;
cdfGrid->cellAreaGet.datasetNCId = -1;
cdfGrid->cellAreaGet.varNCId = -1;
cdfGrid->baseVtable->defArea(grid, area);
unlock_lazy_load(cdfGrid);
}


static const double *
cdfLazyGridInqAreaPtr(grid_t *grid)
{
struct cdfLazyGrid *lazyGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(lazyGrid);
if (grid->area == cdfPendingLoad)
    {
    grid->area = (double *)Malloc((size_t)grid->size * sizeof(double));
    cdf_get_var_double(lazyGrid->cellAreaGet.datasetNCId,
                        lazyGrid->cellAreaGet.varNCId, grid->area);
    }
unlock_lazy_load(lazyGrid);
return lazyGrid->baseVtable->inqAreaPtr(grid);
}

static void
cdfLazyGridInqArea(grid_t *grid, double *area)
{
grid->vtable->inqAreaPtr(grid);
struct cdfLazyGrid *lazyGrid = (struct cdfLazyGrid *)grid;
lazyGrid->baseVtable->inqArea(grid, area);
}


static void
cdfLazyLoadXYVals(struct xyValGet *valsGet, double **valsp)
{
double *grid_vals = (double *)Malloc(valsGet->size * sizeof (double));
*valsp = grid_vals;
if ( valsGet->ndims == 3 )
    cdf_get_vara_double(valsGet->datasetNCId, valsGet->varNCId,
                        valsGet->start, valsGet->count, grid_vals);
else
    cdf_get_var_double(valsGet->datasetNCId, valsGet->varNCId, grid_vals);
cdf_scale_add(valsGet->size, grid_vals, valsGet->addoffset, valsGet->scalefactor);
}

static const double *
cdfLazyGridInqXValsPtr(grid_t *grid)
{
struct cdfLazyGrid *lazyGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(lazyGrid);
if (grid->x.vals == cdfPendingLoad)
    cdfLazyLoadXYVals(&lazyGrid->xValsGet, &grid->x.vals);
unlock_lazy_load(lazyGrid);
return lazyGrid->baseVtable->inqXValsPtr(grid);
}

static const double *
cdfLazyGridInqYValsPtr(grid_t *grid)
{
struct cdfLazyGrid *lazyGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(lazyGrid);
if (grid->y.vals == cdfPendingLoad)
    cdfLazyLoadXYVals(&lazyGrid->yValsGet, &grid->y.vals);
unlock_lazy_load(lazyGrid);
return lazyGrid->baseVtable->inqYValsPtr(grid);
}

static double
cdfLazyGridInqXYVal(grid_t *grid, size_t index,
                    const struct xyValGet *valsGet, double *vals,
                    const double *(*inqValsPtr)(grid_t *gridptr))
{
size_t size = valsGet->size;
double v;
if ( vals == cdfPendingLoad )
    {

    if ( index == 0 || index == size - 1 )
        {
        size_t indexND[3];
        if ( valsGet->ndims == 3 )
            {
            indexND[0] = 0;
            indexND[1] = index / valsGet->count[2];
            indexND[2] = index % valsGet->count[2];
            }
        else if ( valsGet->ndims == 2)
            {
            indexND[0] = index / (size_t)grid->x.size;
            indexND[1] = index % (size_t)grid->x.size;
            }
        else
            indexND[0] = index;
        cdf_get_var1_double(valsGet->datasetNCId, valsGet->varNCId, indexND, &v);
        }
    else
        {
        const double *grid_vals = inqValsPtr(grid);
        v = grid_vals[index];
        }
    }
else if ( vals )
    v = vals[index];
else
    v = 0.0;
return v;
}

static void
cdfLazyGridDefXVals(grid_t *grid, const double *vals)
{
struct cdfLazyGrid *cdfGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(cdfGrid);
if (grid->x.vals == cdfPendingLoad)
    grid->x.vals = NULL;
cdfGrid->xValsGet.datasetNCId = -1;
cdfGrid->xValsGet.varNCId = -1;
cdfGrid->baseVtable->defXVals(grid, vals);
unlock_lazy_load(cdfGrid);
}

static void
cdfLazyGridDefYVals(grid_t *grid, const double *vals)
{
struct cdfLazyGrid *cdfGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(cdfGrid);
if (grid->y.vals == cdfPendingLoad)
    grid->y.vals = NULL;
cdfGrid->yValsGet.datasetNCId = -1;
cdfGrid->yValsGet.varNCId = -1;
cdfGrid->baseVtable->defYVals(grid, vals);
unlock_lazy_load(cdfGrid);
}

static double
cdfLazyGridInqXVal(grid_t *grid, size_t index)
{
struct cdfLazyGrid *lazyGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(lazyGrid);
double rv = cdfLazyGridInqXYVal(grid, index, &lazyGrid->xValsGet,
                                grid->x.vals, grid->vtable->inqXValsPtr);
unlock_lazy_load(lazyGrid);
return rv;
}

static double
cdfLazyGridInqYVal(grid_t *grid, size_t index)
{
struct cdfLazyGrid *lazyGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(lazyGrid);
double rv = cdfLazyGridInqXYVal(grid, index, &lazyGrid->yValsGet,
                                grid->y.vals, grid->vtable->inqYValsPtr);
unlock_lazy_load(lazyGrid);
return rv;
}

static bool
cdfLazyXYValGetCompare(struct cdfLazyGrid *lazyGridRef,
                    struct cdfLazyGrid *lazyGridTest)
{
struct xyValGet *valsGetXRef = &lazyGridRef->xValsGet,
    *valsGetYRef = &lazyGridRef->yValsGet,
    *valsGetXTest = &lazyGridTest->xValsGet,
    *valsGetYTest = &lazyGridTest->yValsGet;
if (valsGetXRef->datasetNCId == -1
    || valsGetXTest->datasetNCId == -1
    || valsGetYRef->datasetNCId == -1
    || valsGetYTest->datasetNCId == -1)
    return lazyGridRef->baseVtable->compareXYFull(&lazyGridRef->base,
                                                &lazyGridTest->base);
return valsGetXRef->datasetNCId != valsGetXTest->datasetNCId
    ||   valsGetXRef->varNCId     != valsGetXTest->varNCId
    ||   valsGetYRef->datasetNCId != valsGetYTest->datasetNCId
    ||   valsGetYRef->varNCId     != valsGetYTest->varNCId;
}

static bool
cdfLazyCompareXYFull(grid_t *gridRef, grid_t *gridTest)
{
bool diff;
struct cdfLazyGrid *lazyGridRef = (struct cdfLazyGrid *)gridRef;
if (gridTest->vtable == &cdfLazyGridVtable)
    diff = cdfLazyXYValGetCompare(lazyGridRef, (struct cdfLazyGrid *)gridTest);
else
    diff = lazyGridRef->baseVtable->compareXYFull(gridRef, gridTest);
return diff;
}

static bool
cdfLazyCompareXYAO(grid_t *gridRef, grid_t *gridTest)
{
bool diff;
struct cdfLazyGrid *lazyGridRef = (struct cdfLazyGrid *)gridRef;
if (gridTest->vtable == &cdfLazyGridVtable)
    diff = cdfLazyXYValGetCompare(lazyGridRef, (struct cdfLazyGrid *)gridTest);
else
    diff = lazyGridRef->baseVtable->compareXYAO(gridRef, gridTest);
return diff;
}


static const double *
cdfLazyGridInqXBoundsPtr(grid_t *grid)
{
struct cdfLazyGrid *lazyGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(lazyGrid);
if (grid->x.bounds == cdfPendingLoad)
    {
    grid->x.bounds = (double *)Malloc((size_t)grid->nvertex
                                    * (size_t)grid->size * sizeof(double));
    cdf_get_var_double(lazyGrid->xBoundsGet.datasetNCId,
                        lazyGrid->xBoundsGet.varNCId, grid->x.bounds);
    }
unlock_lazy_load(lazyGrid);
return lazyGrid->baseVtable->inqXBoundsPtr(grid);
}

static void
cdfLazyGridDefXBounds(grid_t *grid, const double *xbounds)
{
struct cdfLazyGrid *cdfGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(cdfGrid);
if (grid->x.bounds == cdfPendingLoad)
    grid->x.bounds = NULL;
cdfGrid->xBoundsGet.datasetNCId = -1;
cdfGrid->xBoundsGet.varNCId = -1;
cdfGrid->baseVtable->defXBounds(grid, xbounds);
unlock_lazy_load(cdfGrid);
}

static void
cdfLazyGridDefYBounds(grid_t *grid, const double *ybounds)
{
struct cdfLazyGrid *cdfGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(cdfGrid);
if (grid->y.bounds == cdfPendingLoad)
    grid->y.bounds = NULL;
cdfGrid->yBoundsGet.datasetNCId = -1;
cdfGrid->yBoundsGet.varNCId = -1;
cdfGrid->baseVtable->defYBounds(grid, ybounds);
unlock_lazy_load(cdfGrid);
}

static const double *
cdfLazyGridInqYBoundsPtr(grid_t *grid)
{
struct cdfLazyGrid *lazyGrid = (struct cdfLazyGrid *)grid;
lock_lazy_load(lazyGrid);
if (grid->y.bounds == cdfPendingLoad)
    {
    grid->y.bounds = (double *)Malloc((size_t)grid->nvertex
                                    * (size_t)grid->size * sizeof(double));
    cdf_get_var_double(lazyGrid->yBoundsGet.datasetNCId,
                        lazyGrid->yBoundsGet.varNCId, grid->y.bounds);
    }
unlock_lazy_load(lazyGrid);
return lazyGrid->baseVtable->inqYBoundsPtr(grid);
}

static void
cdfLazyGridCopyScalarFields(grid_t *gridptrOrig, grid_t *gridptrDup)
{
struct cdfLazyGrid *lazyGridDup = (struct cdfLazyGrid *)gridptrDup,
    *lazyGridOrig = (struct cdfLazyGrid *)gridptrOrig;
lazyGridOrig->baseVtable->copyScalarFields(gridptrOrig, &lazyGridDup->base);
lazyGridDup->baseVtable = lazyGridOrig->baseVtable;
lazyGridDup->cellAreaGet = lazyGridOrig->cellAreaGet;
lazyGridDup->xBoundsGet = lazyGridOrig->xBoundsGet;
lazyGridDup->yBoundsGet = lazyGridOrig->yBoundsGet;
lazyGridDup->xValsGet = lazyGridOrig->xValsGet;
lazyGridDup->yValsGet = lazyGridOrig->yValsGet;
init_lazy_load_lock(lazyGridDup);
}

static void
cdfLazyGridCopyArrayFields(grid_t *gridptrOrig, grid_t *gridptrDup)
{
size_t nrowlon = (size_t)gridptrOrig->nrowlon;
size_t gridsize = (size_t)gridptrOrig->size;
int gridtype = gridptrOrig->type;
int irregular = gridtype == GRID_CURVILINEAR || gridtype == GRID_UNSTRUCTURED;
if ( nrowlon )
    {
    gridptrDup->rowlon = (int *)Malloc(nrowlon * sizeof (int));
    memcpy(gridptrDup->rowlon, gridptrOrig->rowlon, nrowlon * sizeof(int));
    }

if ( gridptrOrig->x.vals != NULL && gridptrOrig->x.vals != cdfPendingLoad )
    {
    size_t size  = irregular ? gridsize : (size_t)gridptrOrig->x.size;

    gridptrDup->x.vals = (double *)Malloc(size * sizeof (double));
    memcpy(gridptrDup->x.vals, gridptrOrig->x.vals, size * sizeof (double));
    }

if ( gridptrOrig->y.vals != NULL && gridptrOrig->y.vals != cdfPendingLoad )
    {
    size_t size  = irregular ? gridsize : (size_t)gridptrOrig->y.size;

    gridptrDup->y.vals = (double *)Malloc(size * sizeof (double));
    memcpy(gridptrDup->y.vals, gridptrOrig->y.vals, size * sizeof (double));
    }

if ( gridptrOrig->x.bounds != NULL && gridptrOrig->x.bounds != cdfPendingLoad )
    {
    size_t size  = (irregular ? gridsize : (size_t)gridptrOrig->x.size)
        * (size_t)gridptrOrig->nvertex;

    gridptrDup->x.bounds = (double *)Malloc(size * sizeof (double));
    memcpy(gridptrDup->x.bounds, gridptrOrig->x.bounds, size * sizeof (double));
    }

if ( gridptrOrig->y.bounds != NULL && gridptrOrig->y.bounds != cdfPendingLoad )
    {
    size_t size = (irregular ? gridsize : (size_t)gridptrOrig->y.size)
        * (size_t)gridptrOrig->nvertex;

    gridptrDup->y.bounds = (double *)Malloc(size * sizeof (double));
    memcpy(gridptrDup->y.bounds, gridptrOrig->y.bounds, size * sizeof (double));
    }

{
    if ( gridptrOrig->area != NULL && gridptrOrig->area != cdfPendingLoad )
    {
        size_t size = gridsize;

        gridptrDup->area = (double *)Malloc(size * sizeof (double));
        memcpy(gridptrDup->area, gridptrOrig->area, size * sizeof (double));
    }
}

if ( gridptrOrig->mask != NULL )
    {
    size_t size = gridsize;

    gridptrDup->mask = (mask_t *)Malloc(size * sizeof(mask_t));
    memcpy(gridptrDup->mask, gridptrOrig->mask, size * sizeof (mask_t));
    }

if ( gridptrOrig->mask_gme != NULL )
    {
    size_t size = gridsize;

    gridptrDup->mask_gme = (mask_t *)Malloc(size * sizeof (mask_t));
    memcpy(gridptrDup->mask_gme, gridptrOrig->mask_gme, size * sizeof(mask_t));
    }
}

static grid_t *
cdfLazyGridCopy(grid_t *gridptrOrig)
{
struct cdfLazyGrid *lazyGridDup
    = (struct cdfLazyGrid *)Malloc(sizeof (*lazyGridDup));
gridptrOrig->vtable->copyScalarFields(gridptrOrig, &lazyGridDup->base);
gridptrOrig->vtable->copyArrayFields(gridptrOrig, &lazyGridDup->base);
return &lazyGridDup->base;
}

static void
cdfLazyGridInitOnce(void)
{
cdfLazyGridVtable = cdiGridVtable;
cdfLazyGridVtable.destroy = cdfLazyGridDelete;
cdfLazyGridVtable.copy = cdfLazyGridCopy;
cdfLazyGridVtable.copyScalarFields = cdfLazyGridCopyScalarFields;
cdfLazyGridVtable.copyArrayFields = cdfLazyGridCopyArrayFields;
cdfLazyGridVtable.defArea = cdfLazyGridDefArea;
cdfLazyGridVtable.inqAreaPtr = cdfLazyGridInqAreaPtr;
cdfLazyGridVtable.inqArea = cdfLazyGridInqArea;
cdfLazyGridVtable.inqXValsPtr = cdfLazyGridInqXValsPtr;
cdfLazyGridVtable.inqYValsPtr = cdfLazyGridInqYValsPtr;
cdfLazyGridVtable.inqXVal = cdfLazyGridInqXVal;
cdfLazyGridVtable.inqYVal = cdfLazyGridInqYVal;
cdfLazyGridVtable.defXVals = cdfLazyGridDefXVals;
cdfLazyGridVtable.defYVals = cdfLazyGridDefYVals;
cdfLazyGridVtable.compareXYFull = cdfLazyCompareXYFull;
cdfLazyGridVtable.compareXYAO = cdfLazyCompareXYAO;
cdfLazyGridVtable.defXBounds = cdfLazyGridDefXBounds;
cdfLazyGridVtable.defYBounds = cdfLazyGridDefYBounds;
cdfLazyGridVtable.inqXBoundsPtr = cdfLazyGridInqXBoundsPtr;
cdfLazyGridVtable.inqYBoundsPtr = cdfLazyGridInqYBoundsPtr;


cdfPendingLoad = (double *)&cdfPendingLoad;

atexit(cdfLazyGridDestroyOnce);
#ifndef HAVE_LIBPTHREAD
cdfLazyInitialized = true;
#endif
}

static void
cdfBaseGridInit(grid_t *grid, int gridtype)
{
grid_init(grid);
cdiGridTypeInit(grid, gridtype, 0);
}

static void
cdfLazyGridInit(struct cdfLazyGrid *grid, int gridtype)
{
#ifdef HAVE_LIBPTHREAD
pthread_once(&cdfLazyInitialized, cdfLazyGridInitOnce);
#else
if (cdfLazyInitialized) ; else cdfLazyGridInitOnce();
#endif
cdfBaseGridInit(&grid->base, gridtype);
grid->baseVtable = grid->base.vtable;
grid->cellAreaGet.datasetNCId = -1;
grid->cellAreaGet.varNCId = -1;
grid->xValsGet.datasetNCId = -1;
grid->xValsGet.varNCId = -1;
grid->yValsGet.datasetNCId = -1;
grid->yValsGet.varNCId = -1;
grid->xBoundsGet.datasetNCId = -1;
grid->xBoundsGet.varNCId = -1;
grid->yBoundsGet.datasetNCId = -1;
grid->yBoundsGet.varNCId = -1;
grid->base.vtable = &cdfLazyGridVtable;
init_lazy_load_lock(grid);
}


void cdfLazyGridRenew(struct cdfLazyGrid *restrict *restrict gridpptr, int gridtype)
{
struct cdfLazyGrid *restrict grid = *gridpptr;
if (!grid)
    *gridpptr = grid = (struct cdfLazyGrid *)Malloc(sizeof (*grid));
cdfLazyGridInit(grid, gridtype);
}


void cdfBaseGridRenew(struct cdfLazyGrid *restrict *restrict gridpptr, int gridtype)
{
struct cdfLazyGrid *restrict grid = *gridpptr;
if (!grid)
    *gridpptr = grid = (struct cdfLazyGrid *)Malloc(sizeof (grid_t));
cdfBaseGridInit((grid_t*)grid, gridtype);
}

#endif


#ifndef CDI_CKSUM_H_
#define CDI_CKSUM_H_

#include <inttypes.h>

uint32_t cdiCheckSum(int type, int count, const void *data);

#endif


#ifdef HAVE_CONFIG_H
#endif

#include <inttypes.h>
#include <sys/types.h>

void
memcrc_r(uint32_t *state, const unsigned char *block, size_t block_len);

void
memcrc_r_eswap(uint32_t *state, const unsigned char *elems, size_t num_elems,
            size_t elem_size);

uint32_t
memcrc_finish(uint32_t *state, off_t total_size);

uint32_t
memcrc(const unsigned char *b, size_t n);


#ifdef HAVE_CONFIG_H
#endif

#ifndef SERIALIZE_H
#define SERIALIZE_H

#include <string.h>

#ifndef  CDI_CKSUM_H_
#endif
#ifndef  ERROR_H
#endif


int serializeGetSize(int count, int datatype, void *context);
void serializePack(const void *data, int count, int datatype,
                void *buf, int buf_size, int *position, void *context);
void serializeUnpack(const void *buf, int buf_size, int *position,
                    void *data, int count, int datatype, void *context);


static inline int
serializeStrTabGetPackSize(const char **strTab, int numStr,
                        void *context)
{
xassert(numStr >= 0);
int packBuffSize = 0;
for (size_t i = 0; i < (size_t)numStr; ++i)
{
    size_t len = strlen(strTab[i]);
    packBuffSize +=
    serializeGetSize(1, CDI_DATATYPE_INT, context)
    + serializeGetSize((int)len, CDI_DATATYPE_TXT, context);
}
packBuffSize +=
    serializeGetSize(1, CDI_DATATYPE_UINT32, context);
return packBuffSize;
}

static inline void
serializeStrTabPack(const char **strTab, int numStr,
                    void *buf, int buf_size, int *position, void *context)
{
uint32_t d = 0;
xassert(numStr >= 0);
for (size_t i = 0; i < (size_t)numStr; ++i)
{
    int len = (int)strlen(strTab[i]);
    serializePack(&len, 1, CDI_DATATYPE_INT,
                buf, buf_size, position, context);
    serializePack(strTab[i], len, CDI_DATATYPE_TXT,
                buf, buf_size, position, context);
    d ^= cdiCheckSum(CDI_DATATYPE_TXT, len, strTab[i]);
}
serializePack(&d, 1, CDI_DATATYPE_UINT32,
                buf, buf_size, position, context);
}

static inline void
serializeStrTabUnpack(const void *buf, int buf_size, int *position,
                    char **strTab, int numStr, void *context)
{
uint32_t d, d2 = 0;
xassert(numStr >= 0);
for (size_t i = 0; i < (size_t)numStr; ++i)
    {
    int len;
    serializeUnpack(buf, buf_size, position,
                    &len, 1, CDI_DATATYPE_INT, context);
    serializeUnpack(buf, buf_size, position,
                    strTab[i], len, CDI_DATATYPE_TXT, context);
    strTab[i][len] = '\0';
    d2 ^= cdiCheckSum(CDI_DATATYPE_TXT, len, strTab[i]);
    }
serializeUnpack(buf, buf_size, position,
                &d, 1, CDI_DATATYPE_UINT32, context);
xassert(d == d2);
}


int serializeGetSizeInCore(int count, int datatype, void *context);
void serializePackInCore(const void *data, int count, int datatype,
                        void *buf, int buf_size, int *position, void *context);
void serializeUnpackInCore(const void *buf, int buf_size, int *position,
                        void *data, int count, int datatype, void *context);

#endif

#include <inttypes.h>
#include <sys/types.h>
#include <stdlib.h>


uint32_t cdiCheckSum(int type, int count, const void *buffer)
{
uint32_t s = 0U;
xassert(count >= 0);
size_t elemSize = (size_t)serializeGetSizeInCore(1, type, NULL);
memcrc_r_eswap(&s, (const unsigned char *)buffer, (size_t)count, elemSize);
s = memcrc_finish(&s, (off_t)(elemSize * (size_t)count));
return s;
}


#include <stdio.h>
#include <string.h>
#include <errno.h>

const char *cdiStringError(int cdiErrno)
{
static const char UnknownError[] = "Unknown Error";
static const char _ETMOF[]       = "Too many open files";
static const char _EISDIR[]      = "Is a directory";
static const char _EISEMPTY[]    = "File is empty";
static const char _EUFTYPE[]     = "Unsupported file type";
static const char _ELIBNAVAIL[]  = "Unsupported file type (library support not compiled in)";
static const char _EUFSTRUCT[]   = "Unsupported file structure";
static const char _EUNC4[]       = "Unsupported NetCDF4 structure";
static const char _EDIMSIZE[]    = "Invalid dimension size";
static const char _ELIMIT[]      = "Internal limits exceeded";

switch (cdiErrno) {
case CDI_ESYSTEM:
    {
    const char *cp = strerror(errno);
    if ( cp == NULL ) break;
    return cp;
    }
case CDI_ETMOF:      return _ETMOF;
case CDI_EISDIR:     return _EISDIR;
case CDI_EISEMPTY:   return _EISEMPTY;
case CDI_EUFTYPE:    return _EUFTYPE;
case CDI_ELIBNAVAIL: return _ELIBNAVAIL;
case CDI_EUFSTRUCT:  return _EUFSTRUCT;
case CDI_EUNC4:      return _EUNC4;
case CDI_EDIMSIZE:   return _EDIMSIZE;
case CDI_ELIMIT:     return _ELIMIT;
}

return UnknownError;
}


#ifndef  DTYPES_H
#define  DTYPES_H

#include <stdio.h>
#include <limits.h>



#ifndef  INT_MAX
#error INT_MAX undefined
#endif

#undef  INT32
#if  INT_MAX == 2147483647L
#  define  INT32  int
#elif LONG_MAX == 2147483647L
#  define  INT32  long
#endif



#ifndef  LONG_MAX
#  error LONG_MAX undefined
#endif

#undef  INT64
#if  LONG_MAX > 2147483647L
#  define  INT64  long
#else
#  define  INT64  long long
#endif



#undef   FLT32
#define  FLT32  float



#undef   FLT64
#define  FLT64  double



#define  UINT32   unsigned INT32
#define  UINT64   unsigned INT64

#endif

#ifndef BINARY_H
#define BINARY_H

#ifdef HAVE_CONFIG_H
#endif

#include <inttypes.h>


#ifndef HOST_ENDIANNESS
#ifdef __cplusplus
static const uint32_t HOST_ENDIANNESS_temp[1] = { UINT32_C(0x00030201) };
#define HOST_ENDIANNESS (((const unsigned char *)HOST_ENDIANNESS_temp)[0])
#else
#define HOST_ENDIANNESS (((const unsigned char *)&(const uint32_t[1]){UINT32_C(0x00030201)})[0])
#endif
#endif


UINT32 get_UINT32(unsigned char *x);
UINT32 get_SUINT32(unsigned char *x);
UINT64 get_UINT64(unsigned char *x);
UINT64 get_SUINT64(unsigned char *x);


size_t binReadF77Block(int fileID, int byteswap);
void   binWriteF77Block(int fileID, int byteswap, size_t blocksize);

int binReadInt32(int fileID, int byteswap, size_t size, INT32 *ptr);
int binReadInt64(int fileID, int byteswap, size_t size, INT64 *ptr);

int binWriteInt32(int fileID, int byteswap, size_t size, INT32 *ptr);
int binWriteInt64(int fileID, int byteswap, size_t size, INT64 *ptr);

int binReadFlt32(int fileID, int byteswap, size_t size, FLT32 *ptr);
int binReadFlt64(int fileID, int byteswap, size_t size, FLT64 *ptr);

int binWriteFlt32(int fileID, int byteswap, size_t size, FLT32 *ptr);
int binWriteFlt64(int fileID, int byteswap, size_t size, FLT64 *ptr);

#endif

#ifndef _FILE_H
#define _FILE_H

#include <stdio.h>
#include <sys/types.h>


#define  FILE_UNDEFID      -1

#define  FILE_TYPE_OPEN     1
#define  FILE_TYPE_FOPEN    2


#define  FILE_BUFTYPE_STD   1
#define  FILE_BUFTYPE_MMAP  2

const
char  *fileLibraryVersion(void);

void   fileDebug(int debug);

void  *filePtr(int fileID);

int    fileSetBufferType(int fileID, int type);
void   fileSetBufferSize(int fileID, long buffersize);

int    fileOpen(const char *filename, const char *mode);
int    fileOpen_serial(const char *filename, const char *mode);
int    fileClose(int fileID);
int    fileClose_serial(int fileID);

char  *fileInqName(int fileID);
int    fileInqMode(int fileID);

int    fileFlush(int fileID);
void   fileClearerr(int fileID);
int    fileEOF(int fileID);
int    filePtrEOF(void *fileptr);
void   fileRewind(int fileID);

off_t  fileGetPos(int fileID);
int    fileSetPos(int fileID, off_t offset, int whence);

int    fileGetc(int fileID);
int    filePtrGetc(void *fileptr);

size_t filePtrRead(void *fileptr, void *restrict ptr, size_t size);
size_t fileRead(int fileID, void *restrict ptr, size_t size);
size_t fileWrite(int fileID, const void *restrict ptr, size_t size);

#endif

#ifdef  HAVE_CONFIG_H
#endif

#include <ctype.h>


#ifdef  HAVE_LIBCGRIBEX
#endif

int cdiDefaultCalendar = CALENDAR_PROLEPTIC;

int cdiDefaultInstID   = CDI_UNDEFID;
int cdiDefaultModelID  = CDI_UNDEFID;
int cdiDefaultTableID  = CDI_UNDEFID;

int cdiNcChunksizehint = CDI_UNDEFID;
int cdiChunkType       = CDI_CHUNK_GRID;
int cdiSplitLtype105   = CDI_UNDEFID;

bool cdiIgnoreAttCoordinates = false;
bool cdiCoordinatesLonLat    = false;
bool cdiIgnoreValidRange     = false;
int cdiSkipRecords          = 0;
int CDI_convention           = CDI_CONVENTION_ECHAM;
int CDI_inventory_mode        = 1;
int CDO_version_info        = 1;
int CDI_read_cell_corners   = 1;
int CDI_cmor_mode           = 0;
int CDI_reduce_dim          = 0;
size_t CDI_netcdf_hdr_pad   = 0UL;
bool CDI_netcdf_lazy_grid_load = false;

char *cdiPartabPath   = NULL;
int   cdiPartabIntern = 1;

double CDI_default_missval = -9.E33;
double CDI_grid_missval = -9999.;

static const char Filetypes[][9] = {
"UNKNOWN",
"GRIB",
"GRIB2",
"NetCDF",
"NetCDF2",
"NetCDF4",
"NetCDF4c",
"NetCDF5",
"SERVICE",
"EXTRA",
"IEG",
"HDF5",
};

int CDI_Debug   = 0;
int CDI_Recopt = 0;

int cdiGribApiDebug     = 0;
int cdiDefaultLeveltype = -1;
int cdiDataUnreduced = 0;
int cdiSortName = 0;
int cdiSortParam = 0;
int cdiHaveMissval = 0;


static long cdiGetenvInt(const char *envName)
{
long envValue = -1;

char *envString = getenv(envName);
if ( envString )
    {
    long fact = 1;
    int len = (int) strlen(envString);
    for ( int loop = 0; loop < len; loop++ )
        {
        if ( ! isdigit((int) envString[loop]) )
            {
            switch ( tolower((int) envString[loop]) )
                {
                case 'k':  fact = 1024;        break;
                case 'm':  fact = 1048576;     break;
                case 'g':  fact = 1073741824;  break;
                default:
                fact = 0;
                Message("Invalid number string in %s: %s", envName, envString);
                Warning("%s must comprise only digits [0-9].",envName);
                break;
                }
            break;
            }
        }

    if ( fact ) envValue = fact*atol(envString);

    if ( CDI_Debug ) Message("set %s to %ld", envName, envValue);
    }

return envValue;
}

static void
cdiPrintDefaults(void)
{
fprintf(stderr, "default instID     :  %d\n"
        "default modelID    :  %d\n"
        "default tableID    :  %d\n"
        "default missval    :  %g\n", cdiDefaultInstID,
        cdiDefaultModelID, cdiDefaultTableID, CDI_default_missval);
}

void cdiPrintVersion(void)
{
fprintf(stderr, "     CDI library version : %s\n", cdiLibraryVersion());
#ifdef  HAVE_LIBCGRIBEX
fprintf(stderr, " CGRIBEX library version : %s\n", cgribexLibraryVersion());
#endif
#ifdef  HAVE_LIBGRIB_API
fprintf(stderr, "GRIB_API library version : %s\n", gribapiLibraryVersionString());
#endif
#ifdef  HAVE_LIBNETCDF
fprintf(stderr, "  NetCDF library version : %s\n", cdfLibraryVersion());
#endif
#ifdef  HAVE_NC4HDF5
fprintf(stderr, "    HDF5 library version : %s\n", hdfLibraryVersion());
#endif
#ifdef  HAVE_LIBSERVICE
fprintf(stderr, "    EXSE library version : %s\n", srvLibraryVersion());
#endif

fprintf(stderr, "    FILE library version : %s\n", fileLibraryVersion());
}

static void cdiPrintDatatypes(void)
{
#define XSTRING(x)	#x
#define STRING(x)	XSTRING(x)
fprintf (stderr, "+-------------+-------+\n"
        "| types       | bytes |\n"
        "+-------------+-------+\n"
        "| void *      |   %3d |\n"
        "+-------------+-------+\n"
        "| char        |   %3d |\n"
        "+-------------+-------+\n"
        "| bool        |   %3d |\n"
        "| short       |   %3d |\n"
        "| int         |   %3d |\n"
        "| long        |   %3d |\n"
        "| long long   |   %3d |\n"
        "| size_t      |   %3d |\n"
        "| off_t       |   %3d |\n"
        "+-------------+-------+\n"
        "| float       |   %3d |\n"
        "| double      |   %3d |\n"
        "| long double |   %3d |\n"
        "+-------------+-------+\n\n"
        "+-------------+-----------+\n"
        "| INT32       | %-9s |\n"
        "| INT64       | %-9s |\n"
        "| FLT32       | %-9s |\n"
        "| FLT64       | %-9s |\n"
        "+-------------+-----------+\n"
        "\n  byte ordering is %s\n\n",
        (int) sizeof(void *), (int) sizeof(char), (int) sizeof(bool),
        (int) sizeof(short), (int) sizeof(int), (int) sizeof(long), (int) sizeof(long long),
        (int) sizeof(size_t), (int) sizeof(off_t),
        (int) sizeof(float), (int) sizeof(double), (int) sizeof(long double),
        STRING(INT32), STRING(INT64), STRING(FLT32), STRING(FLT64),
        ((HOST_ENDIANNESS == CDI_BIGENDIAN) ? "BIGENDIAN"
            : ((HOST_ENDIANNESS == CDI_LITTLEENDIAN) ? "LITTLEENDIAN"
            : "Unhandled endianness!")));
#undef STRING
#undef XSTRING
}


void cdiDebug(int level)
{
if ( level == 1 || (level &  2) ) CDI_Debug = 1;

if ( CDI_Debug ) Message("debug level %d", level);

if ( level == 1 || (level &  4) ) memDebug(1);

if ( level == 1 || (level &  8) ) fileDebug(1);

if ( level == 1 || (level & 16) )
    {
#ifdef HAVE_LIBCGRIBEX
    gribSetDebug(1);
#endif
#ifdef HAVE_LIBNETCDF
    cdfDebug(1);
#endif
#ifdef HAVE_LIBSERVICE
    srvDebug(1);
#endif
#ifdef HAVE_LIBEXTRA
    extDebug(1);
#endif
#ifdef HAVE_LIBIEG
    iegDebug(1);
#endif
    }

if ( CDI_Debug )
    {
    cdiPrintDefaults();
    cdiPrintDatatypes();
    }
}


int cdiHaveFiletype(int filetype)
{
int status = 0;

switch (filetype)
    {
#ifdef  HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:  status = 1; break;
#endif
#ifdef  HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:  status = 1; break;
#endif
#ifdef  HAVE_LIBIEG
    case CDI_FILETYPE_IEG:  status = 1; break;
#endif
#ifdef  HAVE_LIBGRIB
#if  defined  HAVE_LIBGRIB_API || defined  HAVE_LIBCGRIBEX
    case CDI_FILETYPE_GRB:  status = 1; break;
#endif
#ifdef  HAVE_LIBGRIB_API
    case CDI_FILETYPE_GRB2: status = 1; break;
#endif
#endif
#ifdef  HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:   status = 1; break;
#ifdef  HAVE_NETCDF2
    case CDI_FILETYPE_NC2:  status = 1; break;
#endif
#ifdef  HAVE_NETCDF4
    case CDI_FILETYPE_NC4:  status = 1; break;
    case CDI_FILETYPE_NC4C: status = 1; break;
#endif
#ifdef  HAVE_NETCDF5
    case CDI_FILETYPE_NC5:  status = 1; break;
#endif
#endif
    default: status = 0; break;
    }

return status;
}

void cdiDefTableID(int tableID)
{
cdiDefaultTableID = tableID;
int modelID = cdiDefaultModelID = tableInqModel(tableID);
cdiDefaultInstID = modelInqInstitut(modelID);
}

static
void cdiSetChunk(const char *chunkAlgo)
{


int algo = -1;

if      ( strcmp("auto",  chunkAlgo)   == 0 ) algo = CDI_CHUNK_AUTO;
else if ( strcmp("grid",  chunkAlgo)   == 0 ) algo = CDI_CHUNK_GRID;
else if ( strcmp("lines", chunkAlgo)   == 0 ) algo = CDI_CHUNK_LINES;

else
    Warning("Invalid environment variable CDI_CHUNK_ALGO: %s", chunkAlgo);

if ( algo != -1 )
    {
    cdiChunkType = algo;
    if ( CDI_Debug ) Message("set ChunkAlgo to %s", chunkAlgo);
    }
}


void cdiInitialize(void)
{
static bool Init_CDI = false;

if ( ! Init_CDI )
    {
    Init_CDI = true;
    char *envstr;
    long value;

#ifdef  HAVE_LIBCGRIBEX
    gribFixZSE(1);
    gribSetConst(1);
#endif

    value = cdiGetenvInt("CDI_DEBUG");
    if ( value >= 0 ) CDI_Debug = (int) value;

    value = cdiGetenvInt("CDI_GRIBAPI_DEBUG");
    if ( value >= 0 ) cdiGribApiDebug = (int) value;

    value = cdiGetenvInt("CDI_READ_CELL_CORNERS");
    if ( value >= 0 ) CDI_read_cell_corners = (int) value;

    value = cdiGetenvInt("CDI_RECOPT");
    if ( value >= 0 ) CDI_Recopt = (int) value;

    value = cdiGetenvInt("CDI_REGULARGRID");
    if ( value >= 0 ) cdiDataUnreduced = (int) value;

    value = cdiGetenvInt("CDI_SORTNAME");
    if ( value >= 0 ) cdiSortName = (int) value;

    value = cdiGetenvInt("CDI_SORTPARAM");
    if ( value >= 0 ) cdiSortParam = (int) value;

    value = cdiGetenvInt("CDI_HAVE_MISSVAL");
    if ( value >= 0 ) cdiHaveMissval = (int) value;

    value = cdiGetenvInt("CDI_LEVELTYPE");
    if ( value >= 0 ) cdiDefaultLeveltype = (int) value;

    value = cdiGetenvInt("CDI_NETCDF_HDR_PAD");
    if ( value >= 0 ) CDI_netcdf_hdr_pad = (size_t) value;

    envstr = getenv("CDI_MISSVAL");
    if ( envstr ) CDI_default_missval = atof(envstr);

    envstr = getenv("NC_CHUNKSIZEHINT");
    if ( envstr ) cdiNcChunksizehint = atoi(envstr);

    envstr = getenv("CDI_CHUNK_ALGO");
    if ( envstr ) cdiSetChunk(envstr);

    envstr = getenv("SPLIT_LTYPE_105");
    if ( envstr ) cdiSplitLtype105 = atoi(envstr);

    envstr = getenv("IGNORE_ATT_COORDINATES");
    if ( envstr ) cdiIgnoreAttCoordinates = atoi(envstr) > 0;

    envstr = getenv("CDI_COORDINATES_LONLAT");
    if ( envstr ) cdiCoordinatesLonLat = atoi(envstr) > 0;

    envstr = getenv("IGNORE_VALID_RANGE");
    if ( envstr ) cdiIgnoreValidRange = atoi(envstr) > 0;

    envstr = getenv("CDI_SKIP_RECORDS");
    if ( envstr )
        {
        cdiSkipRecords = atoi(envstr);
        cdiSkipRecords = cdiSkipRecords > 0 ? cdiSkipRecords : 0;
        }

    envstr = getenv("CDI_CONVENTION");
    if ( envstr )
        {
        if ( strcmp(envstr, "CF") == 0 || strcmp(envstr, "cf") == 0 )
            {
            CDI_convention = CDI_CONVENTION_CF;
            if ( CDI_Debug )
                Message("CDI convention was set to CF!");
            }
        }

    envstr = getenv("CDI_INVENTORY_MODE");
    if ( envstr )
        {
        if ( strncmp(envstr, "time", 4) == 0 )
            {
            CDI_inventory_mode = 2;
            if ( CDI_Debug )
                Message("Inventory mode was set to timestep!");
            }
        }

    envstr = getenv("CDI_VERSION_INFO");
    if ( envstr )
        {
        int ival = atoi(envstr);
        if ( ival == 0 || ival == 1 )
            {
            CDO_version_info = ival;
            if ( CDI_Debug )
                Message("CDO_version_info = %s", envstr);
            }
        }


    envstr = getenv("CDI_CALENDAR");
    if ( envstr )
        {
        if      ( strncmp(envstr, "standard", 8) == 0 )
            cdiDefaultCalendar = CALENDAR_STANDARD;
        else if ( strncmp(envstr, "gregorian", 9) == 0 )
            cdiDefaultCalendar = CALENDAR_GREGORIAN;
        else if ( strncmp(envstr, "proleptic", 9) == 0 )
            cdiDefaultCalendar = CALENDAR_PROLEPTIC;
        else if ( strncmp(envstr, "360days", 7) == 0 )
            cdiDefaultCalendar = CALENDAR_360DAYS;
        else if ( strncmp(envstr, "365days", 7) == 0 )
            cdiDefaultCalendar = CALENDAR_365DAYS;
        else if ( strncmp(envstr, "366days", 7) == 0 )
            cdiDefaultCalendar = CALENDAR_366DAYS;
        else if ( strncmp(envstr, "none", 4) == 0 )
            cdiDefaultCalendar = CALENDAR_NONE;

        if ( CDI_Debug )
            Message("Default calendar set to %s!", envstr);
        }
#ifdef  HAVE_LIBCGRIBEX
    gribSetCalendar(cdiDefaultCalendar);
#endif

    envstr = getenv("PARTAB_INTERN");
    if ( envstr ) cdiPartabIntern = atoi(envstr);

    envstr = getenv("PARTAB_PATH");
    if ( envstr ) cdiPartabPath = strdup(envstr);
    }
}


const char *strfiletype(int filetype)
{
int size = (int) (sizeof(Filetypes)/sizeof(char *));
const char *name = (filetype > 0 && filetype < size) ? Filetypes[filetype] : Filetypes[0];

return name;
}


void cdiDefGlobal(const char *string, int val)
{
if      ( strcmp(string, "REGULARGRID")      == 0 ) cdiDataUnreduced = val;
else if ( strcmp(string, "GRIBAPI_DEBUG")    == 0 ) cdiGribApiDebug = val;
else if ( strcmp(string, "SORTNAME")         == 0 ) cdiSortName = val;
else if ( strcmp(string, "SORTPARAM")        == 0 ) cdiSortParam = val;
else if ( strcmp(string, "HAVE_MISSVAL")     == 0 ) cdiHaveMissval = val;
else if ( strcmp(string, "NC_CHUNKSIZEHINT") == 0 ) cdiNcChunksizehint = val;
else if ( strcmp(string, "READ_CELL_CORNERS")== 0 ) CDI_read_cell_corners = val;
else if ( strcmp(string, "CMOR_MODE")        == 0 ) CDI_cmor_mode = val;
else if ( strcmp(string, "REDUCE_DIM")       == 0 ) CDI_reduce_dim = val;
else if ( strcmp(string, "NETCDF_HDR_PAD")   == 0 ) CDI_netcdf_hdr_pad = (size_t) val;
else if ( strcmp(string, "NETCDF_LAZY_GRID_LOAD") == 0)
    CDI_netcdf_lazy_grid_load = (bool)val;
else Warning("Unsupported global key: %s", string);
}


void cdiDefMissval(double missval)
{
cdiInitialize();

CDI_default_missval = missval;
}


double cdiInqMissval(void)
{
cdiInitialize();

return CDI_default_missval;
}


double cdiInqGridMissval(void)
{
cdiInitialize();

return CDI_default_missval;
}



#ifdef HAVE_CONFIG_H
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

void cdiDecodeParam(int param, int *pnum, int *pcat, int *pdis)
{
unsigned uparam = (unsigned)param;
unsigned upnum;

*pdis = 0xff   & uparam;
*pcat = 0xff   & uparam >> 8;
upnum = 0xffff & uparam >> 16;
if ( upnum > 0x7fffU ) upnum = 0x8000U - upnum;
*pnum = (int)upnum;
}


int cdiEncodeParam(int pnum, int pcat, int pdis)
{
if ( pcat < 0 || pcat > 255 ) pcat = 255;
if ( pdis < 0 || pdis > 255 ) pdis = 255;

unsigned upnum = (unsigned)pnum;
if ( pnum < 0 ) upnum = (unsigned)(0x8000 - pnum);

unsigned uparam = upnum << 16 | (unsigned)(pcat << 8) | (unsigned)pdis;

return (int)uparam;
}


void cdiDecodeDate(int64_t date, int *year, int *month, int *day)
{
int64_t iyear = (int)(date / 10000);
*year = (int)iyear;
int64_t idate = date - iyear * 10000;
if ( idate < 0 ) idate = -idate;
int64_t imonth = idate / 100;
*month = (int)imonth;
*day   = (int)(idate - imonth * 100);
}


int64_t cdiEncodeDate(int year, int month, int day)
{
int64_t iyear = abs(year);
int64_t date = iyear * 10000 + month * 100 + day;
if ( year < 0 ) date = -date;

return date;
}


void cdiDecodeTime(int time, int *hour, int *minute, int *second)
{
int ihour = time / 10000,
    itime = time - ihour * 10000,
    iminute = itime / 100;
*hour   = ihour;
*minute = iminute;
*second = itime - iminute * 100;
}


int cdiEncodeTime(int hour, int minute, int second)
{
return hour*10000 + minute*100 + second;
}


void cdiParamToString(int param, char *paramstr, int maxlen)
{
int dis, cat, num;
cdiDecodeParam(param, &num, &cat, &dis);

size_t umaxlen = maxlen >= 0 ? (unsigned)maxlen : 0U;
int len;
/*
if ( dis == 255 && (cat == 255 || cat == 0 ) )
    len = snprintf(paramstr, umaxlen, "%d", num);
else  if ( dis == 255 )
    len = snprintf(paramstr, umaxlen, "%d.%d", num, cat);
else
    len = snprintf(paramstr, umaxlen, "%d.%d.%d", num, cat, dis);*/

if ( len >= maxlen || len < 0)
    fprintf(stderr, "Internal problem (%s): size of input string is too small!\n", __func__);
}


const char *cdiUnitNamePtr(int cdi_unit)
{
const char *cdiUnits[] = {
    "undefined",
    "Pa",
    "hPa",
    "mm",
    "cm",
    "dm",
    "m",
};
enum { numUnits = (int) (sizeof(cdiUnits)/sizeof(char *)) };
const char *name = ( cdi_unit > 0 && cdi_unit < numUnits ) ?
    cdiUnits[cdi_unit] : NULL;

return name;
}

size_t
cdiGetPageSize(bool largePageAlign)
{
long pagesize = -1L;
#if HAVE_DECL__SC_LARGE_PAGESIZE || HAVE_DECL__SC_PAGE_SIZE || HAVE_DECL__SC_PAGESIZE
bool nameAssigned = false;
int name;
#  if HAVE_DECL__SC_LARGE_PAGESIZE
if (largePageAlign)
    {
    name = _SC_LARGE_PAGESIZE;
    nameAssigned = true;
    }
else
#  else
    (void)largePageAlign;
#  endif
    {
#  if HAVE_DECL__SC_PAGESIZE || HAVE_DECL__SC_PAGE_SIZE
    name =
#    if HAVE_DECL__SC_PAGESIZE
        _SC_PAGESIZE
#    elif HAVE_DECL__SC_PAGE_SIZE
        _SC_PAGE_SIZE
#    endif
        ;
    nameAssigned = true;
#  endif
    }
if (nameAssigned)
    pagesize = sysconf(name);
#endif
if (pagesize == -1L)
    pagesize =
#if HAVE_DECL_PAGESIZE
    PAGESIZE
#elif HAVE_DECL_PAGE_SIZE
    PAGE_SIZE
#else
    commonPageSize
#endif
    ;
return (size_t)pagesize;
}



#ifdef HAVE_CONFIG_H
#endif

#include <inttypes.h>
#include <stdlib.h>
#include <sys/types.h>
#ifdef WORDS_BIGENDIAN
#include <limits.h>
#endif


static const uint32_t crctab[] = {
0x00000000,
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};


uint32_t
memcrc(const unsigned char *b, size_t n)
{



uint32_t s = 0;

memcrc_r(&s, b, n);


while (n != 0) {
    register uint32_t c = n & 0377;
    n >>= 8;
    s = (s << 8) ^ crctab[(s >> 24) ^ c];
}


return ~s;
}

void
memcrc_r(uint32_t *state, const unsigned char *block, size_t block_len)
{



register uint32_t c, s = *state;
register size_t n = block_len;
register const unsigned char *b = block;

for (; n > 0; --n) {
    c = (uint32_t)(*b++);
    s = (s << 8) ^ crctab[(s >> 24) ^ c];
}

*state = s;
}

#ifdef WORDS_BIGENDIAN
#define SWAP_CSUM(BITWIDTH,BYTEWIDTH,NACC)                              \
do {                                                                  \
    register const uint##BITWIDTH##_t *b = (uint##BITWIDTH##_t *)elems; \
    for (size_t i = 0; i < num_elems; ++i) {                            \
    for(size_t aofs = NACC; aofs > 0; --aofs) {                       \
        uint##BITWIDTH##_t accum = b[i + aofs - 1];                     \
        for (size_t j = 0; j < BYTEWIDTH; ++j) {                        \
        uint32_t c = (uint32_t)(accum & UCHAR_MAX);                   \
        s = (s << 8) ^ crctab[(s >> 24) ^ c];                         \
        accum >>= 8;                                                  \
        }                                                               \
    }                                                                 \
    }                                                                   \
} while (0)
#endif



void
memcrc_r_eswap(uint32_t *state, const unsigned char *elems, size_t num_elems,
            size_t elem_size)
{
#ifdef WORDS_BIGENDIAN
register uint32_t s = *state;

switch (elem_size)
{
case 1:
    memcrc_r(state, elems, num_elems * elem_size);
    return;
case 2:
    SWAP_CSUM(16,2,1);
    break;
case 4:
    SWAP_CSUM(32,4,1);
    break;
case 8:
    SWAP_CSUM(64,8,1);
    break;
case 16:
    SWAP_CSUM(64,8,2);
    break;
}
*state = s;
#else
memcrc_r(state, elems, num_elems * elem_size);
#endif
}


uint32_t
memcrc_finish(uint32_t *state, off_t total_size)
{
register uint32_t c, s = *state;
register uint64_t n = (uint64_t)total_size;


while (n != 0) {
    c = n & 0377;
    n >>= 8;
    s = (s << 8) ^ crctab[(s >> 24) ^ c];
}

return ~s;
}


#ifdef HAVE_CONFIG_H
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <errno.h>

#if !defined(HAVE_CONFIG_H) && !defined(HAVE_MALLOC_H) && defined(SX)
#  define  HAVE_MALLOC_H
#endif

#if  defined(HAVE_MALLOC_H)
#  include <malloc.h>
#endif


enum             {MALLOC_FUNC=0, CALLOC_FUNC, REALLOC_FUNC, FREE_FUNC};
static const char *memfunc[] = {"Malloc", "Calloc", "Realloc", "Free"};

#undef   MEM_UNDEFID
#define  MEM_UNDEFID  -1

#define  MEM_MAXNAME  32

static int dmemory_ExitOnError = 1;

typedef struct
{
void     *ptr;
size_t    size;
size_t    nobj;
int       item;
int       mtype;
int       line;
char      filename[MEM_MAXNAME];
char      functionname[MEM_MAXNAME];
}
MemTable_t;

static MemTable_t *memTable;
static size_t  memTableSize  = 0;
static long    memAccess     = 0;

static size_t  MemObjs       = 0;
static size_t  MaxMemObjs    = 0;
static size_t  MemUsed       = 0;
static size_t  MaxMemUsed    = 0;

static int     MEM_Debug     = 0;
static int     MEM_Info      = 0;

static
const char *get_filename(const char *file)
{
const char *fnptr = strrchr(file, '/');
if ( fnptr ) fnptr++;
else         fnptr = (char *) file;

return fnptr;
}


void memDebug(int debug)
{
MEM_Debug = debug;
}


#if ! defined __GNUC__ && ! defined __attribute__
#  define  __attribute__(x)
#endif

static
void memInternalProblem(const char *caller, const char *fmt, ...)
__attribute__((noreturn));
static
void memError(const char *caller, const char *file, int line, size_t size)
__attribute__((noreturn));

static
void memInternalProblem(const char *functionname, const char *fmt, ...)
{
va_list args;

va_start(args, fmt);

printf("\n");
fprintf(stderr, "Internal problem (%s) : ", functionname);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");

va_end(args);

exit(EXIT_FAILURE);
}

static
void memError(const char *functionname, const char *file, int line, size_t size)
{
fputs("\n", stdout);
fprintf(stderr, "Error (%s) : Allocation of %zu bytes failed. [ line %d file %s ]\n",
        functionname, size, line, get_filename(file));

if ( errno ) perror("System error message ");

exit(EXIT_FAILURE);
}

static
void memListPrintEntry(int mtype, int item, size_t size, void *ptr,
                    const char *functionname, const char *file, int line)
{
fprintf(stderr, "[%-7s ", memfunc[mtype]);

fprintf(stderr, "memory item %3d ", item);
fprintf(stderr, "(%6zu byte) ", size);
fprintf(stderr, "at %p", ptr);
if ( file != NULL )
    {
    fprintf(stderr, " line %4d", line);
    fprintf(stderr, " file %s", get_filename(file));
    }
if ( functionname != NULL )
    fprintf(stderr, " (%s)", functionname);
fprintf(stderr, "]\n");
}

static
void memListPrintTable(void)
{
if ( MemObjs ) fprintf(stderr, "\nMemory table:\n");

for ( size_t memID = 0; memID < memTableSize; memID++ )
    {
    if ( memTable[memID].item != MEM_UNDEFID )
        memListPrintEntry(memTable[memID].mtype, memTable[memID].item,
                        memTable[memID].size*memTable[memID].nobj,
                        memTable[memID].ptr, memTable[memID].functionname,
                        memTable[memID].filename, memTable[memID].line);
    }

if ( MemObjs )
    {
    fprintf(stderr, "  Memory access             : %6u\n", (unsigned) memAccess);
    fprintf(stderr, "  Maximum objects           : %6zu\n", memTableSize);
    fprintf(stderr, "  Objects used              : %6u\n", (unsigned) MaxMemObjs);
    fprintf(stderr, "  Objects in use            : %6u\n", (unsigned) MemObjs);
    fprintf(stderr, "  Memory allocated          : ");
    if (MemUsed > 1024*1024*1024)
        fprintf(stderr, " %5d GB\n",   (int) (MemUsed/(1024*1024*1024)));
    else if (MemUsed > 1024*1024)
        fprintf(stderr, " %5d MB\n",   (int) (MemUsed/(1024*1024)));
    else if (MemUsed > 1024)
        fprintf(stderr, " %5d KB\n",   (int) (MemUsed/(1024)));
    else
        fprintf(stderr, " %5d Byte\n", (int)  MemUsed);
    }

if ( MaxMemUsed )
    {
    fprintf(stderr, "  Maximum memory allocated  : ");
    if (MaxMemUsed > 1024*1024*1024)
        fprintf(stderr, " %5d GB\n",   (int) (MaxMemUsed/(1024*1024*1024)));
    else if (MaxMemUsed > 1024*1024)
        fprintf(stderr, " %5d MB\n",   (int) (MaxMemUsed/(1024*1024)));
    else if (MaxMemUsed > 1024)
        fprintf(stderr, " %5d KB\n",   (int) (MaxMemUsed/(1024)));
    else
        fprintf(stderr, " %5d Byte\n", (int)  MaxMemUsed);
    }
}

static
void memGetDebugLevel(void)
{
const char *envstr;

envstr = getenv("MEMORY_INFO");
if ( envstr && isdigit((int) envstr[0]) ) MEM_Info = atoi(envstr);

envstr = getenv("MEMORY_DEBUG");
if ( envstr && isdigit((int) envstr[0]) ) MEM_Debug = atoi(envstr);

if ( MEM_Debug && !MEM_Info ) MEM_Info = 1;

if ( MEM_Info ) atexit(memListPrintTable);
}

static
void memInit(void)
{
static int initDebugLevel = 0;

if ( ! initDebugLevel )
    {
    memGetDebugLevel();
    initDebugLevel = 1;
    }
}

static
int memListDeleteEntry(void *ptr, size_t *size)
{
int item = MEM_UNDEFID;
size_t memID;

for ( memID = 0; memID < memTableSize; memID++ )
    {
    if ( memTable[memID].item == MEM_UNDEFID ) continue;
    if ( memTable[memID].ptr == ptr ) break;
    }

if ( memID != memTableSize )
    {
    MemObjs--;
    MemUsed -= memTable[memID].size * memTable[memID].nobj;
    *size = memTable[memID].size * memTable[memID].nobj;
    item = memTable[memID].item;
    memTable[memID].item = MEM_UNDEFID;
    }

return item;
}

static
void memTableInitEntry(size_t memID)
{
if ( memID >= memTableSize )
    memInternalProblem(__func__, "memID %d undefined!", memID);

memTable[memID].ptr    = NULL;
memTable[memID].item   = MEM_UNDEFID;
memTable[memID].size   = 0;
memTable[memID].nobj   = 0;
memTable[memID].mtype  = MEM_UNDEFID;
memTable[memID].line   = MEM_UNDEFID;
}

static
int memListNewEntry(int mtype, void *ptr, size_t size, size_t nobj,
                    const char *functionname, const char *file, int line)
{
static int item = 0;
size_t memSize = 0;
size_t memID = 0;


if ( memTableSize == 0 )
    {
    memTableSize = 8;
    memSize  = memTableSize * sizeof(MemTable_t);
    memTable = (MemTable_t *) malloc(memSize);
    if( memTable == NULL ) memError(__func__, __FILE__, __LINE__, memSize);

    for ( size_t i = 0; i < memTableSize; i++ )
        memTableInitEntry(i);
    }
else
    {
    while ( memID < memTableSize )
        {
        if ( memTable[memID].item == MEM_UNDEFID ) break;
        memID++;
        }
    }

if ( memID == memTableSize )
    {
    memTableSize = 2*memTableSize;
    memSize  = memTableSize*sizeof(MemTable_t);
    memTable = (MemTable_t*) realloc(memTable, memSize);
    if ( memTable == NULL ) memError(__func__, __FILE__, __LINE__, memSize);

    for ( size_t i = memID; i < memTableSize; i++ )
        memTableInitEntry(i);
    }

memTable[memID].item  = item;
memTable[memID].ptr   = ptr;
memTable[memID].size  = size;
memTable[memID].nobj  = nobj;
memTable[memID].mtype = mtype;
memTable[memID].line  = line;

if ( file )
    {
    const char *filename = get_filename(file);
    size_t len = strlen(filename);
    if ( len > MEM_MAXNAME-1 ) len = MEM_MAXNAME-1;

    (void) memcpy(memTable[memID].filename, filename, len);
    memTable[memID].filename[len] = '\0';
    }
else
    {
    (void) strcpy(memTable[memID].filename, "unknown");
    }

if ( functionname )
    {
    size_t len = strlen(functionname);
    if ( len > MEM_MAXNAME-1 ) len = MEM_MAXNAME-1;

    (void) memcpy(memTable[memID].functionname, functionname, len);
    memTable[memID].functionname[len] = '\0';
    }
else
    {
    (void) strcpy(memTable[memID].functionname, "unknown");
    }

MaxMemObjs++;
MemObjs++;
MemUsed += size*nobj;
if ( MemUsed > MaxMemUsed ) MaxMemUsed = MemUsed;

return item++;
}

static
int memListChangeEntry(void *ptrold, void *ptr, size_t size,
                    const char *functionname, const char *file, int line)
{
int item = MEM_UNDEFID;
size_t memID = 0;

while( memID < memTableSize )
    {
    if ( memTable[memID].item != MEM_UNDEFID )
        if ( memTable[memID].ptr == ptrold ) break;
    memID++;
    }

if ( memID == memTableSize )
    {
    if ( ptrold != NULL )
        memInternalProblem(__func__, "Item at %p not found.", ptrold);
    }
else
    {
    item = memTable[memID].item;

    size_t sizeold = memTable[memID].size*memTable[memID].nobj;

    memTable[memID].ptr   = ptr;
    memTable[memID].size  = size;
    memTable[memID].nobj  = 1;
    memTable[memID].mtype = REALLOC_FUNC;
    memTable[memID].line  = line;

    if ( file )
        {
        const char *filename = get_filename(file);
        size_t len = strlen(filename);
        if ( len > MEM_MAXNAME-1 ) len = MEM_MAXNAME-1;

        (void) memcpy(memTable[memID].filename, filename, len);
        memTable[memID].filename[len] = '\0';
        }
    else
        {
        (void) strcpy(memTable[memID].filename, "unknown");
        }

    if ( functionname )
        {
        size_t len = strlen(functionname);
        if ( len > MEM_MAXNAME-1 ) len = MEM_MAXNAME-1;

        (void) memcpy(memTable[memID].functionname, functionname, len);
        memTable[memID].functionname[len] = '\0';
        }
    else
        {
        (void) strcpy(memTable[memID].functionname, "unknown");
        }

    MemUsed -= sizeold;
    MemUsed += size;
    if ( MemUsed > MaxMemUsed ) MaxMemUsed = MemUsed;
    }

return item;
}


void *memCalloc(size_t nobjs, size_t size, const char *file, const char *functionname, int line)
{
void *ptr = NULL;

memInit();

if ( nobjs*size > 0 )
    {
    ptr = calloc(nobjs, size);

    if ( MEM_Info )
        {
        memAccess++;

        int item = MEM_UNDEFID;
        if ( ptr ) item = memListNewEntry(CALLOC_FUNC, ptr, size, nobjs, functionname, file, line);

        if ( MEM_Debug ) memListPrintEntry(CALLOC_FUNC, item, size*nobjs, ptr, functionname, file, line);
        }

    if ( ptr == NULL && dmemory_ExitOnError )
        memError(functionname, file, line, size*nobjs);
    }
else
    fprintf(stderr, "Warning (%s) : Allocation of 0 bytes! [ line %d file %s ]\n", functionname, line, file);

return ptr;
}


void *memMalloc(size_t size, const char *file, const char *functionname, int line)
{
void *ptr = NULL;

memInit();

if ( size > 0 )
    {
    ptr = malloc(size);

    if ( MEM_Info )
        {
        memAccess++;

        int item = MEM_UNDEFID;
        if ( ptr ) item = memListNewEntry(MALLOC_FUNC, ptr, size, 1, functionname, file, line);

        if ( MEM_Debug ) memListPrintEntry(MALLOC_FUNC, item, size, ptr, functionname, file, line);
        }

    if ( ptr == NULL && dmemory_ExitOnError )
        memError(functionname, file, line, size);
    }
else
    fprintf(stderr, "Warning (%s) : Allocation of 0 bytes! [ line %d file %s ]\n", functionname, line, file);

return ptr;
}


void *memRealloc(void *ptrold, size_t size, const char *file, const char *functionname, int line)
{
void *ptr = NULL;

memInit();

if ( size > 0 )
    {
    ptr = realloc(ptrold, size);

    if ( MEM_Info )
        {
        memAccess++;

        int item = MEM_UNDEFID;
        if ( ptr )
            {
            item = memListChangeEntry(ptrold, ptr, size, functionname, file, line);

            if ( item == MEM_UNDEFID ) item = memListNewEntry(REALLOC_FUNC, ptr, size, 1, functionname, file, line);
            }

        if ( MEM_Debug ) memListPrintEntry(REALLOC_FUNC, item, size, ptr, functionname, file, line);
        }

    if ( ptr == NULL && dmemory_ExitOnError )
        memError(functionname, file, line, size);
    }
else
    fprintf(stderr, "Warning (%s) : Allocation of 0 bytes! [ line %d file %s ]\n", functionname, line, get_filename(file));

return ptr;
}


void memFree(void *ptr, const char *file, const char *functionname, int line)
{
memInit();

if ( MEM_Info )
    {
    int item;
    size_t size;

    if ( (item = memListDeleteEntry(ptr, &size)) >= 0 )
        {
        if ( MEM_Debug ) memListPrintEntry(FREE_FUNC, item, size, ptr, functionname, file, line);
        }
    else
        {
        if ( ptr && MEM_Debug  )
            fprintf(stderr, "%s info: memory entry at %p not found. [line %4d file %s (%s)]\n",
                    __func__, ptr, line, get_filename(file), functionname);
        }
    }

free(ptr);
}


size_t memTotal(void)
{
size_t memtotal = 0;
#if  defined  (HAVE_MALLINFO)
struct mallinfo meminfo = mallinfo();
if ( MEM_Debug )
    {
    fprintf(stderr, "arena      %8zu (non-mmapped space allocated from system)\n", (size_t)meminfo.arena);
    fprintf(stderr, "ordblks    %8zu (number of free chunks)\n", (size_t)meminfo.ordblks);
    fprintf(stderr, "smblks     %8zu (number of fastbin blocks)\n", (size_t) meminfo.smblks);
    fprintf(stderr, "hblks      %8zu (number of mmapped regions)\n", (size_t) meminfo.hblks);
    fprintf(stderr, "hblkhd     %8zu (space in mmapped regions)\n", (size_t) meminfo.hblkhd);
    fprintf(stderr, "usmblks    %8zu (maximum total allocated space)\n", (size_t) meminfo.usmblks);
    fprintf(stderr, "fsmblks    %8zu (maximum total allocated space)\n", (size_t) meminfo.fsmblks);
    fprintf(stderr, "uordblks   %8zu (total allocated space)\n", (size_t) meminfo.uordblks);
    fprintf(stderr, "fordblks   %8zu (total free space)\n", (size_t) meminfo.fordblks);
    fprintf(stderr, "Memory in use:   %8zu bytes\n", (size_t) meminfo.usmblks + (size_t)meminfo.uordblks);
    fprintf(stderr, "Total heap size: %8zu bytes\n", (size_t) meminfo.arena);


    }
memtotal = (size_t)meminfo.arena;
#endif

return memtotal;
}


void memExitOnError(void)
{
dmemory_ExitOnError = 1;
}

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>

#if !defined (NAMESPACE_H)
#endif

int _ExitOnError   = 1;
int _Verbose = 1;
int _Debug   = 0;


#if ! defined __GNUC__ && ! defined __attribute__
#  define  __attribute__(x)
#endif

void SysError_(const char *caller, const char *fmt, ...)
__attribute__((noreturn));

void SysError_(const char *caller, const char *fmt, ...)
{
va_list args;
int saved_errno = errno;

va_start(args, fmt);

printf("\n");
fprintf(stderr, "Error (%s): ", caller);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");

va_end(args);

if ( saved_errno )
    {
    errno = saved_errno;
    perror("System error message");
    }

exit(EXIT_FAILURE);
}


void Error_(const char *caller, const char *fmt, ...)
{
va_list args;

va_start(args, fmt);

printf("\n");
fprintf(stderr, "Error (%s): ", caller);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");

va_end(args);

if ( _ExitOnError ) exit(EXIT_FAILURE);
}

typedef void (*cdiAbortCFunc)(const char * caller, const char * filename,
                            const char *functionname, int line,
                            const char * errorString, va_list ap)
#ifdef __GNUC__
__attribute__((noreturn))
#endif
;

void cdiAbortC(const char * caller, const char * filename,
            const char *functionname, int line,
            const char * errorString, ... )
{
va_list ap;
va_start(ap, errorString);
cdiAbortCFunc cdiAbortC_p
    = (cdiAbortCFunc)namespaceSwitchGet(NSSWITCH_ABORT).func;
cdiAbortC_p(caller, filename, functionname, line, errorString, ap);
va_end(ap);
}

void
cdiAbortC_serial(const char *caller, const char *filename,
                const char *functionname, int line,
                const char *errorString, va_list ap)
{
fprintf(stderr, "ERROR, %s, %s, line %d%s%s\nerrorString: \"",
        functionname, filename, line, caller?", called from ":"",
        caller?caller:"");
vfprintf(stderr, errorString, ap);
fputs("\"\n", stderr);
exit(EXIT_FAILURE);
}

typedef void (*cdiWarningFunc)(const char * caller, const char * fmt,
                            va_list ap);

void Warning_(const char *caller, const char *fmt, ...)
{
va_list args;

va_start(args, fmt);

if ( _Verbose )
    {
    cdiWarningFunc cdiWarning_p
        = (cdiWarningFunc)namespaceSwitchGet(NSSWITCH_WARNING).func;
    cdiWarning_p(caller, fmt, args);
    }

va_end(args);
}

void cdiWarning(const char *caller, const char *fmt, va_list ap)
{
fprintf(stderr, "Warning (%s): ", caller);
vfprintf(stderr, fmt, ap);
fputc('\n', stderr);
}


void Message_(const char *caller, const char *fmt, ...)
{
va_list args;

va_start(args, fmt);

fprintf(stdout, "%-18s: ", caller);
vfprintf(stdout, fmt, args);
fprintf(stdout, "\n");

va_end(args);
}

#ifdef HAVE_CONFIG_H
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif



#ifndef O_BINARY
#define O_BINARY 0
#endif

#ifndef strdupx
#ifndef strdup
char *strdup(const char *s);
#endif
#define strdupx  strdup

#endif


#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif



#define  MAX_FILES  8192
static int _file_max = MAX_FILES;

static void file_initialize(void);

static bool _file_init = false;

#ifdef HAVE_LIBPTHREAD
#include <pthread.h>

static pthread_once_t  _file_init_thread = PTHREAD_ONCE_INIT;
static pthread_mutex_t _file_mutex;

#define FILE_LOCK()         pthread_mutex_lock(&_file_mutex)
#define FILE_UNLOCK()       pthread_mutex_unlock(&_file_mutex)
#define FILE_INIT()        \
if ( _file_init == false ) pthread_once(&_file_init_thread, file_initialize)

#else

#define FILE_LOCK()
#define FILE_UNLOCK()
#define FILE_INIT()        \
if ( _file_init == false ) file_initialize()

#endif


typedef struct
{
int        self;
int        flag;
int        eof;
int        fd;
FILE      *fp;
char      *name;
off_t      size;
off_t      position;
long       access;
off_t      byteTrans;
size_t     blockSize;
int        mode;
short      type;
short      bufferType;
size_t     bufferSize;
size_t     mappedSize;
char      *buffer;
long       bufferNumFill;
char      *bufferPtr;
off_t      bufferPos;
off_t      bufferStart;
off_t      bufferEnd;
size_t     bufferCnt;
double     time_in_sec;
}
bfile_t;


enum F_I_L_E_Flags
{
    FILE_READ  =  01,
    FILE_WRITE =  02,
    FILE_UNBUF =  04,
    FILE_EOF   = 010,
    FILE_ERROR = 020
};


static bool FileInfo = false;


#ifndef MIN_BUF_SIZE
#define  MIN_BUF_SIZE  131072L
#endif


static size_t FileBufferSizeMin = MIN_BUF_SIZE;
static long   FileBufferSizeEnv = -1;
static short  FileBufferTypeEnv =  0;

static short  FileTypeRead  = FILE_TYPE_OPEN;
static short  FileTypeWrite = FILE_TYPE_FOPEN;
static int    FileFlagWrite = 0;

static int    FILE_Debug = 0;


static void file_table_print(void);


#undef   LIBVERSION
#define  LIBVERSION      1.8.3
#define  XSTRING(x)	 #x
#define  STRING(x) 	 XSTRING(x)
static const char file_libvers[] = STRING(LIBVERSION);




typedef struct _filePtrToIdx {
int idx;
bfile_t *ptr;
struct _filePtrToIdx *next;
} filePtrToIdx;


static filePtrToIdx *_fileList  = NULL;
static filePtrToIdx *_fileAvail = NULL;

static
void file_list_new(void)
{
assert(_fileList == NULL);

_fileList = (filePtrToIdx *) Malloc((size_t)_file_max*sizeof(filePtrToIdx));
}

static
void file_list_delete(void)
{
if ( _fileList )
    {
    Free(_fileList);
    _fileList = NULL;
    }
}

static
void file_init_pointer(void)
{
for ( int i = 0; i < _file_max; i++ )
    {
    _fileList[i].next = _fileList + i + 1;
    _fileList[i].idx  = i;
    _fileList[i].ptr  = 0;
    }

_fileList[_file_max-1].next = 0;

_fileAvail = _fileList;
}

static
bfile_t *file_to_pointer(int idx)
{
bfile_t *fileptr = NULL;

FILE_INIT();

if ( idx >= 0 && idx < _file_max )
    {
    FILE_LOCK();

    fileptr = _fileList[idx].ptr;

    FILE_UNLOCK();
    }
else
    Error("file index %d undefined!", idx);

return fileptr;
}


static
int file_from_pointer(bfile_t *ptr)
{
int idx = -1;
filePtrToIdx *newptr;

if ( ptr )
    {
    FILE_LOCK();

    if ( _fileAvail )
        {
        newptr       = _fileAvail;
        _fileAvail   = _fileAvail->next;
        newptr->next = 0;
        idx	       = newptr->idx;
        newptr->ptr  = ptr;

        if ( FILE_Debug )
            Message("Pointer %p has idx %d from file list", ptr, idx);
        }
    else
        {
        Warning("Too many open files (limit is %d)!", _file_max);
        idx = -2;
        }

    FILE_UNLOCK();
    }
else
    Error("Internal problem (pointer %p undefined)", ptr);

return idx;
}

static
void file_init_entry(bfile_t *fileptr)
{
fileptr->self          = file_from_pointer(fileptr);

fileptr->flag          = 0;
fileptr->fd            = -1;
fileptr->fp            = NULL;
fileptr->mode          = 0;
fileptr->size          = 0;
fileptr->name          = NULL;
fileptr->access        = 0;
fileptr->position      = 0;
fileptr->byteTrans     = 0;
fileptr->type          = 0;
fileptr->bufferType    = 0;
fileptr->bufferSize    = 0;
fileptr->mappedSize    = 0;
fileptr->buffer        = NULL;
fileptr->bufferNumFill = 0;
fileptr->bufferStart   = 0;
fileptr->bufferEnd     = -1;
fileptr->bufferPos     = 0;
fileptr->bufferCnt     = 0;
fileptr->bufferPtr     = NULL;
fileptr->time_in_sec   = 0.0;
}

static
bfile_t *file_new_entry(void)
{
bfile_t *fileptr = (bfile_t *) Malloc(sizeof(bfile_t));
if ( fileptr ) file_init_entry(fileptr);

return fileptr;
}

static
void file_delete_entry(bfile_t *fileptr)
{
int idx = fileptr->self;

FILE_LOCK();

Free(fileptr);

_fileList[idx].next = _fileAvail;
_fileList[idx].ptr  = 0;
_fileAvail   	      = &_fileList[idx];

FILE_UNLOCK();

if ( FILE_Debug )
    Message("Removed idx %d from file list", idx);
}


const char *fileLibraryVersion(void)
{
return file_libvers;
}

static
int pagesize(void)
{
#ifdef _SC_PAGESIZE
return (int) sysconf(_SC_PAGESIZE);
#else
#ifndef POSIXIO_DEFAULT_PAGESIZE
#define POSIXIO_DEFAULT_PAGESIZE 4096
#endif
return (int) POSIXIO_DEFAULT_PAGESIZE;
#endif
}

static
double file_time()
{
#ifdef HAVE_SYS_TIME_H
struct timeval mytime;
gettimeofday(&mytime, NULL);
double tseconds = (double) mytime.tv_sec + (double) mytime.tv_usec*1.0e-6;
return tseconds;
#else
return 0;
#endif
}

void fileDebug(int debug)
{
FILE_Debug = debug;
if ( FILE_Debug ) Message("Debug level %d", debug);
}


void *filePtr(int fileID)
{
return (void*)file_to_pointer(fileID);
}

static
void file_pointer_info(const char *caller, int fileID)
{
if ( FILE_Debug )
    {
    fprintf(stdout, "%-18s : ", caller);
    fprintf(stdout, "The fileID %d underlying pointer is not valid!", fileID);
    fprintf(stdout, "\n");
    }
}


int fileSetBufferType(int fileID, int type)
{
int ret = 0;
bfile_t *fileptr = file_to_pointer(fileID);

if ( fileptr )
    {
    switch (type)
        {
        case FILE_BUFTYPE_STD:
        case FILE_BUFTYPE_MMAP:
        fileptr->bufferType = (short)type;
        break;
        default:
        Error("File type %d not implemented!", type);
        }
    }

#ifndef HAVE_MMAP
if ( type == FILE_BUFTYPE_MMAP ) ret = 1;
#endif

return ret;
}

int fileFlush(int fileID)
{
int retval = 0;
bfile_t *fileptr = file_to_pointer(fileID);
if ( fileptr ) retval = fflush(fileptr->fp);

return retval;
}


void fileClearerr(int fileID)
{
bfile_t *fileptr = file_to_pointer(fileID);

if ( fileptr )
    {
    if ( fileptr->mode != 'r' )
        clearerr(fileptr->fp);
    }
}


int filePtrEOF(void *vfileptr)
{
int retval = 0;
bfile_t *fileptr = (bfile_t *) vfileptr;
if ( fileptr ) retval = (fileptr->flag & FILE_EOF) != 0;

return retval;
}


int fileEOF(int fileID)
{
int retval = 0;
bfile_t *fileptr = file_to_pointer(fileID);
if ( fileptr ) retval = (fileptr->flag & FILE_EOF) != 0;

return retval;
}

void fileRewind(int fileID)
{
fileSetPos(fileID, (off_t) 0, SEEK_SET);
fileClearerr(fileID);
}


off_t fileGetPos(int fileID)
{
off_t filepos = 0;
bfile_t *fileptr = file_to_pointer(fileID);

if ( fileptr )
    {
    if ( fileptr->mode == 'r' && fileptr->type == FILE_TYPE_OPEN )
        filepos = fileptr->position;
    else
        filepos = ftell(fileptr->fp);
    }

if ( FILE_Debug ) Message("Position %ld", filepos);

return filepos;
}


int fileSetPos(int fileID, off_t offset, int whence)
{
int status = 0;
bfile_t *fileptr = file_to_pointer(fileID);

if ( FILE_Debug ) Message("Offset %8ld  Whence %3d", (long) offset, whence);

if ( fileptr == 0 )
    {
    file_pointer_info(__func__, fileID);
    return 1;
    }

switch (whence)
    {
    case SEEK_SET:
    if ( fileptr->mode == 'r' && fileptr->type == FILE_TYPE_OPEN )
        {
        off_t position = offset;
        fileptr->position = position;
        if ( position < fileptr->bufferStart || position > fileptr->bufferEnd )
            {
            if ( fileptr->bufferType == FILE_BUFTYPE_STD )
                fileptr->bufferPos = position;
            else
                fileptr->bufferPos = position - position % pagesize();

            fileptr->bufferCnt = 0;
            fileptr->bufferPtr = NULL;
            }
        else
            {
            if ( fileptr->bufferPos != fileptr->bufferEnd + 1 )
                {
                if ( FILE_Debug )
                    Message("Reset buffer pos from %ld to %ld",
                            fileptr->bufferPos, fileptr->bufferEnd + 1);

                fileptr->bufferPos = fileptr->bufferEnd + 1;
                }
            fileptr->bufferCnt = (size_t)(fileptr->bufferEnd - position) + 1;
            fileptr->bufferPtr = fileptr->buffer + position - fileptr->bufferStart;
            }
        }
    else
        {
        status = fseek(fileptr->fp, offset, whence);
        }
    break;
    case SEEK_CUR:
    if ( fileptr->mode == 'r' && fileptr->type == FILE_TYPE_OPEN )
        {
        fileptr->position += offset;
        off_t position = fileptr->position;
        if ( position < fileptr->bufferStart || position > fileptr->bufferEnd )
            {
            if ( fileptr->bufferType == FILE_BUFTYPE_STD )
                fileptr->bufferPos = position;
            else
                fileptr->bufferPos = position - position % pagesize();

            fileptr->bufferCnt = 0;
            fileptr->bufferPtr = NULL;
            }
        else
            {
            if ( fileptr->bufferPos != fileptr->bufferEnd + 1 )
                {
                if ( FILE_Debug )
                    Message("Reset buffer pos from %ld to %ld",
                            fileptr->bufferPos, fileptr->bufferEnd + 1);

                fileptr->bufferPos = fileptr->bufferEnd + 1;
                }
            fileptr->bufferCnt -= (size_t)offset;
            fileptr->bufferPtr += offset;
            }
        }
    else
        {
        status = fseek(fileptr->fp, offset, whence);
        }
    break;
    default:
    Error("Whence = %d not implemented", whence);
    }

if ( fileptr->position < fileptr->size )
    if ( (fileptr->flag & FILE_EOF) != 0 )
    fileptr->flag -= FILE_EOF;

return status;
}

static
void file_table_print(void)
{
int lprintHeader = 1;

for ( int fileID = 0; fileID < _file_max; fileID++ )
    {
    bfile_t *fileptr = file_to_pointer(fileID);

    if ( fileptr )
        {
        if ( lprintHeader )
            {
            fprintf(stderr, "\nFile table:\n");
            fprintf(stderr, "+-----+---------+");
            fprintf(stderr, "----------------------------------------------------+\n");
            fprintf(stderr, "|  ID |  Mode   |");
            fprintf(stderr, "  Name                                              |\n");
            fprintf(stderr, "+-----+---------+");
            fprintf(stderr, "----------------------------------------------------+\n");
            lprintHeader = 0;
            }

        fprintf(stderr, "| %3d | ", fileID);

        switch ( fileptr->mode )
            {
            case 'r':
            fprintf(stderr, "read   ");
            break;
            case 'w':
            fprintf(stderr, "write  ");
            break;
            case 'a':
            fprintf(stderr, "append ");
            break;
            default:
            fprintf(stderr, "unknown");
            }

        fprintf(stderr, " | %-51s|\n", fileptr->name);
        }
    }

if ( lprintHeader == 0 )
    {
    fprintf(stderr, "+-----+---------+");
    fprintf(stderr, "----------------------------------------------------+\n");
    }
}


char *fileInqName(int fileID)
{
char *name = NULL;
bfile_t *fileptr = file_to_pointer(fileID);
if ( fileptr ) name = fileptr->name;

return name;
}


int fileInqMode(int fileID)
{
int mode = 0;
bfile_t *fileptr = file_to_pointer(fileID);
if ( fileptr ) mode = fileptr->mode;

return mode;
}

static
long file_getenv(const char *envName)
{
long envValue = -1;
long fact = 1;

char *envString = getenv(envName);

if ( envString )
    {
    for ( int loop = 0; loop < (int) strlen(envString); loop++ )
        {
        if ( ! isdigit((int) envString[loop]) )
            {
            switch ( tolower((int) envString[loop]) )
                {
                case 'k':  fact =       1024;  break;
                case 'm':  fact =    1048576;  break;
                case 'g':  fact = 1073741824;  break;
                default:
                fact = 0;
                Message("Invalid number string in %s: %s", envName, envString);
                Warning("%s must comprise only digits [0-9].",envName);
                }
            break;
            }
        }

    if ( fact ) envValue = fact*atol(envString);

    if ( FILE_Debug ) Message("Set %s to %ld", envName, envValue);
    }

return envValue;
}

static
void file_initialize(void)
{
long value;

#ifdef HAVE_LIBPTHREAD

pthread_mutex_init(&_file_mutex, NULL);
#endif

value = file_getenv("FILE_DEBUG");
if ( value >= 0 ) FILE_Debug = (int) value;

value = file_getenv("FILE_MAX");
if ( value >= 0 ) _file_max = (int) value;

if ( FILE_Debug )
    Message("FILE_MAX = %d", _file_max);

FileInfo = file_getenv("FILE_INFO") > 0;

value  = file_getenv("FILE_BUFSIZE");
if ( value >= 0 ) FileBufferSizeEnv = value;
else
    {
    value  = file_getenv("GRIB_API_IO_BUFFER_SIZE");
    if ( value >= 0 ) FileBufferSizeEnv = value;
    }

value = file_getenv("FILE_TYPE_READ");
if ( value > 0 )
    {
    switch (value)
        {
        case FILE_TYPE_OPEN:
        case FILE_TYPE_FOPEN:
        FileTypeRead = (short)value;
        break;
        default:
        Warning("File type %d not implemented!", value);
        }
    }

value = file_getenv("FILE_TYPE_WRITE");
if ( value > 0 )
    {
    switch (value)
        {
        case FILE_TYPE_OPEN:
        case FILE_TYPE_FOPEN:
        FileTypeWrite = (short)value;
        break;
        default:
        Warning("File type %d not implemented!", value);
        }
    }

#ifdef O_NONBLOCK
FileFlagWrite = O_NONBLOCK;
#endif
char *envString = getenv("FILE_FLAG_WRITE");
if ( envString )
    {
#ifdef O_NONBLOCK
    if ( strcmp(envString, "NONBLOCK") == 0 ) FileFlagWrite = O_NONBLOCK;
#endif
    }

value = file_getenv("FILE_BUFTYPE");
#ifndef HAVE_MMAP
if ( value == FILE_BUFTYPE_MMAP )
    {
    Warning("MMAP not available!");
    value = 0;
    }
#endif
if ( value > 0 )
    {
    switch (value)
        {
        case FILE_BUFTYPE_STD:
        case FILE_BUFTYPE_MMAP:
        FileBufferTypeEnv = (short)value;
        break;
        default:
        Warning("File buffer type %d not implemented!", value);
        }
    }

file_list_new();
atexit(file_list_delete);

FILE_LOCK();

file_init_pointer();

FILE_UNLOCK();

if ( FILE_Debug ) atexit(file_table_print);

_file_init = true;
}

static
void file_set_buffer(bfile_t *fileptr)
{
size_t buffersize = 0;

if ( fileptr->mode == 'r' )
    {
    if ( FileBufferTypeEnv )
        fileptr->bufferType = FileBufferTypeEnv;
    else if ( fileptr->bufferType == 0 )
        fileptr->bufferType = FILE_BUFTYPE_STD;

    if ( FileBufferSizeEnv >= 0 )
        buffersize = (size_t) FileBufferSizeEnv;
    else if ( fileptr->bufferSize > 0 )
        buffersize = fileptr->bufferSize;
    else
        {
        buffersize = fileptr->blockSize * 4;
        if ( buffersize < FileBufferSizeMin ) buffersize = FileBufferSizeMin;
        }

    if ( (size_t) fileptr->size < buffersize )
        buffersize = (size_t) fileptr->size;

    if ( fileptr->bufferType == FILE_BUFTYPE_MMAP )
        {
        size_t blocksize = (size_t) pagesize();
        size_t minblocksize = 4 * blocksize;
        buffersize = buffersize - buffersize % minblocksize;

        if ( buffersize < (size_t) fileptr->size && buffersize < minblocksize )
            buffersize = minblocksize;
        }

    if ( buffersize == 0 ) buffersize = 1;
    }
else
    {
    fileptr->bufferType = FILE_BUFTYPE_STD;

    if ( FileBufferSizeEnv >= 0 )
        buffersize = (size_t) FileBufferSizeEnv;
    else if ( fileptr->bufferSize > 0 )
        buffersize = fileptr->bufferSize;
    else
        {
        buffersize = fileptr->blockSize * 4;
        if ( buffersize < FileBufferSizeMin ) buffersize = FileBufferSizeMin;
        }
    }

if ( fileptr->bufferType == FILE_BUFTYPE_STD || fileptr->type == FILE_TYPE_FOPEN )
    {
    if ( buffersize > 0 )
        {
        fileptr->buffer = (char *) Malloc(buffersize);
        if ( fileptr->buffer == NULL )
            SysError("Allocation of file buffer failed!");
        }
    }

if ( fileptr->type == FILE_TYPE_FOPEN )
    if ( setvbuf(fileptr->fp, fileptr->buffer, fileptr->buffer ? _IOFBF : _IONBF, buffersize) )
    SysError("setvbuf failed!");

fileptr->bufferSize = buffersize;
}

static
int file_fill_buffer(bfile_t *fileptr)
{
ssize_t nread;
long offset = 0;

if ( FILE_Debug )
    Message("file ptr = %p  Cnt = %ld", fileptr, fileptr->bufferCnt);

if ( (fileptr->flag & FILE_EOF) != 0 ) return EOF;

if ( fileptr->buffer == NULL ) file_set_buffer(fileptr);

if ( fileptr->bufferSize == 0 ) return EOF;

int fd = fileptr->fd;

#ifdef HAVE_MMAP
if ( fileptr->bufferType == FILE_BUFTYPE_MMAP )
    {
    if ( fileptr->bufferPos >= fileptr->size )
        {
        nread = 0;
        }
    else
        {
        xassert(fileptr->bufferSize <= SSIZE_MAX);
        nread = (ssize_t)fileptr->bufferSize;
        if ( (nread + fileptr->bufferPos) > fileptr->size )
            nread = fileptr->size - fileptr->bufferPos;

        if ( fileptr->buffer )
            {
            int ret = munmap(fileptr->buffer, fileptr->mappedSize);
            if ( ret == -1 ) SysError("munmap error for read %s", fileptr->name);
            fileptr->buffer = NULL;
            }

        fileptr->mappedSize = (size_t)nread;

        fileptr->buffer = (char*) mmap(NULL, (size_t) nread, PROT_READ, MAP_PRIVATE, fd, fileptr->bufferPos);

        if ( fileptr->buffer == MAP_FAILED ) SysError("mmap error for read %s", fileptr->name);

        offset = fileptr->position - fileptr->bufferPos;
        }
    }
else
#endif
    {
    off_t retseek = lseek(fileptr->fd, fileptr->bufferPos, SEEK_SET);
    if ( retseek == (off_t)-1 )
        SysError("lseek error at pos %ld file %s", (long) fileptr->bufferPos, fileptr->name);

    nread = read(fd, fileptr->buffer, fileptr->bufferSize);
    if ( nread > 0 ) offset = fileptr->position - fileptr->bufferPos;
    }

if ( nread <= 0 )
    {
    fileptr->flag |= (nread == 0) ? FILE_EOF : FILE_ERROR;
    fileptr->bufferCnt = 0;
    return EOF;
    }

fileptr->bufferPtr = fileptr->buffer;
fileptr->bufferCnt = (size_t)nread;

fileptr->bufferStart = fileptr->bufferPos;
fileptr->bufferPos  += nread;
fileptr->bufferEnd   = fileptr->bufferPos - 1;

if ( FILE_Debug )
    {
    Message("fileID = %d  Val     = %d",  fileptr->self, (int) fileptr->buffer[0]);
    Message("fileID = %d  Start   = %ld", fileptr->self, fileptr->bufferStart);
    Message("fileID = %d  End     = %ld", fileptr->self, fileptr->bufferEnd);
    Message("fileID = %d  nread   = %ld", fileptr->self, nread);
    Message("fileID = %d  offset  = %ld", fileptr->self, offset);
    Message("fileID = %d  Pos     = %ld", fileptr->self, fileptr->bufferPos);
    Message("fileID = %d  position = %ld", fileptr->self, fileptr->position);
    }

if ( offset > 0 )
    {
    if ( offset > nread )
        Error("Internal problem with buffer handling. nread = %d offset = %d", nread, offset);

    fileptr->bufferPtr += offset;
    fileptr->bufferCnt -= (size_t)offset;
    }

fileptr->bufferNumFill++;

return (unsigned char) *fileptr->bufferPtr;
}

static
void file_copy_from_buffer(bfile_t *fileptr, void *ptr, size_t size)
{
if ( FILE_Debug )
    Message("size = %ld  Cnt = %ld", size, fileptr->bufferCnt);

if ( fileptr->bufferCnt < size )
    Error("Buffer too small. bufferCnt = %d", fileptr->bufferCnt);

if ( size == 1 )
    {
    ((char *)ptr)[0] = fileptr->bufferPtr[0];

    fileptr->bufferPtr++;
    fileptr->bufferCnt--;
    }
else
    {
    memcpy(ptr, fileptr->bufferPtr, size);

    fileptr->bufferPtr += size;
    fileptr->bufferCnt -= size;
    }
}

static
size_t file_read_from_buffer(bfile_t *fileptr, void *ptr, size_t size)
{
size_t nread;
size_t offset = 0;

if ( FILE_Debug )
    Message("size = %ld  Cnt = %ld", size, (long) fileptr->bufferCnt);

if ( ((long)fileptr->bufferCnt) < 0L )
    Error("Internal problem. bufferCnt = %ld", (long) fileptr->bufferCnt);

size_t rsize = size;

while ( fileptr->bufferCnt < rsize )
    {
    nread = fileptr->bufferCnt;

    if ( nread > (size_t) 0 )
        file_copy_from_buffer(fileptr, (char *)ptr+offset, nread);
    offset += nread;
    if ( nread < rsize )
        rsize -= nread;
    else
        rsize = 0;

    if ( file_fill_buffer(fileptr) == EOF ) break;
    }

nread = size - offset;

if ( fileptr->bufferCnt < nread ) nread = fileptr->bufferCnt;

if ( nread > (unsigned) 0 )
    file_copy_from_buffer(fileptr, (char *)ptr+offset, nread);

return (nread+offset);
}


void fileSetBufferSize(int fileID, long buffersize)
{
bfile_t *fileptr = file_to_pointer(fileID);
xassert(buffersize >= 0);
if ( fileptr ) fileptr->bufferSize = (size_t)buffersize;
}


int fileOpen(const char *filename, const char *mode)
{
int (*myFileOpen)(const char *filename, const char *mode)
    = (int (*)(const char *, const char *))
    namespaceSwitchGet(NSSWITCH_FILE_OPEN).func;
return myFileOpen(filename, mode);
}

int fileOpen_serial(const char *filename, const char *mode)
{
FILE *fp = NULL;
int fd = -1;
int fileID = FILE_UNDEFID;
struct stat filestat;
bfile_t *fileptr = NULL;

FILE_INIT();

int fmode = tolower((int) mode[0]);

switch ( fmode )
    {
    case 'r':
    if ( FileTypeRead == FILE_TYPE_FOPEN )
        fp = fopen(filename, "rb");
    else
        fd =  open(filename, O_RDONLY | O_BINARY);
    break;
    case 'x':  fp = fopen(filename, "rb");      break;
    case 'w':
    if ( FileTypeWrite == FILE_TYPE_FOPEN )
        fp = fopen(filename, "wb");
    else
        fd =  open(filename, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY | FileFlagWrite, 0666);
    break;
    case 'a':  fp = fopen(filename, "ab");      break;
    default:   Error("Mode %c unexpected!", fmode);
    }

if ( FILE_Debug )
    if ( fp == NULL && fd == -1 )
    Message("Open failed on %s mode %c errno %d", filename, fmode, errno);

if ( fp )
    {
    if ( stat(filename, &filestat) != 0 ) return fileID;

    fileptr = file_new_entry();
    if ( fileptr )
        {
        fileID = fileptr->self;
        fileptr->fp = fp;
        }
    }
else if ( fd >= 0 )
    {
    if ( fstat(fd, &filestat) != 0 ) return fileID;

    fileptr = file_new_entry();
    if ( fileptr )
        {
        fileID = fileptr->self;
        fileptr->fd = fd;
        }
    }

if ( fileID >= 0 )
    {
    fileptr->mode = fmode;
    fileptr->name = strdupx(filename);

#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
    fileptr->blockSize = (size_t) filestat.st_blksize;
#else
    fileptr->blockSize = (size_t) 4096;
#endif

    if ( fmode == 'r' )
        fileptr->type = FileTypeRead;
    else if ( fmode == 'w' )
        fileptr->type = FileTypeWrite;
    else
        fileptr->type = FILE_TYPE_FOPEN;

    if ( fmode == 'r' ) fileptr->size = filestat.st_size;

    if ( fileptr->type == FILE_TYPE_FOPEN ) file_set_buffer(fileptr);

    if ( FILE_Debug )
        Message("File %s opened with ID %d", filename, fileID);
    }

return fileID;
}


int fileClose(int fileID)
{
int (*myFileClose)(int fileID)
    = (int (*)(int))namespaceSwitchGet(NSSWITCH_FILE_CLOSE).func;
return myFileClose(fileID);
}

int fileClose_serial(int fileID)
{
int ret;
const char *fbtname[] = {"unknown", "standard", "mmap"};
const char *ftname[] = {"unknown", "open", "fopen"};
bfile_t *fileptr = file_to_pointer(fileID);
double rout = 0;

if ( fileptr == NULL )
    {
    file_pointer_info(__func__, fileID);
    return 1;
    }

char *name = fileptr->name;

if ( FILE_Debug )
    Message("fileID = %d  filename = %s", fileID, name);

if ( FileInfo )
    {
    fprintf(stderr, "____________________________________________\n");
    fprintf(stderr, " file ID          : %d\n",  fileID);
    fprintf(stderr, " file name        : %s\n",  fileptr->name);
    fprintf(stderr, " file type        : %d (%s)\n", fileptr->type, ftname[fileptr->type]);

    if ( fileptr->type == FILE_TYPE_FOPEN )
        fprintf(stderr, " file pointer     : %p\n",  (void *) fileptr->fp);
    else
        {
        fprintf(stderr, " file descriptor  : %d\n",  fileptr->fd);
        fprintf(stderr, " file flag        : %d\n", FileFlagWrite);
        }
    fprintf(stderr, " file mode        : %c\n",  fileptr->mode);

    if ( sizeof(off_t) > sizeof(long) )
        {
#ifdef _WIN32
        fprintf(stderr, " file size        : %I64d\n", (long long) fileptr->size);
        if ( fileptr->type == FILE_TYPE_OPEN )
            fprintf(stderr, " file position    : %I64d\n", (long long) fileptr->position);
        fprintf(stderr, " bytes transferred : %I64d\n", (long long) fileptr->byteTrans);
#else
        fprintf(stderr, " file size        : %lld\n", (long long) fileptr->size);
        if ( fileptr->type == FILE_TYPE_OPEN )
            fprintf(stderr, " file position    : %lld\n", (long long) fileptr->position);
        fprintf(stderr, " bytes transferred : %lld\n", (long long) fileptr->byteTrans);
#endif
        }
    else
        {
        fprintf(stderr, " file size        : %ld\n", (long) fileptr->size);
        if ( fileptr->type == FILE_TYPE_OPEN )
            fprintf(stderr, " file position    : %ld\n", (long) fileptr->position);
        fprintf(stderr, " bytes transferred : %ld\n", (long) fileptr->byteTrans);
        }

    if ( fileptr->time_in_sec > 0 )
        rout = (double)fileptr->byteTrans / (1024.*1024.*fileptr->time_in_sec);

    fprintf(stderr, " wall time [s]    : %.2f\n", fileptr->time_in_sec);
    fprintf(stderr, " data rate [MB/s] : %.1f\n", rout);

    fprintf(stderr, " file access      : %ld\n", fileptr->access);
    if ( fileptr->mode == 'r' && fileptr->type == FILE_TYPE_OPEN )
        {
        fprintf(stderr, " buffer type      : %d (%s)\n", fileptr->bufferType, fbtname[fileptr->bufferType]);
        fprintf(stderr, " num buffer fill  : %ld\n", fileptr->bufferNumFill);
        }
    fprintf(stderr, " buffer size      : %lu\n", (unsigned long) fileptr->bufferSize);
    fprintf(stderr, " block size       : %lu\n", (unsigned long) fileptr->blockSize);
    fprintf(stderr, " page size        : %d\n",  pagesize());
    fprintf(stderr, "--------------------------------------------\n");
    }

if ( fileptr->type == FILE_TYPE_FOPEN )
    {
    ret = fclose(fileptr->fp);
    if ( ret == EOF )
        SysError("EOF returned for close of %s!", name);
    }
else
    {
#ifdef HAVE_MMAP
    if ( fileptr->buffer && fileptr->mappedSize )
        {
        ret = munmap(fileptr->buffer, fileptr->mappedSize);
        if ( ret == -1 ) SysError("munmap error for close %s", fileptr->name);
        fileptr->buffer = NULL;
        }
#endif
    ret = close(fileptr->fd);
    if ( ret == -1 )
        SysError("EOF returned for close of %s!", name);
    }

if ( fileptr->name )    Free((void*) fileptr->name);
if ( fileptr->buffer )  Free((void*) fileptr->buffer);

file_delete_entry(fileptr);

return 0;
}


int filePtrGetc(void *vfileptr)
{
int ivalue = EOF;
bfile_t *fileptr = (bfile_t *) vfileptr;

if ( fileptr )
    {
    if ( fileptr->mode == 'r' && fileptr->type == FILE_TYPE_OPEN )
        {
        int fillret = (fileptr->bufferCnt == 0) ? file_fill_buffer(fileptr) : 0;
        if ( fillret >= 0 )
            {
            ivalue = (unsigned char) *fileptr->bufferPtr++;
            fileptr->bufferCnt--;
            fileptr->position++;

            fileptr->byteTrans++;
            fileptr->access++;
            }
        }
    else
        {
        ivalue = fgetc(fileptr->fp);
        if ( ivalue >= 0 )
            {
            fileptr->byteTrans++;
            fileptr->access++;
            }
        else
            fileptr->flag |= FILE_EOF;
        }
    }

return ivalue;
}


int fileGetc(int fileID)
{
bfile_t *fileptr = file_to_pointer(fileID);
return filePtrGetc((void *)fileptr);
}


size_t filePtrRead(void *vfileptr, void *restrict ptr, size_t size)
{
size_t nread = 0;
bfile_t *fileptr = (bfile_t *) vfileptr;

if ( fileptr )
    {
    if ( fileptr->mode == 'r' && fileptr->type == FILE_TYPE_OPEN )
        nread = file_read_from_buffer(fileptr, ptr, size);
    else
        {
        nread = fread(ptr, 1, size, fileptr->fp);
        if ( nread != size )
            fileptr->flag |= (nread == 0) ? FILE_EOF : FILE_ERROR;
        }

    fileptr->position  += (off_t)nread;
    fileptr->byteTrans += (off_t)nread;
    fileptr->access++;
    }

if ( FILE_Debug ) Message("size %ld  nread %ld", size, nread);

return nread;
}


size_t fileRead(int fileID, void *restrict ptr, size_t size)
{
size_t nread = 0;
bfile_t *fileptr = file_to_pointer(fileID);

if ( fileptr )
    {
    double t_begin = 0.0;

    if ( FileInfo ) t_begin = file_time();

    if ( fileptr->type == FILE_TYPE_OPEN )
        nread = file_read_from_buffer(fileptr, ptr, size);
    else
        {
        nread = fread(ptr, 1, size, fileptr->fp);
        if ( nread != size )
            {
            if ( nread == 0 )
                fileptr->flag |= FILE_EOF;
            else
                fileptr->flag |= FILE_ERROR;
            }
        }

    if ( FileInfo ) fileptr->time_in_sec += file_time() - t_begin;

    fileptr->position  += (off_t)nread;
    fileptr->byteTrans += (off_t)nread;
    fileptr->access++;
    }

if ( FILE_Debug ) Message("size %ld  nread %ld", size, nread);

return nread;
}


size_t fileWrite(int fileID, const void *restrict ptr, size_t size)
{
size_t nwrite = 0;
bfile_t *fileptr = file_to_pointer(fileID);

if ( fileptr )
    {
    double t_begin = 0.0;

    if ( FileInfo ) t_begin = file_time();

    if ( fileptr->type == FILE_TYPE_FOPEN )
        nwrite = fwrite(ptr, 1, size, fileptr->fp);
    else
        {
        ssize_t temp = write(fileptr->fd, ptr, size);
        if (temp == -1)
            {
            perror("error writing to file");
            nwrite = 0;
            }
        else
            nwrite = (size_t)temp;
        }

    if ( FileInfo ) fileptr->time_in_sec += file_time() - t_begin;

    fileptr->position  += (off_t)nwrite;
    fileptr->byteTrans += (off_t)nwrite;
    fileptr->access++;
    }

return nwrite;
}

#ifndef _GAUSSGRID_H
#define _GAUSSGRID_H

#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

void gaussaw(double *restrict pa, double *restrict pw, size_t nlat);
bool isGaussGrid(size_t ysize, double yinc, const double *yvals);

#if defined (__cplusplus)
}
#endif

#endif

#ifdef  HAVE_CONFIG_H
#endif

#include <stdio.h>
#include <float.h>
#include <math.h>

#ifndef  M_SQRT2
#define  M_SQRT2  1.41421356237309504880168872420969808
#endif



static
void cpledn(size_t kn, size_t kodd, double *pfn, double pdx, int kflag,
            double *pw, double *pdxn, double *pxmod)
{


double zdlx = pdx;
double zdlk = 0.0;
if ( kodd == 0 ) zdlk = 0.5*pfn[0];
double zdlxn  = 0.0;
double zdlldn = 0.0;

size_t ik = 1;

if ( kflag == 0 )
    {
    for ( size_t jn = 2-kodd; jn <= kn; jn += 2 )
        {

        zdlk   = zdlk + pfn[ik]*cos((double)(jn)*zdlx);

        zdlldn = zdlldn - pfn[ik]*(double)(jn)*sin((double)(jn)*zdlx);
        ik++;
        }

    double zdlmod = -(zdlk/zdlldn);
    zdlxn = zdlx + zdlmod;
    *pdxn = zdlxn;
    *pxmod = zdlmod;
    }



if ( kflag == 1 )
    {
    for ( size_t jn = 2-kodd; jn <= kn; jn += 2 )
        {

        zdlldn = zdlldn - pfn[ik]*(double)(jn)*sin((double)(jn)*zdlx);
        ik++;
        }
    *pw = (double)(2*kn+1)/(zdlldn*zdlldn);
    }

return;
}

static
void gawl(double *pfn, double *pl, double *pw, size_t kn)
{
double pmod = 0;
double zw = 0;
double zdlxn = 0;



int iflag  =  0;
int itemax = 20;

size_t iodd   = (kn % 2);

double zdlx   =  *pl;



for ( int jter = 1; jter <= itemax+1; jter++ )
    {
    cpledn(kn, iodd, pfn, zdlx, iflag, &zw, &zdlxn, &pmod);
    zdlx = zdlxn;
    if (iflag == 1) break;
    if (fabs(pmod) <= DBL_EPSILON*1000.0) iflag = 1;
    }

*pl = zdlxn;
*pw = zw;

return;
}

static
void gauaw(size_t kn, double *restrict pl, double *restrict pw)
{

double *zfn    = (double *) Malloc((kn+1) * (kn+1) * sizeof(double));
double *zfnlat = (double *) Malloc((kn/2+1+1)*sizeof(double));

zfn[0] = M_SQRT2;
for ( size_t jn = 1; jn <= kn; jn++ )
    {
    double zfnn = zfn[0];
    for (size_t jgl = 1; jgl <= jn; jgl++)
        {
        zfnn *= sqrt(1.0-0.25/((double)(jgl*jgl)));
        }

    zfn[jn*(kn+1)+jn] = zfnn;

    size_t iodd = jn % 2;
    for ( size_t jgl = 2; jgl <= jn-iodd; jgl += 2 )
        {
        zfn[jn*(kn+1)+jn-jgl] = zfn[jn*(kn+1)+jn-jgl+2]
            *((double)((jgl-1)*(2*jn-jgl+2)))/((double)(jgl*(2*jn-jgl+1)));
        }
    }




size_t iodd = kn % 2;
size_t ik = iodd;
for ( size_t jgl = iodd; jgl <= kn; jgl += 2 )
    {
    zfnlat[ik] = zfn[kn*(kn+1)+jgl];
    ik++;
    }



size_t ins2 = kn/2+(kn % 2);
double z;

for ( size_t jgl = 1; jgl <= ins2; jgl++ )
    {
    z = ((double)(4*jgl-1))*M_PI/((double)(4*kn+2));
    pl[jgl-1] = z+1.0/(tan(z)*((double)(8*kn*kn)));
    }



for ( size_t jgl = ins2; jgl >= 1 ; jgl-- )
    {
    size_t jglm1 = jgl-1;
    gawl(zfnlat, &(pl[jglm1]), &(pw[jglm1]), kn);
    }



for ( size_t jgl = 0; jgl < ins2; jgl++ )
    {
    pl[jgl] = cos(pl[jgl]);
    }

for ( size_t jgl = 1; jgl <= kn/2; jgl++ )
    {
    size_t jglm1 = jgl-1;
    size_t isym =  kn-jgl;
    pl[isym] =  -pl[jglm1];
    pw[isym] =  pw[jglm1];
    }

Free(zfnlat);
Free(zfn);

return;
}


void gaussaw(double *restrict pa, double *restrict pw, size_t nlat)
{

gauaw(nlat, pa, pw);
}


bool isGaussGrid(size_t ysize, double yinc, const double *yvals)
{
bool lgauss = false;

if ( IS_EQUAL(yinc, 0) && ysize > 2 )
    {
    size_t i;
    double *yv = (double *) Malloc(ysize*sizeof(double));
    double *yw = (double *) Malloc(ysize*sizeof(double));
    gaussaw(yv, yw, ysize);
    Free(yw);
    for ( i = 0; i < ysize; i++ )
        yv[i] = asin(yv[i])/M_PI*180.0;

    for ( i = 0; i < ysize; i++ )
        if ( fabs(yv[i] - yvals[i]) >
            ((yv[0] - yv[1])/500) ) break;

    if ( i == ysize ) lgauss = true;


    if ( lgauss == false )
        {
        for ( i = 0; i < ysize; i++ )
            if ( fabs(yv[i] - yvals[ysize-i-1]) >
                ((yv[0] - yv[1])/500) ) break;

        if ( i == ysize ) lgauss = true;
        }

    Free(yv);
    }

return lgauss;
}



#ifndef CDI_UUID_H
#define CDI_UUID_H

#ifdef HAVE_CONFIG_H
#endif



#ifdef __cplusplus
extern "C" {
#endif

static inline int cdiUUIDIsNull(const unsigned char uuid[])
{
int isNull = 1;
for (size_t i = 0; i < CDI_UUID_SIZE; ++i)
    isNull &= (uuid[i] == 0);
return isNull;
}

void cdiCreateUUID(unsigned char uuid[CDI_UUID_SIZE]);

void cdiUUID2Str(const unsigned char uuid[], char uuidstr[]);
int cdiStr2UUID(const char *uuidstr, unsigned char uuid[]);

#if defined (__cplusplus)
}
#endif

#endif


#ifndef RESOURCE_UNPACK_H
#define RESOURCE_UNPACK_H

#ifdef HAVE_CONFIG_H
#endif

enum
{ GRID      = 1,
ZAXIS     = 2,
TAXIS     = 3,
INSTITUTE = 4,
MODEL     = 5,
STREAM    = 6,
VLIST     = 7,
RESH_DELETE,
START     = 55555555,
END       = 99999999
};

int reshUnpackResources(char * unpackBuffer, int unpackBufferSize,
                        void *context);

#endif


#ifndef  CDI_KEY_H
#define  CDI_KEY_H



typedef struct {
int       key;
int       type;
int       length;
union {
    int i;
    double d;
    unsigned char *s;
} v;
} cdi_key_t;


typedef struct {
size_t     nalloc;
size_t     nelems;
cdi_key_t  value[MAX_KEYS];
} cdi_keys_t;


void cdiDefVarKeyInt(cdi_keys_t *keysp, int key, int value);
void cdiDefVarKeyBytes(cdi_keys_t *keysp, int key, const unsigned char *bytes, int length);

void cdiCopyVarKeys(cdi_keys_t *keysp, int cdiID2, int varID2);
void cdiDeleteVarKeys(cdi_keys_t *keysp);
int cdiDeleteKeys(int cdiID, int varID);

#endif


#ifndef  VLIST_H
#define  VLIST_H

#ifdef  HAVE_CONFIG_H
#endif

#ifndef  ERROR_H
#endif

#include <stddef.h>

#ifndef  CDI_LIMITS_H
#endif

#define VALIDMISS 1.e+303


typedef struct
{
bool     flag;
int      index;
int      mlevelID;
int      flevelID;
}
levinfo_t;

#define DEFAULT_LEVINFO(levID) \
(levinfo_t){ 0, -1, levID, levID}

typedef struct
{
int ens_index;
int ens_count;
int forecast_init_type;
}
ensinfo_t;



typedef struct
{
bool        isUsed;
bool        flag;
int         mvarID;
int         fvarID;
int         param;
int         gridID;
int         zaxisID;
int         timetype;
int         tsteptype;
int         datatype;
int         instID;
int         modelID;
int         tableID;
int         timave;
int         chunktype;
int         xyz;
bool        missvalused;
bool        lvalidrange;
char       *name;
char       *longname;
char       *stdname;
char       *units;
char       *extra;
double      missval;
double      scalefactor;
double      addoffset;
double      validrange[2];
levinfo_t  *levinfo;
int         comptype;
int         complevel;
cdi_keys_t  keys;
cdi_atts_t  atts;
int         iorank;

int         subtypeID;

int                 opt_grib_nentries;
int                 opt_grib_kvpair_size;
opt_key_val_pair_t *opt_grib_kvpair;
}
var_t;


typedef struct
{

bool        immutable;

bool        internal;
int         self;
int         nvars;
int         ngrids;
int         nzaxis;
int         nsubtypes;
long        ntsteps;
int         taxisID;
int         tableID;
int         instID;
int         modelID;
int         varsAllocated;
int         gridIDs[MAX_GRIDS_PS];
int         zaxisIDs[MAX_ZAXES_PS];
int         subtypeIDs[MAX_SUBTYPES_PS];
var_t      *vars;
cdi_keys_t  keys;
cdi_atts_t  atts;
}
vlist_t;


int vlist_key_compare(vlist_t *a, int varIDA, vlist_t *b, int varIDB, int keynum);

vlist_t *vlist_to_pointer(int vlistID);
void cdiVlistMakeInternal(int vlistID);
void cdiVlistMakeImmutable(int vlistID);
void vlistCheckVarID(const char *caller, int vlistID, int varID);
void     vlistDestroyVarName(int vlistID, int varID);
void     vlistDestroyVarLongname(int vlistID, int varID);
void     vlistDestroyVarStdname(int vlistID, int varID);
void     vlistDestroyVarUnits(int vlistID, int varID);
void     cdiVlistDestroy_(int vlistID);
int      vlistInqVarMissvalUsed(int vlistID, int varID);
int      vlistHasTime(int vlistID);

void     vlistUnpack(char * buffer, int bufferSize, int * pos,
                    int originNamespace, void *context, int force_id);


void    vlistDefVarValidrange(int vlistID, int varID, const double *validrange);


int     vlistInqVarValidrange(int vlistID, int varID, double *validrange);

void vlistInqVarDimorder(int vlistID, int varID, int (*outDimorder)[3]);

int cdi_att_compare(vlist_t *a, int varIDA, vlist_t *b, int varIDB, int attnum);

void resize_opt_grib_entries(var_t *var, int nentries);



static inline
void vlistAdd2GridIDs(vlist_t *vlistptr, int gridID)
{
int index, ngrids = vlistptr->ngrids;
for ( index = 0; index < ngrids; index++ )
    {
    if ( vlistptr->gridIDs[index] == gridID ) break;

    }

if ( index == ngrids )
    {
    if ( ngrids >= MAX_GRIDS_PS )
        Error("Internal limit exceeded: more than %d grids.", MAX_GRIDS_PS);
    vlistptr->gridIDs[ngrids] = gridID;
    ++(vlistptr->ngrids);
    }
}

static inline
void vlistAdd2ZaxisIDs(vlist_t *vlistptr, int zaxisID)
{
int index, nzaxis = vlistptr->nzaxis;
for ( index = 0; index < nzaxis; index++ )
    if ( zaxisID == vlistptr->zaxisIDs[index] ) break;

if ( index == nzaxis )
    {
    if ( nzaxis >= MAX_ZAXES_PS )
        Error("Internal limit exceeded: more than %d zaxis.", MAX_ZAXES_PS);
    vlistptr->zaxisIDs[nzaxis] = zaxisID;
    ++(vlistptr->nzaxis);
    }
}

static inline
void vlistAdd2SubtypeIDs(vlist_t *vlistptr, int subtypeID)
{
if ( subtypeID == CDI_UNDEFID ) return;

int index, nsubs = vlistptr->nsubtypes;
for ( index = 0; index < nsubs; index++ )
    if ( vlistptr->subtypeIDs[index] == subtypeID ) break;

if ( index == nsubs )
    {
    if ( nsubs >= MAX_SUBTYPES_PS )
        Error("Internal limit exceeded: more than %d subs.", MAX_SUBTYPES_PS);
    vlistptr->subtypeIDs[nsubs] = subtypeID;
    ++(vlistptr->nsubtypes);
    }
}



#ifdef HAVE_LIBGRIB_API
extern int   cdiNAdditionalGRIBKeys;
extern char* cdiAdditionalGRIBKeys[];
#endif

extern
#ifndef __cplusplus
const
#endif
resOps vlistOps;

#endif

#ifdef HAVE_CONFIG_H
#endif

#include <assert.h>
#include <string.h>


int (*proj_lonlat_to_lcc_func)() = NULL;
int (*proj_lcc_to_lonlat_func)() = NULL;
int (*proj_lonlat_to_stere_func)() = NULL;
int (*proj_stere_to_lonlat_func)() = NULL;


static const char Grids[][17] = {
    "undefined",
    "generic",
    "gaussian",
    "gaussian_reduced",
    "lonlat",
    "spectral",
    "fourier",
    "gme",
    "trajectory",
    "unstructured",
    "curvilinear",
    "lcc",
    "projection",
    "characterXY",
};


enum xystdname_idx {
grid_xystdname_grid_latlon,
grid_xystdname_latlon,
grid_xystdname_projection,
grid_xystdname_char,
};

/*static const char xystdname_tab[][2][30] = {
[grid_xystdname_grid_latlon] = { "grid_longitude",
                                "grid_latitude" },
[grid_xystdname_latlon] = { "longitude",
                            "latitude" },
[grid_xystdname_projection] = { "projection_x_coordinate",
                                "projection_y_coordinate" },
[grid_xystdname_char] = { "region",
                            "region" },
};*/



static int    gridCompareP    ( void * gridptr1, void * gridptr2 );
static void   gridDestroyP    ( void * gridptr );
static void   gridPrintP      ( void * gridptr, FILE * fp );
static int    gridGetPackSize ( void * gridptr, void *context);
static void   gridPack        ( void * gridptr, void * buff, int size, int *position, void *context);
static int    gridTxCode      ( void );

static const resOps gridOps = {
gridCompareP,
gridDestroyP,
gridPrintP,
gridGetPackSize,
gridPack,
gridTxCode
};

static int  GRID_Debug = 0;


grid_t *grid_to_pointer(int gridID)
{
return (grid_t *)reshGetVal(gridID, &gridOps);
}

#define gridMark4Update(gridID) reshSetStatus(gridID, &gridOps, RESH_DESYNC_IN_USE)

static
bool cdiInqAttConvertedToFloat(int gridID, int atttype, const char *attname, int attlen, double *attflt)
{
bool status = true;

if ( atttype == CDI_DATATYPE_INT32 )
    {
    int attint;
    int *pattint = attlen > 1 ? (int*) malloc(attlen*sizeof(int)) : &attint;
    cdiInqAttInt(gridID, CDI_GLOBAL, attname, attlen, pattint);
    for ( int i = 0; i < attlen; ++i ) attflt[i] = (double)pattint[i];
    if (attlen > 1) free(pattint);
    }
else if ( atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64 )
    {
    cdiInqAttFlt(gridID, CDI_GLOBAL, attname, attlen, attflt);
    }
else
    {
    status = false;
    }

return status;
}


void grid_init(grid_t *gridptr)
{
gridptr->self          = CDI_UNDEFID;
gridptr->type          = CDI_UNDEFID;
gridptr->proj          = CDI_UNDEFID;
gridptr->projtype      = CDI_UNDEFID;
gridptr->mask          = NULL;
gridptr->mask_gme      = NULL;
gridptr->x.vals        = NULL;
gridptr->x.cvals       = NULL;
gridptr->x.clength     = 0;
gridptr->y.vals        = NULL;
gridptr->y.cvals       = NULL;
gridptr->y.clength     = 0;
gridptr->x.bounds      = NULL;
gridptr->y.bounds      = NULL;
gridptr->area          = NULL;
gridptr->rowlon        = NULL;
gridptr->nrowlon       = 0;

gridptr->x.first       = 0.0;
gridptr->x.last        = 0.0;
gridptr->x.inc         = 0.0;
gridptr->y.first       = 0.0;
gridptr->y.last        = 0.0;
gridptr->y.inc         = 0.0;

gridptr->gme.nd        = 0;
gridptr->gme.ni        = 0;
gridptr->gme.ni2       = 0;
gridptr->gme.ni3       = 0;

gridptr->trunc         = 0;
gridptr->nvertex       = 0;
gridptr->number        = 0;
gridptr->position      = 0;
gridptr->reference     = NULL;
gridptr->datatype      = CDI_DATATYPE_FLT64;
gridptr->size          = 0;
gridptr->x.size        = 0;
gridptr->y.size        = 0;
gridptr->np            = 0;
gridptr->x.flag        = 0;
gridptr->y.flag        = 0;
gridptr->isCyclic      = CDI_UNDEFID;

gridptr->lcomplex      = false;
gridptr->hasdims       = true;
gridptr->x.dimname[0]  = 0;
gridptr->y.dimname[0]  = 0;
gridptr->x.name[0]     = 0;
gridptr->y.name[0]     = 0;
gridptr->x.longname[0] = 0;
gridptr->y.longname[0] = 0;
gridptr->x.units[0]    = 0;
gridptr->y.units[0]    = 0;
gridptr->x.stdname     = NULL;
gridptr->y.stdname     = NULL;
gridptr->vdimname[0]   = 0;
gridptr->mapname[0]    = 0;
gridptr->mapping[0]    = 0;
memset(gridptr->uuid, 0, CDI_UUID_SIZE);
gridptr->name          = NULL;
gridptr->vtable        = &cdiGridVtable;
gridptr->atts.nalloc   = MAX_ATTRIBUTES;
gridptr->atts.nelems   = 0;
gridptr->uvRelativeToGrid      = 0;
gridptr->iScansNegatively      = 0;
gridptr->jScansPositively      = 1;
gridptr->jPointsAreConsecutive = 0;
gridptr->scanningMode          = 128*gridptr->iScansNegatively + 64*gridptr->jScansPositively + 32*gridptr->jPointsAreConsecutive;

}


static
void grid_free_components(grid_t *gridptr)
{
void *p2free[] = { gridptr->mask, gridptr->mask_gme,
                    gridptr->x.vals, gridptr->y.vals,
                    gridptr->x.cvals, gridptr->y.cvals,
                    gridptr->x.bounds, gridptr->y.bounds,
                    gridptr->rowlon, gridptr->area,
                    gridptr->reference, gridptr->name};

for ( size_t i = 0; i < sizeof(p2free)/sizeof(p2free[0]); ++i )
    if ( p2free[i] ) Free(p2free[i]);
}

void grid_free(grid_t *gridptr)
{
grid_free_components(gridptr);
grid_init(gridptr);
}

static
grid_t *gridNewEntry(cdiResH resH)
{
grid_t *gridptr = (grid_t*) Malloc(sizeof(grid_t));
grid_init(gridptr);

if ( resH == CDI_UNDEFID )
    gridptr->self = reshPut(gridptr, &gridOps);
else
    {
    gridptr->self = resH;
    reshReplace(resH, gridptr, &gridOps);
    }

return gridptr;
}

static
void gridInit(void)
{
static bool gridInitialized = false;
if ( gridInitialized ) return;
gridInitialized = true;

const char *env = getenv("GRID_DEBUG");
if ( env ) GRID_Debug = atoi(env);
}

static
void grid_copy_base_scalar_fields(grid_t *gridptrOrig, grid_t *gridptrDup)
{
memcpy(gridptrDup, gridptrOrig, sizeof(grid_t));
gridptrDup->self = CDI_UNDEFID;
if ( gridptrOrig->reference )
    gridptrDup->reference = strdupx(gridptrOrig->reference);
}


static
grid_t *grid_copy_base(grid_t *gridptrOrig)
{
grid_t *gridptrDup = (grid_t *)Malloc(sizeof (*gridptrDup));
gridptrOrig->vtable->copyScalarFields(gridptrOrig, gridptrDup);
gridptrOrig->vtable->copyArrayFields(gridptrOrig, gridptrDup);
return gridptrDup;
}


unsigned cdiGridCount(void)
{
return reshCountType(&gridOps);
}

static inline
void gridSetString(char *gridstrname, const char *name, size_t len)
{
if ( len > CDI_MAX_NAME ) len = CDI_MAX_NAME;
strncpy(gridstrname, name, len);
gridstrname[len - 1] = 0;
}

static inline
void gridGetString(char *name, const char *gridstrname, size_t len)
{
if ( len > CDI_MAX_NAME ) len = CDI_MAX_NAME;
strncpy(name, gridstrname, len);
name[len - 1] = 0;
}

static inline
void gridSetName(char *gridstrname, const char *name)
{
strncpy(gridstrname, name, CDI_MAX_NAME);
gridstrname[CDI_MAX_NAME - 1] = 0;
}


void cdiGridTypeInit(grid_t *gridptr, int gridtype, size_t size)
{
gridptr->type = gridtype;
gridptr->size = size;

if      ( gridtype == GRID_CURVILINEAR  ) gridptr->nvertex = 4;
else if ( gridtype == GRID_UNSTRUCTURED ) gridptr->x.size = size;

switch (gridtype)
    {
    case GRID_LONLAT:
    case GRID_GAUSSIAN:
    case GRID_GAUSSIAN_REDUCED:
    case GRID_TRAJECTORY:
    case GRID_CURVILINEAR:
    case GRID_UNSTRUCTURED:
    case GRID_GME:
    {
        if ( gridtype == GRID_TRAJECTORY )
        {
            if ( !gridptr->x.name[0] ) gridSetName(gridptr->x.name, "tlon");
            if ( !gridptr->y.name[0] ) gridSetName(gridptr->y.name, "tlat");
        }
        else
        {
            if ( !gridptr->x.name[0] ) gridSetName(gridptr->x.name, "lon");
            if ( !gridptr->y.name[0] ) gridSetName(gridptr->y.name, "lat");
        }

        if ( !gridptr->x.longname[0] ) gridSetName(gridptr->x.longname, "longitude");
        if ( !gridptr->y.longname[0] ) gridSetName(gridptr->y.longname, "latitude");

        if ( !gridptr->x.units[0] ) gridSetName(gridptr->x.units, "degrees_east");
        if ( !gridptr->y.units[0] ) gridSetName(gridptr->y.units, "degrees_north");

        gridptr->x.stdname = "longitude";//xystdname_tab[grid_xystdname_latlon][0];
        gridptr->y.stdname = "latitude";//xystdname_tab[grid_xystdname_latlon][1];

        break;
    }
    case GRID_CHARXY:
    {
        if ( gridptr->x.cvals ) gridptr->x.stdname = "longitude";//xystdname_tab[grid_xystdname_char][0];
        if ( gridptr->y.cvals ) gridptr->y.stdname = "latitude";//xystdname_tab[grid_xystdname_char][0];

        break;
    }
    case GRID_GENERIC:
    case GRID_PROJECTION:
    {
        if ( gridptr->x.name[0] == 0 ) gridSetName(gridptr->x.name, "x");
        if ( gridptr->y.name[0] == 0 ) gridSetName(gridptr->y.name, "y");
        if ( gridtype == GRID_PROJECTION )
        {
            gridSetName(gridptr->mapname, "Projection");

            gridptr->x.stdname = "longitude";//xystdname_tab[grid_xystdname_projection][0];
            gridptr->y.stdname = "latitude";//xystdname_tab[grid_xystdname_projection][1];

            if ( !gridptr->x.units[0] ) gridSetName(gridptr->x.units, "m");
            if ( !gridptr->y.units[0] ) gridSetName(gridptr->y.units, "m");
        }
        break;
    }
    }
}



void gridGenXvals(int xsize, double xfirst, double xlast, double xinc, double *restrict xvals)
{
if ( (! (fabs(xinc) > 0)) && xsize > 1 )
    {
    if ( xfirst >= xlast )
        {
        while ( xfirst >= xlast ) xlast += 360;
        xinc = (xlast-xfirst)/(xsize);
        }
    else
        {
        xinc = (xlast-xfirst)/(xsize-1);
        }
    }

for ( int i = 0; i < xsize; ++i )
    xvals[i] = xfirst + i*xinc;
}

static
void calc_gaussgrid(double *restrict yvals, size_t ysize, double yfirst, double ylast)
{
double *restrict yw = (double *) Malloc(ysize * sizeof(double));
gaussaw(yvals, yw, ysize);
Free(yw);
for (size_t i = 0; i < ysize; i++ )
    yvals[i] = asin(yvals[i])/M_PI*180.0;

if ( yfirst < ylast && yfirst > -90.0 && ylast < 90.0 )
    {
    size_t yhsize = ysize/2;
    for (size_t i = 0; i < yhsize; i++ )
        {
        double ytmp = yvals[i];
        yvals[i] = yvals[ysize-i-1];
        yvals[ysize-i-1] = ytmp;
        }
    }
}


void gridGenYvals(int gridtype, int ysize, double yfirst, double ylast, double yinc, double *restrict yvals)
{
const double deleps = 0.002;

if ( gridtype == GRID_GAUSSIAN || gridtype == GRID_GAUSSIAN_REDUCED )
    {
    if ( ysize > 2 )
        {
        calc_gaussgrid(yvals, ysize, yfirst, ylast);

        if ( ! (IS_EQUAL(yfirst, 0) && IS_EQUAL(ylast, 0)) )
            if ( fabs(yvals[0] - yfirst) > deleps || fabs(yvals[ysize-1] - ylast) > deleps )
            {
                double *restrict ytmp = NULL;
                int nstart;
                bool lfound = false;
                int ny = (int) (180./(fabs(ylast-yfirst)/(ysize-1)) + 0.5);
                ny -= ny%2;
                if ( ny > ysize && ny < 4096 )
                {
                    ytmp = (double *) Malloc((size_t)ny * sizeof (double));
                    calc_gaussgrid(ytmp, ny, yfirst, ylast);
                    {
                    int i;
                    for ( i = 0; i < (ny-ysize); i++ )
                        if ( fabs(ytmp[i] - yfirst) < deleps ) break;
                    nstart = i;
                    }

                    lfound = (nstart+ysize-1) < ny
                    && fabs(ytmp[nstart+ysize-1] - ylast) < deleps;
                    if ( lfound )
                    {
                        for (int i = 0; i < ysize; i++) yvals[i] = ytmp[i+nstart];
                    }
                }

                if ( !lfound )
                {
                    Warning("Cannot calculate gaussian latitudes for lat1 = %g latn = %g!", yfirst, ylast);
                    for (int i = 0; i < ysize; i++ ) yvals[i] = 0;
                    yvals[0] = yfirst;
                    yvals[ysize-1] = ylast;
                }

                if ( ytmp ) Free(ytmp);
            }
        }
    else
        {
        yvals[0] = yfirst;
        yvals[ysize-1] = ylast;
        }
    }

else
    {
    if ( (! (fabs(yinc) > 0)) && ysize > 1 )
        {
        if ( IS_EQUAL(yfirst, ylast) && IS_NOT_EQUAL(yfirst, 0) ) ylast *= -1;

        if ( yfirst > ylast )
            yinc = (yfirst-ylast)/(ysize-1);
        else if ( yfirst < ylast )
            yinc = (ylast-yfirst)/(ysize-1);
        else
            {
            if ( ysize%2 != 0 )
                {
                yinc = 180.0/(ysize-1);
                yfirst = -90;
                }
            else
                {
                yinc = 180.0/ysize;
                yfirst = -90 + yinc/2;
                }
            }
        }

    if ( yfirst > ylast && yinc > 0 ) yinc = -yinc;

    for (int i = 0; i < ysize; i++ )
        yvals[i] = yfirst + i*yinc;
    }

}


int gridCreate(int gridtype, size_t size)
{
if ( CDI_Debug ) Message("gridtype=%s  size=%zu", gridNamePtr(gridtype), size);

gridInit();

grid_t *gridptr = gridNewEntry(CDI_UNDEFID);
if ( ! gridptr ) Error("No memory");

int gridID = gridptr->self;

if ( CDI_Debug ) Message("gridID: %d", gridID);

cdiGridTypeInit(gridptr, gridtype, size);

return gridID;
}

static
void gridDestroyKernel( grid_t * gridptr )
{
xassert ( gridptr );

int id = gridptr->self;

grid_free_components(gridptr);
Free( gridptr );

reshRemove ( id, &gridOps );
}


void gridDestroy(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
gridptr->vtable->destroy(gridptr);
}

static
void gridDestroyP(void * gridptr)
{
((grid_t *)gridptr)->vtable->destroy((grid_t *)gridptr);
}


const char *gridNamePtr(int gridtype)
{
int size = (int) (sizeof(Grids)/sizeof(Grids[0]));

const char *name = (gridtype >= 0 && gridtype < size) ? Grids[gridtype] : Grids[GRID_GENERIC];

return name;
}


void gridName(int gridtype, char *gridname)
{
strcpy(gridname, gridNamePtr(gridtype));
}

static
void *grid_key_to_ptr(grid_t *gridptr, int key)
{
void *keyptr = NULL;

switch (key)
    {
    case CDI_KEY_XNAME:      keyptr = (void*)gridptr->x.name; break;
    case CDI_KEY_XLONGNAME:  keyptr = (void*)gridptr->x.longname; break;
    case CDI_KEY_XUNITS:     keyptr = (void*)gridptr->x.units; break;
    case CDI_KEY_YNAME:      keyptr = (void*)gridptr->y.name; break;
    case CDI_KEY_YLONGNAME:  keyptr = (void*)gridptr->y.longname; break;
    case CDI_KEY_YUNITS:     keyptr = (void*)gridptr->y.units; break;
    case CDI_KEY_XDIMNAME:   keyptr = (void*)gridptr->x.dimname; break;
    case CDI_KEY_YDIMNAME:   keyptr = (void*)gridptr->y.dimname; break;
    case CDI_KEY_VDIMNAME:   keyptr = (void*)gridptr->vdimname; break;
    case CDI_KEY_MAPPING:    keyptr = (void*)gridptr->mapname; break;
    case CDI_KEY_MAPNAME:    keyptr = (void*)gridptr->mapping; break;
    }

return keyptr;
}


int cdiGridDefKeyStr(int gridID, int key, int size, const char *mesg)
{
if ( size < 1 || mesg == NULL || *mesg == 0 ) return -1;

grid_t *gridptr = grid_to_pointer(gridID);

char *keyptr = (char*)grid_key_to_ptr(gridptr, key);
if ( keyptr == NULL )
    {
    Warning("CDI grid string key %d not supported!", key);
    return -1;
    }

gridSetString(keyptr, mesg, (size_t)size);
gridMark4Update(gridID);

return 0;
}


int cdiGridInqKeyStr(int gridID, int key, int size, char *mesg)
{
if ( size < 1 || mesg == NULL ) return -1;

grid_t *gridptr = grid_to_pointer(gridID);
const char *keyptr = (const char*)grid_key_to_ptr(gridptr, key);
if ( keyptr == NULL)
    {
    Warning("CDI grid string key %d not supported!", key);
    return -1;
    }

gridGetString(mesg, keyptr, (size_t)size);

return 0;
}


void gridDefXname(int gridID, const char *xname)
{
(void)cdiGridDefKeyStr(gridID, CDI_KEY_XNAME, CDI_MAX_NAME, xname);
}


void gridDefXlongname(int gridID, const char *xlongname)
{
(void)cdiGridDefKeyStr(gridID, CDI_KEY_XLONGNAME, CDI_MAX_NAME, xlongname);
}


void gridDefXunits(int gridID, const char *xunits)
{
(void)cdiGridDefKeyStr(gridID, CDI_KEY_XUNITS, CDI_MAX_NAME, xunits);
}


void gridDefYname(int gridID, const char *yname)
{
(void)cdiGridDefKeyStr(gridID, CDI_KEY_YNAME, CDI_MAX_NAME, yname);
}


void gridDefYlongname(int gridID, const char *ylongname)
{
(void)cdiGridDefKeyStr(gridID, CDI_KEY_YLONGNAME, CDI_MAX_NAME, ylongname);
}


void gridDefYunits(int gridID, const char *yunits)
{
(void)cdiGridDefKeyStr(gridID, CDI_KEY_YUNITS, CDI_MAX_NAME, yunits);
}


void gridInqXname(int gridID, char *xname)
{
(void)cdiGridInqKeyStr(gridID, CDI_KEY_XNAME, CDI_MAX_NAME, xname);
}


void gridInqXlongname(int gridID, char *xlongname)
{
(void)cdiGridInqKeyStr(gridID, CDI_KEY_XLONGNAME, CDI_MAX_NAME, xlongname);
}


void gridInqXunits(int gridID, char *xunits)
{
(void)cdiGridInqKeyStr(gridID, CDI_KEY_XUNITS, CDI_MAX_NAME, xunits);
}


void gridInqXstdname(int gridID, char *xstdname)
{
if ( xstdname )
    {
    xstdname[0] = 0;
    grid_t *gridptr = grid_to_pointer(gridID);
    if ( gridptr->x.stdname ) strcpy(xstdname, gridptr->x.stdname);
    }
}


void gridInqYname(int gridID, char *yname)
{
(void)cdiGridInqKeyStr(gridID, CDI_KEY_YNAME, CDI_MAX_NAME, yname);
}


void gridInqYlongname(int gridID, char *ylongname)
{
(void)cdiGridInqKeyStr(gridID, CDI_KEY_YLONGNAME, CDI_MAX_NAME, ylongname);
}


void gridInqYunits(int gridID, char *yunits)
{
(void)cdiGridInqKeyStr(gridID, CDI_KEY_YUNITS, CDI_MAX_NAME, yunits);
}


void gridInqYstdname(int gridID, char *ystdname)
{
if ( ystdname )
    {
    ystdname[0] = 0;
    grid_t *gridptr = grid_to_pointer(gridID);
    if ( gridptr->y.stdname ) strcpy(ystdname, gridptr->y.stdname);
    }
}


void gridDefProj(int gridID, int projID)
{
grid_t *gridptr = grid_to_pointer(gridID);
gridptr->proj = projID;

if ( gridptr->type == GRID_CURVILINEAR )
    {
    grid_t *projptr = grid_to_pointer(projID);
    if ( projptr->x.name[0] ) strcpy(gridptr->x.dimname, projptr->x.name);
    if ( projptr->y.name[0] ) strcpy(gridptr->y.dimname, projptr->y.name);
    }
}


int gridInqProj(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->proj;
}


int gridInqProjType(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);

int projtype = gridptr->projtype;
if ( projtype == -1 )
    {
    char mapping[CDI_MAX_NAME]; mapping[0] = 0;
    cdiGridInqKeyStr(gridID, CDI_KEY_MAPNAME, CDI_MAX_NAME, mapping);
    if ( mapping[0] )
        {
        if      ( strcmp(mapping, "rotated_latitude_longitude") == 0 )   projtype = CDI_PROJ_RLL;
        else if ( strcmp(mapping, "lambert_azimuthal_equal_area") == 0 ) projtype = CDI_PROJ_LAEA;
        else if ( strcmp(mapping, "lambert_conformal_conic") == 0 )      projtype = CDI_PROJ_LCC;
        else if ( strcmp(mapping, "sinusoidal") == 0 )                   projtype = CDI_PROJ_SINU;
        else if ( strcmp(mapping, "polar_stereographic") == 0 )          projtype = CDI_PROJ_STERE;

        gridptr->projtype = projtype;
        }
    }

return projtype;
}


void gridVerifyProj(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);

int projtype = gridInqProjType(gridID);

if ( projtype == CDI_PROJ_RLL )
    {
    gridptr->x.stdname = "longitude";//xystdname_tab[grid_xystdname_grid_latlon][0];
    gridptr->y.stdname = "latitude";//xystdname_tab[grid_xystdname_grid_latlon][1];
    gridSetName(gridptr->x.units, "degrees");
    gridSetName(gridptr->y.units, "degrees");
    }
else if ( projtype == CDI_PROJ_LCC )
    {
    gridptr->x.stdname = "longitude";//xystdname_tab[grid_xystdname_projection][0];
    gridptr->y.stdname = "latitude";//xystdname_tab[grid_xystdname_projection][1];
    if ( !gridptr->x.units[0] ) gridSetName(gridptr->x.units, "m");
    if ( !gridptr->y.units[0] ) gridSetName(gridptr->y.units, "m");
    }
}


int gridInqType(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);

return gridptr->type;
}



size_t gridInqSize(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);

size_t size = gridptr->size;

if ( size == 0 )
    {
    size_t xsize = gridptr->x.size;
    size_t ysize = gridptr->y.size;

    size = ysize ? xsize * ysize : xsize;

    gridptr->size = size;
    }

return size;
}

static
int nsp2trunc(int nsp)
{






int trunc = (int) (sqrt(nsp*4 + 1.) - 3) / 2;
return trunc;
}


int gridInqTrunc(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( gridptr->trunc == 0 )
    {
    if ( gridptr->type == GRID_SPECTRAL )
        gridptr->trunc = nsp2trunc(gridptr->size);

    }

return gridptr->trunc;
}


void gridDefTrunc(int gridID, int trunc)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( gridptr->trunc != trunc )
    {
    gridMark4Update(gridID);
    gridptr->trunc = trunc;
    }
}


void gridDefXsize(int gridID, size_t xsize)
{
grid_t *gridptr = grid_to_pointer(gridID);

size_t gridSize = gridInqSize(gridID);
if ( xsize > gridSize )
    Error("xsize %zu is greater then gridsize %zu", xsize, gridSize);

int gridType = gridInqType(gridID);
if ( gridType == GRID_UNSTRUCTURED && xsize != gridSize )
    Error("xsize %zu must be equal to gridsize %zu for gridtype: UNSTRUCTURED", xsize, gridSize);

if ( gridptr->x.size != xsize )
    {
    gridMark4Update(gridID);
    gridptr->x.size = xsize;
    }

if ( gridType != GRID_UNSTRUCTURED && gridType != GRID_PROJECTION )
    {
    size_t axisproduct = gridptr->x.size*gridptr->y.size;
    if ( axisproduct > 0 && axisproduct != gridSize )
        Error("Inconsistent grid declaration! (xsize=%zu ysize=%zu gridsize=%zu)",
            gridptr->x.size, gridptr->y.size, gridSize);
    }
}


void gridDefDatatype(int gridID, int datatype)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( gridptr->datatype != datatype )
    {
    gridMark4Update(gridID);
    gridptr->datatype = datatype;
    }
}


int gridInqDatatype(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->datatype;
}


size_t gridInqXsize(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->x.size;
}


void gridDefYsize(int gridID, size_t ysize)
{
grid_t *gridptr = grid_to_pointer(gridID);

size_t gridSize = gridInqSize(gridID);

if ( ysize > gridSize )
    Error("ysize %zu is greater then gridsize %zu", ysize, gridSize);

int gridType = gridInqType(gridID);
if ( gridType == GRID_UNSTRUCTURED && ysize != gridSize )
    Error("ysize %zu must be equal gridsize %zu for gridtype: UNSTRUCTURED", ysize, gridSize);

if ( gridptr->y.size != ysize )
    {
    gridMark4Update(gridID);
    gridptr->y.size = ysize;
    }

if ( gridType != GRID_UNSTRUCTURED && gridType != GRID_PROJECTION )
    {
    size_t axisproduct = gridptr->x.size*gridptr->y.size;
    if ( axisproduct > 0 && axisproduct != gridSize )
        Error("Inconsistent grid declaration! (xsize=%zu ysize=%zu gridsize=%zu)",
            gridptr->x.size, gridptr->y.size, gridSize);
    }
}


size_t gridInqYsize(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->y.size;
}


void gridDefNP(int gridID, int np)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( gridptr->np != np )
    {
    gridMark4Update(gridID);
    gridptr->np = np;
    }
}


int gridInqNP(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->np;
}


void gridDefRowlon(int gridID, int nrowlon, const int rowlon[])
{
grid_t *gridptr = grid_to_pointer(gridID);

gridptr->rowlon = (int *) Malloc((size_t)nrowlon * sizeof(int));
gridptr->nrowlon = nrowlon;
memcpy(gridptr->rowlon, rowlon, (size_t)nrowlon * sizeof(int));
gridMark4Update(gridID);
}


void gridInqRowlon(int gridID, int *rowlon)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( gridptr->rowlon == 0 )  Error("undefined pointer!");

memcpy(rowlon, gridptr->rowlon, (size_t)gridptr->nrowlon * sizeof(int));
}

static size_t
gridInqMaskSerialGeneric(grid_t *gridptr, mask_t **internalMask,
                        int *restrict mask)
{
size_t size = gridptr->size;

if ( CDI_Debug && size == 0 )
    Warning("Size undefined for gridID = %d", gridptr->self);

const mask_t *restrict mask_src = *internalMask;
if ( mask_src )
    {
    if (mask && size > 0)
        for (size_t i = 0; i < size; ++i)
        mask[i] = (int)mask_src[i];
    }
else
    size = 0;

return size;
}

static size_t
gridInqMaskSerial(grid_t *gridptr, int *mask)
{
return gridInqMaskSerialGeneric(gridptr, &gridptr->mask, mask);
}


int gridInqMask(int gridID, int *mask)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqMask(gridptr, mask);
}

static void
gridDefMaskSerial(grid_t *gridptr, const int *mask)
{
size_t size = gridptr->size;

if ( size == 0 )
    Error("Size undefined for gridID = %d", gridptr->self);

if ( mask == NULL )
    {
    if ( gridptr->mask )
        {
        Free(gridptr->mask);
        gridptr->mask = NULL;
        }
    }
else
    {
    if ( gridptr->mask == NULL )
        gridptr->mask = (mask_t *) Malloc(size*sizeof(mask_t));
    else if ( CDI_Debug )
        Warning("grid mask already defined!");

    for (size_t i = 0; i < size; ++i )
        gridptr->mask[i] = (mask_t)(mask[i] != 0);
    }
}

void gridDefMask(int gridID, const int *mask)
{
grid_t *gridptr = grid_to_pointer(gridID);
gridptr->vtable->defMask(gridptr, mask);
gridMark4Update(gridID);
}

static int
gridInqMaskGMESerial(grid_t *gridptr, int *mask_gme)
{
return gridInqMaskSerialGeneric(gridptr, &gridptr->mask_gme, mask_gme);
}

int gridInqMaskGME(int gridID, int *mask)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqMaskGME(gridptr, mask);
}

static void
gridDefMaskGMESerial(grid_t *gridptr, const int *mask)
{
size_t size = gridptr->size;

if ( size == 0 )
    Error("Size undefined for gridID = %d", gridptr->self);

if ( gridptr->mask_gme == NULL )
    gridptr->mask_gme = (mask_t *) Malloc(size * sizeof (mask_t));
else if ( CDI_Debug )
    Warning("mask already defined!");

for (size_t i = 0; i < size; ++i)
    gridptr->mask_gme[i] = (mask_t)(mask[i] != 0);
}

void gridDefMaskGME(int gridID, const int *mask)
{
grid_t *gridptr = grid_to_pointer(gridID);
gridptr->vtable->defMaskGME(gridptr, mask);
gridMark4Update(gridID);
}

static
size_t gridInqXValsSerial(grid_t *gridptr, double *xvals)
{
size_t size;
if ( gridptr->type == GRID_CURVILINEAR || gridptr->type == GRID_UNSTRUCTURED )
    size = gridptr->size;
else if ( gridptr->type == GRID_GAUSSIAN_REDUCED )
    size = 2;
else
    size = gridptr->x.size;

if ( CDI_Debug && size == 0 )
    Warning("size undefined for gridID = %d", gridptr->self);

if ( gridptr->x.vals )
    {
    if ( size && xvals )
        {
        const double *gridptr_xvals = gridptr->vtable->inqXValsPtr(gridptr);
        memcpy(xvals, gridptr_xvals, size * sizeof (double));
        }
    }
else
    size = 0;

return size;
}

static
size_t gridInqXValsPartSerial(grid_t *gridptr, int start, size_t length, double *xvals)
{
size_t size;
if ( gridptr->type == GRID_CURVILINEAR || gridptr->type == GRID_UNSTRUCTURED )
    size = gridptr->size;
else if ( gridptr->type == GRID_GAUSSIAN_REDUCED )
    size = 2;
else
    size = gridptr->x.size;

if ( CDI_Debug && size == 0 )
    Warning("size undefined for gridID = %d", gridptr->self);

if ( gridptr->x.vals )
    {
    if ( size && xvals && length <= size )
        {
        const double *gridptr_xvals = gridptr->vtable->inqXValsPtr(gridptr);
        memcpy(xvals, gridptr_xvals+start, length * sizeof (double));
        }
    }
else
    size = 0;

return size;
}

static
size_t gridInqXCvalsSerial(grid_t *gridptr, char **xcvals)
{
if ( gridptr->type != GRID_CHARXY )
    Error("Function only valid for grid type 'GRID_CHARXY'.");

size_t size = gridptr->x.size;
size_t maxclength = 0;

const char **gridptr_xcvals = gridptr->vtable->inqXCvalsPtr(gridptr);
if ( gridptr_xcvals && size && xcvals )
    {
    maxclength = gridptr->x.clength;
    for ( size_t i = 0; i < size; i++ )
        memcpy(xcvals[i], gridptr_xcvals[i], maxclength*sizeof(char));
    }

return maxclength;
}

static
int gridInqXIscSerial(grid_t *gridptr)
{
int clen = gridptr->x.clength;

return clen;
}


size_t gridInqXvals(int gridID, double *xvals)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqXVals(gridptr, xvals);
}


size_t gridInqXvalsPart(int gridID, int start, size_t length, double *xvals)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqXValsPart(gridptr, start, length, xvals);
}


size_t gridInqXCvals(int gridID, char **xcvals)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqXCvals(gridptr, xcvals);
}


int gridInqXIsc(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqXIsc(gridptr);
}

static
void gridDefXValsSerial(grid_t *gridptr, const double *xvals)
{
int gridtype = gridptr->type;

size_t size;
if ( gridtype == GRID_UNSTRUCTURED || gridtype == GRID_CURVILINEAR )
    size = gridptr->size;
else if ( gridtype == GRID_GAUSSIAN_REDUCED )
    size = 2;
else
    size = gridptr->x.size;

if ( size == 0 )
    Error("Size undefined for gridID = %d", gridptr->self);

if (gridptr->x.vals && CDI_Debug)
    Warning("values already defined!");
gridptr->x.vals = (double *)Realloc(gridptr->x.vals,
                                    size * sizeof(double));
memcpy(gridptr->x.vals, xvals, size * sizeof (double));
}

static
size_t gridInqYCvalsSerial(grid_t *gridptr, char **ycvals)
{
if ( gridptr->type != GRID_CHARXY )
    Error("Function only valid for grid type 'GRID_CHARXY'.");

size_t size = gridptr->y.size;
size_t maxclength = 0;

const char **gridptr_ycvals = gridptr->vtable->inqYCvalsPtr(gridptr);
if ( gridptr_ycvals && size && ycvals )
    {
    maxclength = gridptr->y.clength;
    for ( size_t i = 0; i < size; i++ )
        memcpy(ycvals[i], gridptr_ycvals[i], maxclength*sizeof(char));
    }

return maxclength;
}

static
int gridInqYIscSerial(grid_t *gridptr)
{
int clen = gridptr->y.clength;

return clen;
}


void gridDefXvals(int gridID, const double *xvals)
{
grid_t *gridptr = grid_to_pointer(gridID);
gridptr->vtable->defXVals(gridptr, xvals);
gridMark4Update(gridID);
}

static
size_t gridInqYValsSerial(grid_t *gridptr, double *yvals)
{
int gridtype = gridptr->type;
size_t size = (gridtype == GRID_CURVILINEAR || gridtype == GRID_UNSTRUCTURED)
            ? gridptr->size : gridptr->y.size;

if ( CDI_Debug && size == 0 )
    Warning("size undefined for gridID = %d!", gridptr->self);

if ( gridptr->y.vals )
    {
    if ( size && yvals )
        {
        const double *gridptr_yvals = gridptr->vtable->inqYValsPtr(gridptr);
        memcpy(yvals, gridptr_yvals, size * sizeof (double));
        }
    }
else
    size = 0;

return size;
}

static
size_t gridInqYValsPartSerial(grid_t *gridptr, int start, size_t length, double *yvals)
{
int gridtype = gridptr->type;
size_t size = (gridtype == GRID_CURVILINEAR || gridtype == GRID_UNSTRUCTURED)
            ? gridptr->size : gridptr->y.size;

if ( CDI_Debug && size == 0 )
    Warning("size undefined for gridID = %d!", gridptr->self);

if ( gridptr->y.vals )
    {
    if ( size && yvals && length <= size )
        {
        const double *gridptr_yvals = gridptr->vtable->inqYValsPtr(gridptr);
        memcpy(yvals, gridptr_yvals+start, length * sizeof (double));
        }
    }
else
    size = 0;

return size;
}


size_t gridInqYvals(int gridID, double *yvals)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqYVals(gridptr, yvals);
}


size_t gridInqYvalsPart(int gridID, int start, size_t size, double *yvals)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqYValsPart(gridptr, start, size, yvals);
}


size_t gridInqYCvals(int gridID, char **ycvals)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqYCvals(gridptr, ycvals);
}


int gridInqYIsc(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqYIsc(gridptr);
}

static
void gridDefYValsSerial(grid_t *gridptr, const double *yvals)
{
int gridtype = gridptr->type;
size_t size = (gridtype == GRID_CURVILINEAR || gridtype == GRID_UNSTRUCTURED)
            ? gridptr->size : gridptr->y.size;

if ( size == 0 )
    Error("Size undefined for gridID = %d!", gridptr->self);

if ( gridptr->y.vals && CDI_Debug )
    Warning("Values already defined!");

gridptr->y.vals = (double *)Realloc(gridptr->y.vals, size * sizeof (double));
memcpy(gridptr->y.vals, yvals, size * sizeof (double));
}



void gridDefYvals(int gridID, const double *yvals)
{
grid_t *gridptr = grid_to_pointer(gridID);
gridptr->vtable->defYVals(gridptr, yvals);
gridMark4Update(gridID);
}

static double
gridInqXValSerial(grid_t *gridptr, size_t index)
{
double xval = gridptr->x.vals ? gridptr->x.vals[index] : 0;
return xval;
}


double gridInqXval(int gridID, size_t index)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqXVal(gridptr, index);
}

static double
gridInqYValSerial(grid_t *gridptr, size_t index)
{
double yval = gridptr->y.vals ? gridptr->y.vals[index] : 0;
return yval;
}


double gridInqYval(int gridID, size_t index)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqYVal(gridptr, index);
}

static
double grid_calc_increment(size_t size, const double *vals)
{
if ( size > 1 )
    {
    double inc = (vals[size-1] - vals[0])/(size-1);
    double abs_inc = fabs(inc);
    for ( size_t i = 1; i < size; ++i )
        if ( fabs(fabs(vals[i-1] - vals[i]) - abs_inc) > 0.01*abs_inc )
        {
            inc = 0;
            break;
        }

    return inc;
    }

return 0;
}


double gridInqXinc(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
const double *restrict xvals = gridptr->vtable->inqXValsPtr(gridptr);

if ( (! (fabs(gridptr->x.inc) > 0)) && xvals )
    {
    size_t xsize = gridptr->x.size;
    if ( xsize > 1 ) gridptr->x.inc = grid_calc_increment(xsize, xvals);
    }

return gridptr->x.inc;
}


double gridInqYinc(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
const double *yvals = gridptr->vtable->inqYValsPtr(gridptr);

if ( (! (fabs(gridptr->y.inc) > 0)) && yvals )
    {
    size_t ysize = gridptr->y.size;
    if ( ysize > 1 ) gridptr->y.inc = grid_calc_increment(ysize, yvals);
    }

return gridptr->y.inc;
}


void gridInqParamRLL(int gridID, double *xpole, double *ypole, double *angle)
{
*xpole = 0; *ypole = 0; *angle = 0;

const char *projection = "rotated_latitude_longitude";
char mapping[CDI_MAX_NAME]; mapping[0] = 0;
cdiGridInqKeyStr(gridID, CDI_KEY_MAPNAME, CDI_MAX_NAME, mapping);
if ( mapping[0] && strcmp(mapping, projection) == 0 )
    {
    int atttype, attlen;
    char attname[CDI_MAX_NAME+1];

    int natts;
    cdiInqNatts(gridID, CDI_GLOBAL, &natts);

    for ( int iatt = 0; iatt < natts; ++iatt )
        {
        cdiInqAtt(gridID, CDI_GLOBAL, iatt, attname, &atttype, &attlen);
        if ( attlen != 1 ) continue;

        double attflt;
        if ( cdiInqAttConvertedToFloat(gridID, atttype, attname, attlen, &attflt) )
            {
            if      ( strcmp(attname, "grid_north_pole_longitude") == 0 ) *xpole = attflt;
            else if ( strcmp(attname, "grid_north_pole_latitude")  == 0 ) *ypole = attflt;
            else if ( strcmp(attname, "north_pole_grid_longitude") == 0 ) *angle = attflt;
            }
        }
    }
else
    Warning("%s mapping parameter missing!", projection);
}


void gridDefParamRLL(int gridID, double xpole, double ypole, double angle)
{
cdiGridDefKeyStr(gridID, CDI_KEY_MAPPING, CDI_MAX_NAME, "rotated_pole");

const char *mapping = "rotated_latitude_longitude";
cdiGridDefKeyStr(gridID, CDI_KEY_MAPNAME, CDI_MAX_NAME, mapping);
cdiDefAttTxt(gridID, CDI_GLOBAL, "grid_mapping_name", (int)(strlen(mapping)), mapping);
cdiDefAttFlt(gridID, CDI_GLOBAL, "grid_north_pole_longitude", CDI_DATATYPE_FLT64, 1, &xpole);
cdiDefAttFlt(gridID, CDI_GLOBAL, "grid_north_pole_latitude", CDI_DATATYPE_FLT64, 1, &ypole);
if ( IS_NOT_EQUAL(angle, 0) ) cdiDefAttFlt(gridID, CDI_GLOBAL, "north_pole_grid_longitude", CDI_DATATYPE_FLT64, 1, &angle);

grid_t *gridptr = grid_to_pointer(gridID);
gridptr->projtype = CDI_PROJ_RLL;

gridVerifyProj(gridID);
}


void gridInqParamGME(int gridID, int *nd, int *ni, int *ni2, int *ni3)
{
grid_t *gridptr = grid_to_pointer(gridID);

*nd  = gridptr->gme.nd;
*ni  = gridptr->gme.ni;
*ni2 = gridptr->gme.ni2;
*ni3 = gridptr->gme.ni3;
}


void gridDefParamGME(int gridID, int nd, int ni, int ni2, int ni3)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( gridptr->gme.nd != nd )
    {
    gridptr->gme.nd  = nd;
    gridptr->gme.ni  = ni;
    gridptr->gme.ni2 = ni2;
    gridptr->gme.ni3 = ni3;
    gridMark4Update(gridID);
    }
}


void gridChangeType(int gridID, int gridtype)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( CDI_Debug )
    Message("Changed grid type from %s to %s", gridNamePtr(gridptr->type), gridNamePtr(gridtype));

if (gridptr->type != gridtype)
    {
    gridptr->type = gridtype;
    gridMark4Update(gridID);
    }
}

static
void grid_check_cyclic(grid_t *gridptr)
{
gridptr->isCyclic = 0;
enum { numVertices = 4 };
size_t xsize = gridptr->x.size,
        ysize = gridptr->y.size;
const double *xvals = gridptr->vtable->inqXValsPtr(gridptr),
            *yvals = gridptr->vtable->inqYValsPtr(gridptr),
    (*xbounds)[numVertices]
    = (const double (*)[numVertices])gridptr->vtable->inqXBoundsPtr(gridptr);

if ( gridptr->type == GRID_GAUSSIAN || gridptr->type == GRID_LONLAT )
    {
    if ( xvals && xsize > 1 )
        {
        double xval1 = xvals[0];
        double xval2 = xvals[1];
        double xvaln = xvals[xsize-1];
        if ( xval2 < xval1 ) xval2 += 360;
        if ( xvaln < xval1 ) xvaln += 360;

        if ( IS_NOT_EQUAL(xval1, xvaln) )
            {
            double xinc = xval2 - xval1;
            if ( IS_EQUAL(xinc, 0) ) xinc = (xvaln - xval1)/(xsize-1);

            double x0 = xvaln + xinc - 360;

            if ( fabs(x0 - xval1) < 0.01*xinc ) gridptr->isCyclic = 1;
            }
        }
    }
else if ( gridptr->type == GRID_CURVILINEAR )
    {
    bool lcheck = true;
    if ( yvals && xvals )
        {
        if ( (fabs(yvals[0] - yvals[xsize-1]) > fabs(yvals[0] - yvals[xsize*ysize-xsize])) &&
            (fabs(yvals[xsize*ysize-xsize] - yvals[xsize*ysize-1]) > fabs(yvals[xsize-1] - yvals[xsize*ysize-1])) )
            lcheck = false;
        }
    else lcheck = false;

    if ( lcheck && xvals && xsize > 1 )
        {
        size_t nc = 0;
        for ( size_t j = 0; j < ysize; ++j )
            {
            size_t i1 = j*xsize,
                    i2 = j*xsize+1,
                    in = j*xsize+(xsize-1);
            double val1 = xvals[i1],
                    val2 = xvals[i2],
                    valn = xvals[in];
            double xinc = fabs(val2-val1);

            if ( val1 <    1 && valn > 300 ) val1 += 360;
            if ( valn <    1 && val1 > 300 ) valn += 360;
            if ( val1 < -179 && valn > 120 ) val1 += 360;
            if ( valn < -179 && val1 > 120 ) valn += 360;
            if ( fabs(valn-val1) > 180 ) val1 += 360;

            double x0 = valn + copysign(xinc, val1 - valn);

            nc += fabs(x0-val1) < 0.5*xinc;
            }
        gridptr->isCyclic = nc > ysize/2;
        }

    if ( lcheck && xbounds && xsize > 1 )
        {
        bool isCyclic = true;
        for ( size_t j = 0; j < ysize; ++j )
            {
            size_t i1 = j*xsize,
                    i2 = j*xsize+(xsize-1);
            for (size_t k1 = 0; k1 < numVertices; ++k1 )
                {
                double val1 = xbounds[i1][k1];
                for (size_t k2 = 0; k2 < numVertices; ++k2 )
                    {
                    double val2 = xbounds[i2][k2];

                    if ( val1 <    1 && val2 > 300 ) val1 += 360;
                    if ( val2 <    1 && val1 > 300 ) val2 += 360;
                    if ( val1 < -179 && val2 > 120 ) val1 += 360;
                    if ( val2 < -179 && val1 > 120 ) val2 += 360;
                    if ( fabs(val2-val1) > 180 ) val1 += 360;

                    if ( fabs(val1-val2) < 0.001 )
                        goto foundCloseVertices;
                    }
                }

            isCyclic = false;
            break;
            foundCloseVertices:
            ;
            }
        gridptr->isCyclic = isCyclic;
        }
    }
}


int gridIsCircular(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( gridptr->isCyclic == CDI_UNDEFID ) grid_check_cyclic(gridptr);

return gridptr->isCyclic;
}

static
bool compareXYvals(grid_t *gridRef, grid_t *gridTest)
{
bool differ = false;

size_t xsizeTest = gridTest->x.size, ysizeTest = gridTest->y.size;
if ( !differ && xsizeTest > 0 && xsizeTest == gridRef->vtable->inqXVals(gridRef, NULL) )
    {
    const double *restrict xvalsRef = gridRef->vtable->inqXValsPtr(gridRef),
        *restrict xvalsTest = gridTest->vtable->inqXValsPtr(gridTest);

    for ( size_t i = 0; i < xsizeTest; ++i )
        if ( fabs(xvalsTest[i] - xvalsRef[i]) > 1.e-10 )
        {
            differ = true;
            break;
        }
    }

if ( !differ && ysizeTest > 0 && ysizeTest == gridRef->vtable->inqYVals(gridRef, NULL) )
    {
    const double *restrict yvalsRef = gridRef->vtable->inqYValsPtr(gridRef),
        *restrict yvalsTest = gridTest->vtable->inqYValsPtr(gridTest);
    for ( size_t i = 0; i < ysizeTest; ++i )
        if ( fabs(yvalsTest[i] - yvalsRef[i]) > 1.e-10 )
        {
            differ = true;
            break;
        }
    }

return differ;
}

static
bool compareXYvals2(grid_t *gridRef, grid_t *gridTest)
{
size_t gridsize = gridTest->size;
bool differ = ((gridTest->x.vals == NULL) ^ (gridRef->x.vals == NULL))
            || ((gridTest->y.vals == NULL) ^ (gridRef->y.vals == NULL))
            || ((gridTest->x.bounds == NULL) ^ (gridRef->x.bounds == NULL))
            || ((gridTest->y.bounds == NULL) ^ (gridRef->y.bounds == NULL));

typedef double (*inqVal)(grid_t *grid, size_t index);
inqVal inqXValRef = gridRef->vtable->inqXVal,
        inqYValRef = gridRef->vtable->inqYVal,
        inqXValTest = gridTest->vtable->inqXVal,
        inqYValTest = gridTest->vtable->inqYVal;

if ( !differ && gridTest->x.vals )
    differ = fabs(inqXValTest(gridTest, 0) - inqXValRef(gridRef, 0)) > 1.e-9
        || fabs(inqXValTest(gridTest, gridsize-1) - inqXValRef(gridRef, gridsize-1)) > 1.e-9;

if ( !differ && gridTest->y.vals )
    differ = fabs(inqYValTest(gridTest, 0) - inqYValRef(gridRef, 0)) > 1.e-9
        || fabs(inqYValTest(gridTest, gridsize-1) - inqYValRef(gridRef, gridsize-1)) > 1.e-9;

return differ;
}

static
bool gridCompare(int gridID, const grid_t *grid, bool coord_compare)
{
bool differ = true;
const grid_t *gridRef = grid_to_pointer(gridID);

if ( grid->type == gridRef->type || grid->type == GRID_GENERIC )
    {
    if ( grid->size == gridRef->size )
        {
        differ = false;
        if ( grid->type == GRID_LONLAT || grid->type == GRID_PROJECTION )
            {

            if ( grid->x.size == gridRef->x.size && grid->y.size == gridRef->y.size )
                {
                if ( grid->x.flag == 2 && grid->y.flag == 2 )
                    {
                    if ( ! (IS_EQUAL(grid->x.first, 0) && IS_EQUAL(grid->x.last, 0) && IS_EQUAL(grid->x.inc, 0)) &&
                        ! (IS_EQUAL(grid->y.first, 0) && IS_EQUAL(grid->y.last, 0) && IS_EQUAL(grid->y.inc, 0)) &&
                        IS_NOT_EQUAL(grid->x.first, grid->x.last) && IS_NOT_EQUAL(grid->y.first, grid->y.last) )
                        {
                        if ( IS_NOT_EQUAL(grid->x.first, gridInqXval(gridID, 0)) ||
                            IS_NOT_EQUAL(grid->y.first, gridInqYval(gridID, 0)))
                            {
                            differ = true;
                            }
                        if ( !differ && fabs(grid->x.inc) > 0 &&
                            fabs(fabs(grid->x.inc) - fabs(gridRef->x.inc)) > fabs(grid->x.inc/1000))
                            {
                            differ = true;
                            }
                        if ( !differ && fabs(grid->y.inc) > 0 &&
                            fabs(fabs(grid->y.inc) - fabs(gridRef->y.inc)) > fabs(grid->y.inc/1000))
                            {
                            differ = true;
                            }
                        }
                    }
                else if ( grid->x.vals && grid->y.vals )
                    differ = gridRef->vtable->compareXYFull((grid_t *)gridRef, (grid_t *)grid);
                }
            else
                differ = true;
            }
        else if ( grid->type == GRID_GENERIC )
            {
            if ( grid->x.size == gridRef->x.size && grid->y.size == gridRef->y.size )
                {
                if ( grid->x.flag == 1 && grid->y.flag == 1
                    && grid->x.vals && grid->y.vals )
                    differ = gridRef->vtable->compareXYFull((grid_t *)gridRef, (grid_t *)grid);
                }
            else if ( (grid->y.size == 0 || grid->y.size == 1) &&
                        grid->x.size == gridRef->x.size*gridRef->y.size )
                {
                }
            else
                differ = true;
            }
        else if ( grid->type == GRID_GAUSSIAN )
            {
            if ( grid->x.size == gridRef->x.size && grid->y.size == gridRef->y.size )
                {
                if ( grid->x.flag == 2 && grid->y.flag == 2 )
                    {
                    if ( ! (IS_EQUAL(grid->x.first, 0) && IS_EQUAL(grid->x.last, 0) && IS_EQUAL(grid->x.inc, 0)) &&
                        ! (IS_EQUAL(grid->y.first, 0) && IS_EQUAL(grid->y.last, 0)) )
                        if ( fabs(grid->x.first - gridInqXval(gridID, 0)) > 0.0015 ||
                            fabs(grid->y.first - gridInqYval(gridID, 0)) > 0.0015 ||
                            (fabs(grid->x.inc)>0 && fabs(fabs(grid->x.inc) - fabs(gridRef->x.inc)) > fabs(grid->x.inc/1000)) )
                        {
                            differ = true;
                        }
                    }
                else if ( grid->x.vals && grid->y.vals )
                    differ = gridRef->vtable->compareXYFull((grid_t *)gridRef, (grid_t *)grid);
                }
            else
                differ = true;
            }
        else if ( grid->type == GRID_CURVILINEAR )
            {

            if ( grid->x.size == gridRef->x.size && grid->y.size == gridRef->y.size )
                differ = gridRef->vtable->compareXYAO((grid_t *)gridRef, (grid_t *)grid);
            }
        else if ( grid->type == GRID_UNSTRUCTURED )
            {
            if ( coord_compare )
                {
                differ = grid->nvertex != gridRef->nvertex
                    || gridRef->vtable->compareXYAO((grid_t *)gridRef, (grid_t *)grid);
                }
            else
                {

                differ |= ((gridRef->uuid[0] || grid->uuid[0]) && memcmp(gridRef->uuid, grid->uuid, CDI_UUID_SIZE));

                if ( !differ &&
                    ((grid->x.vals == NULL) ^ (gridRef->x.vals == NULL)) &&
                    ((grid->y.vals == NULL) ^ (gridRef->y.vals == NULL)) )
                    {
                    int nvertexA, nvertexB, numberA, numberB;
                    differ = ( (nvertexA = grid->nvertex)
                                && (nvertexB = gridRef->nvertex)
                                && (nvertexA != nvertexB) )
                        || (numberA = grid->number, numberB = gridRef->number,
                            ( (numberA)
                            && numberB
                            && (numberA != numberB) )
                            || ( (numberA && numberB)
                                && (grid->position) != (gridRef->position) ) );
                    }
                else if ( !differ )
                    {
                    differ = grid->nvertex != gridRef->nvertex
                        || grid->number != gridRef->number
                        || (grid->number > 0 && grid->position != gridRef->position)
                        || gridRef->vtable->compareXYAO((grid_t *)gridRef, (grid_t *)grid);
                    }
                }
            }
        }
    }

if ( (grid->scanningMode != gridInqScanningMode(gridID)) || (grid->uvRelativeToGrid != gridInqUvRelativeToGrid(gridID)) )
    {

    differ = 1;
#ifdef HIRLAM_EXTENSIONS
    if ( cdiDebugExt>=200 )
        printf("gridCompare(gridID=%d): Differs: grid.scanningMode [%d] != gridInqScanningMode(gridID) [%d] or  grid.uvRelativeToGrid [%d] != gridInqUvRelativeToGrid(gridID) [%d]\n",
            gridID, grid->scanningMode, gridInqScanningMode(gridID), grid->uvRelativeToGrid, gridInqUvRelativeToGrid(gridID) );
#endif
    }

return differ;
}



int gridCompareP(void *gridptr1, void *gridptr2)
{
grid_t *g1 = ( grid_t * ) gridptr1;
grid_t *g2 = ( grid_t * ) gridptr2;
enum { equal = 0,
        differ = -1 };
size_t i, size;

xassert ( g1 );
xassert ( g2 );

if ( g1->type          != g2->type         ) return differ;
if ( g1->datatype      != g2->datatype     ) return differ;
if ( g1->isCyclic      != g2->isCyclic     ) return differ;
if ( g1->x.flag        != g2->x.flag       ) return differ;
if ( g1->y.flag        != g2->y.flag       ) return differ;
if ( g1->gme.nd        != g2->gme.nd       ) return differ;
if ( g1->gme.ni        != g2->gme.ni       ) return differ;
if ( g1->gme.ni2       != g2->gme.ni2      ) return differ;
if ( g1->gme.ni3       != g2->gme.ni3      ) return differ;
if ( g1->number        != g2->number       ) return differ;
if ( g1->position      != g2->position     ) return differ;
if ( g1->trunc         != g2->trunc        ) return differ;
if ( g1->nvertex       != g2->nvertex      ) return differ;
if ( g1->nrowlon       != g2->nrowlon      ) return differ;
if ( g1->size          != g2->size         ) return differ;
if ( g1->x.size        != g2->x.size       ) return differ;
if ( g1->y.size        != g2->y.size       ) return differ;
if ( g1->lcomplex      != g2->lcomplex     ) return differ;

if ( IS_NOT_EQUAL(g1->x.first       , g2->x.first)       ) return differ;
if ( IS_NOT_EQUAL(g1->y.first	      , g2->y.first)       ) return differ;
if ( IS_NOT_EQUAL(g1->x.last        , g2->x.last)        ) return differ;
if ( IS_NOT_EQUAL(g1->y.last        , g2->y.last)        ) return differ;
if ( IS_NOT_EQUAL(g1->x.inc	      , g2->x.inc)         ) return differ;
if ( IS_NOT_EQUAL(g1->y.inc	      , g2->y.inc)         ) return differ;
if ( IS_NOT_EQUAL(g1->uvRelativeToGrid     , g2->uvRelativeToGrid)     ) return differ;
if ( IS_NOT_EQUAL(g1->scanningMode         , g2->scanningMode)         ) return differ;

const double *restrict g1_xvals = g1->vtable->inqXValsPtr(g1),
            *restrict g2_xvals = g2->vtable->inqXValsPtr(g2);
if ( g1_xvals )
    {
    if ( g1->type == GRID_UNSTRUCTURED || g1->type == GRID_CURVILINEAR )
        size = g1->size;
    else
        size = g1->x.size;
    xassert ( size );

    if ( !g2_xvals ) return differ;

    for ( i = 0; i < size; i++ )
        if ( IS_NOT_EQUAL(g1_xvals[i], g2_xvals[i]) ) return differ;
    }
else if ( g2_xvals )
    return differ;

const double *restrict g1_yvals = g1->vtable->inqYValsPtr(g1),
            *restrict g2_yvals = g2->vtable->inqYValsPtr(g2);
if ( g1_yvals )
    {
    if ( g1->type == GRID_UNSTRUCTURED || g1->type == GRID_CURVILINEAR )
        size = g1->size;
    else
        size = g1->y.size;
    xassert ( size );

    if ( !g2_yvals ) return differ;

    for ( i = 0; i < size; i++ )
        if ( IS_NOT_EQUAL(g1_yvals[i], g2_yvals[i]) ) return differ;
    }
else if ( g2_yvals )
    return differ;

const double *restrict g1_area = g1->vtable->inqAreaPtr(g1),
            *restrict g2_area = g2->vtable->inqAreaPtr(g2);
if ( g1_area )
    {
    xassert ( g1->size );

    if ( !g2_area ) return differ;

    for ( i = 0; i < g1->size; i++ )
        if ( IS_NOT_EQUAL(g1_area[i], g2_area[i]) ) return differ;
    }
else if ( g2_area )
    return differ;

{
    const double *restrict g1_xbounds, *restrict g2_xbounds;
    if ( (g1_xbounds = g1->vtable->inqXBoundsPtr(g1)) )
    {
        xassert ( g1->nvertex );
        if ( g1->type == GRID_CURVILINEAR || g1->type == GRID_UNSTRUCTURED )
        size = g1->nvertex * g1->size;
        else
        size = g1->nvertex * g1->x.size;
        xassert ( size );

        if ( !(g2_xbounds = g2->vtable->inqXBoundsPtr(g2)) ) return differ;

        for ( i = 0; i < size; i++ )
        if ( IS_NOT_EQUAL(g1_xbounds[i], g2_xbounds[i]) ) return differ;
    }
    else if ( g2->vtable->inqXBoundsPtr(g2) )
    return differ;
}

{
    const double *restrict g1_ybounds, *restrict g2_ybounds;
    if ( (g1_ybounds = g1->vtable->inqYBoundsPtr(g1)) )
    {
        xassert ( g1->nvertex );
        if ( g1->type == GRID_CURVILINEAR || g1->type == GRID_UNSTRUCTURED )
        size = g1->nvertex * g1->size;
        else
        size = g1->nvertex * g1->y.size;
        xassert ( size );

        if ( ! (g2_ybounds = g2->vtable->inqYBoundsPtr(g2)) ) return differ;

        for ( i = 0; i < size; i++ )
        if ( IS_NOT_EQUAL(g1->y.bounds[i], g2->y.bounds[i]) ) return differ;
    }
    else if ( g2->vtable->inqYBoundsPtr(g2) )
    return differ;
}

if (strcmp(g1->x.name, g2->x.name)) return differ;
if (strcmp(g1->y.name, g2->y.name)) return differ;
if (strcmp(g1->x.longname, g2->x.longname)) return differ;
if (strcmp(g1->y.longname, g2->y.longname)) return differ;
if (g1->x.stdname != g2->x.stdname) return differ;
if (g1->y.stdname != g2->y.stdname) return differ;
if (strcmp(g1->x.units, g2->x.units)) return differ;
if (strcmp(g1->y.units, g2->y.units)) return differ;

if (strcmp(g1->mapping, g2->mapping)) return differ;

if ( g1->reference )
    {
    if ( !g2->reference ) return differ;
    if ( strcmp(g1->reference, g2->reference) ) return differ;
    }
else if ( g2->reference )
    return differ;

if ( g1->mask )
    {
    xassert ( g1->size );
    if ( !g2->mask ) return differ;
    if ( memcmp ( g1->mask, g2->mask, g1->size * sizeof(mask_t)) ) return differ;
    }
else if ( g2->mask )
    return differ;

if ( g1->mask_gme )
    {
    xassert ( g1->size );
    if ( !g2->mask_gme ) return differ;
    if ( memcmp ( g1->mask_gme, g2->mask_gme, g1->size * sizeof(mask_t)) ) return differ;
    }
else if ( g2->mask_gme )
    return differ;

if ( memcmp(g1->uuid, g2->uuid, CDI_UUID_SIZE) )
    return differ;

return equal;
}

static
void gridComplete(grid_t *grid)
{
int gridID = grid->self;
gridDefDatatype(gridID, grid->datatype);

int gridtype = grid->type;
switch (gridtype)
    {
    case GRID_LONLAT:
    case GRID_GAUSSIAN:
    case GRID_UNSTRUCTURED:
    case GRID_CURVILINEAR:
    case GRID_GENERIC:
    case GRID_PROJECTION:
    case GRID_CHARXY:
    {
        if ( grid->x.size > 0 ) gridDefXsize(gridID, grid->x.size);
        if ( grid->y.size > 0 ) gridDefYsize(gridID, grid->y.size);

        if ( gridtype == GRID_GAUSSIAN ) gridDefNP(gridID, grid->np);

        if ( grid->nvertex > 0 )
        gridDefNvertex(gridID, grid->nvertex);

        if ( grid->x.flag == 2 )
        {
            assert(gridtype != GRID_UNSTRUCTURED && gridtype != GRID_CURVILINEAR);
            double *xvals = (double *) Malloc(grid->x.size * sizeof (double));
            gridGenXvals(grid->x.size, grid->x.first, grid->x.last, grid->x.inc, xvals);
            grid->x.vals = xvals;

        }

        if ( grid->y.flag == 2 )
        {
            assert(gridtype != GRID_UNSTRUCTURED && gridtype != GRID_CURVILINEAR);
            double *yvals = (double *) Malloc(grid->y.size * sizeof (double));
            gridGenYvals(gridtype, grid->y.size, grid->y.first, grid->y.last, grid->y.inc, yvals);
            grid->y.vals = yvals;

        }

        if ( grid->projtype == CDI_PROJ_RLL )
        {
            if ( grid->x.name[0] == 0 || grid->x.name[0] == 'x' ) strcpy(grid->x.name, "rlon");
            if ( grid->y.name[0] == 0 || grid->y.name[0] == 'y' ) strcpy(grid->y.name, "rlat");
            if ( grid->x.longname[0] == 0 ) strcpy(grid->x.longname, "longitude in rotated pole grid");
            if ( grid->y.longname[0] == 0 ) strcpy(grid->y.longname, "latitude in rotated pole grid");
            grid->x.stdname = "longitude";//xystdname_tab[grid_xystdname_grid_latlon][0];
            grid->y.stdname = "latitude";//xystdname_tab[grid_xystdname_grid_latlon][1];
            if ( grid->x.units[0] == 0 ) strcpy(grid->x.units, "degrees");
            if ( grid->y.units[0] == 0 ) strcpy(grid->y.units, "degrees");
        }

        if ( gridtype == GRID_UNSTRUCTURED )
        {
            int number = grid->number;
            int position = grid->position >= 0 ? grid->position : 0;
            if ( number > 0 ) gridDefNumber(gridID, number);
            gridDefPosition(gridID, position);
        }

        break;
    }
    case GRID_GAUSSIAN_REDUCED:
    {
        gridDefNP(gridID, grid->np);
        gridDefYsize(gridID, grid->y.size);
        if ( grid->x.flag == 2 )
        {
            double xvals[2] = { grid->x.first, grid->x.last };
            gridDefXvals(gridID, xvals);
        }

        if ( grid->y.flag == 2 )
        {
            double *yvals = (double *) Malloc(grid->y.size * sizeof (double));
            gridGenYvals(gridtype, grid->y.size, grid->y.first, grid->y.last, grid->y.inc, yvals);
            grid->y.vals = yvals;

        }
        break;
    }
    case GRID_SPECTRAL:
    {
        gridDefTrunc(gridID, grid->trunc);
        if ( grid->lcomplex ) gridDefComplexPacking(gridID, 1);
        break;
    }
    case GRID_FOURIER:
    {
        gridDefTrunc(gridID, grid->trunc);
        break;
    }
    case GRID_GME:
    {
        gridDefParamGME(gridID, grid->gme.nd, grid->gme.ni, grid->gme.ni2, grid->gme.ni3);
        break;
    }

    case GRID_TRAJECTORY:
    {
        gridDefXsize(gridID, 1);
        gridDefYsize(gridID, 1);
        break;
    }
    default:
    {
        Error("Gridtype %s unsupported!", gridNamePtr(gridtype));
        break;
    }
    }

grid->x.name[CDI_MAX_NAME - 1] = 0;
grid->x.longname[CDI_MAX_NAME - 1] = 0;
grid->x.units[CDI_MAX_NAME - 1] = 0;
grid->y.name[CDI_MAX_NAME - 1] = 0;
grid->y.longname[CDI_MAX_NAME - 1] = 0;
grid->y.units[CDI_MAX_NAME - 1] = 0;
}

#define GRID_STR_SERIALIZE(gridP) { gridP->x.dimname, gridP->y.dimname,  \
    gridP->vdimname, gridP->x.name, gridP->y.name,  \
    gridP->x.longname, gridP->y.longname, \
    gridP->x.units, gridP->y.units }

int gridGenerate(const grid_t *grid)
{
int gridtype = grid->type;
int gridID = gridCreate(gridtype, grid->size);
grid_t *restrict gridptr = grid_to_pointer(gridID);
gridptr->datatype = grid->datatype;
gridptr->x.size = grid->x.size;
gridptr->y.size = grid->y.size;
gridptr->np = grid->np;
gridptr->nvertex = grid->nvertex;
gridptr->x.flag = grid->x.flag;
int valdef_group1 = 0;
static const int valdef_group1_tab[] = {
    GRID_LONLAT, GRID_GAUSSIAN, GRID_UNSTRUCTURED, GRID_CURVILINEAR,
    GRID_GENERIC, GRID_PROJECTION
};
for ( size_t i = 0; i < sizeof (valdef_group1_tab) / sizeof (valdef_group1_tab[0]); ++i)
    valdef_group1 |= (gridtype == valdef_group1_tab[i]);
if ( valdef_group1 && grid->x.flag == 1 )
    {
    gridDefXvals(gridID, grid->x.vals);
    if ( grid->x.bounds )
        gridDefXbounds(gridID, grid->x.bounds);
    }
gridptr->x.first = grid->x.first;
gridptr->x.last = grid->x.last;
gridptr->x.inc = grid->x.inc;
gridptr->y.flag = grid->y.flag;
if ( (valdef_group1 || gridtype == GRID_GAUSSIAN_REDUCED) && grid->y.flag == 1)
    {
    gridDefYvals(gridID, grid->y.vals);
    if ( grid->y.bounds )
        gridDefYbounds(gridID, grid->y.bounds);
    }
gridptr->y.first = grid->y.first;
gridptr->y.last = grid->y.last;
gridptr->y.inc = grid->y.inc;
if ( valdef_group1 && grid->area)
    gridDefArea(gridID, grid->area);
gridptr->number = grid->number;
gridptr->position = grid->position;
gridptr->uvRelativeToGrid       = grid->uvRelativeToGrid;
gridptr->scanningMode           = grid->scanningMode;
gridptr->iScansNegatively       = grid->iScansNegatively;
gridptr->jScansPositively       = grid->jScansPositively;
gridptr->jPointsAreConsecutive  = grid->jPointsAreConsecutive;
memcpy(gridptr->uuid, grid->uuid, CDI_UUID_SIZE);
if ( gridtype == GRID_UNSTRUCTURED && grid->reference )
    gridDefReference(gridID, grid->reference);
if ( gridtype == GRID_PROJECTION )
    gridptr->name = strdup(grid->name);
if ( gridtype == GRID_GAUSSIAN_REDUCED )
    gridDefRowlon(gridID, grid->y.size, grid->rowlon);
gridptr->trunc = grid->trunc;
gridptr->lcomplex = grid->lcomplex;
gridptr->gme.nd = grid->gme.nd;
gridptr->gme.ni = grid->gme.ni;
gridptr->gme.ni2 = grid->gme.ni2;
gridptr->gme.ni3 = grid->gme.ni3;
const char *grid_str_tab[] = GRID_STR_SERIALIZE(grid);
char *gridptr_str_tab[] = GRID_STR_SERIALIZE(gridptr);
for (size_t i = 0; i < sizeof (grid_str_tab) / sizeof (grid_str_tab[0]); ++i)
    if ( grid_str_tab[i][0] )
    memcpy(gridptr_str_tab[i], grid_str_tab[i], CDI_MAX_NAME);
gridComplete(gridptr);

return gridID;
}

static void
grid_copy_base_array_fields(grid_t *gridptrOrig, grid_t *gridptrDup)
{
size_t nrowlon = (size_t)gridptrOrig->nrowlon;
size_t gridsize = gridptrOrig->size;
int gridtype = gridptrOrig->type;
int irregular = gridtype == GRID_CURVILINEAR || gridtype == GRID_UNSTRUCTURED;
if ( nrowlon )
    {
    gridptrDup->rowlon = (int*) Malloc(nrowlon * sizeof(int));
    memcpy(gridptrDup->rowlon, gridptrOrig->rowlon, nrowlon * sizeof(int));
    }

if ( gridptrOrig->x.vals != NULL )
    {
    size_t size = irregular ? gridsize : gridptrOrig->x.size;

    gridptrDup->x.vals = (double *)Malloc(size * sizeof (double));
    memcpy(gridptrDup->x.vals, gridptrOrig->x.vals, size * sizeof (double));
    }

if ( gridptrOrig->y.vals != NULL )
    {
    size_t size  = irregular ? gridsize : gridptrOrig->y.size;

    gridptrDup->y.vals = (double *)Malloc(size * sizeof (double));
    memcpy(gridptrDup->y.vals, gridptrOrig->y.vals, size * sizeof (double));
    }

if ( gridptrOrig->x.bounds != NULL )
    {
    size_t size  = (irregular ? gridsize : gridptrOrig->x.size)
        * gridptrOrig->nvertex;

    gridptrDup->x.bounds = (double *)Malloc(size * sizeof (double));
    memcpy(gridptrDup->x.bounds, gridptrOrig->x.bounds, size * sizeof (double));
    }

if ( gridptrOrig->y.bounds != NULL )
    {
    size_t size = (irregular ? gridsize : gridptrOrig->y.size)
        * gridptrOrig->nvertex;

    gridptrDup->y.bounds = (double *)Malloc(size * sizeof (double));
    memcpy(gridptrDup->y.bounds, gridptrOrig->y.bounds, size * sizeof (double));
    }

{
    const double *gridptrOrig_area
    = gridptrOrig->vtable->inqAreaPtr(gridptrOrig);
    if ( gridptrOrig_area != NULL )
    {
        size_t size = gridsize;

        gridptrDup->area = (double *)Malloc(size * sizeof (double));
        memcpy(gridptrDup->area, gridptrOrig_area, size * sizeof (double));
    }
}

if ( gridptrOrig->mask != NULL )
    {
    size_t size = gridsize;

    gridptrDup->mask = (mask_t *)Malloc(size * sizeof(mask_t));
    memcpy(gridptrDup->mask, gridptrOrig->mask, size * sizeof (mask_t));
    }

if ( gridptrOrig->mask_gme != NULL )
    {
    size_t size = gridsize;

    gridptrDup->mask_gme = (mask_t *)Malloc(size * sizeof (mask_t));
    memcpy(gridptrDup->mask_gme, gridptrOrig->mask_gme, size * sizeof(mask_t));
    }
}



int gridDuplicate(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
grid_t *gridptrnew = gridptr->vtable->copy(gridptr);
int gridIDnew = reshPut(gridptrnew, &gridOps);
gridptrnew->self = gridIDnew;
return gridIDnew;
}


void gridCompress(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);

int gridtype = gridInqType(gridID);
if ( gridtype == GRID_UNSTRUCTURED )
    {
    if ( gridptr->mask_gme != NULL )
        {
        size_t gridsize = gridInqSize(gridID);
        size_t nv = (size_t)gridptr->nvertex;
        double *restrict area
            = (double *)gridptr->vtable->inqAreaPtr(gridptr),
            *restrict xvals = (double *)gridptr->vtable->inqXValsPtr((grid_t *)gridptr),
            *restrict yvals = (double *)gridptr->vtable->inqYValsPtr((grid_t *)gridptr),
            *restrict xbounds = (double *)gridptr->vtable->inqXBoundsPtr(gridptr),
            *restrict ybounds = (double *)gridptr->vtable->inqYBoundsPtr(gridptr);
        mask_t *restrict mask_gme = gridptr->mask_gme;
        size_t *restrict selection = (size_t *)Malloc(gridsize * sizeof (selection[0]));
        size_t nselect;
        {
            size_t j = 0;
            for (size_t i = 0; i < gridsize; i++ )
            selection[j] = i, j += (mask_gme[i] != 0);
            nselect = j;
        }
        selection = (size_t *)Realloc(selection, nselect * sizeof (selection[0]));
        if (xvals)
            for (size_t i = 0; i < nselect; i++ )
            xvals[i] = xvals[selection[i]];
        if (yvals)
            for (size_t i = 0; i < nselect; i++ )
            yvals[i] = yvals[selection[i]];
        if (area)
            for (size_t i = 0; i < nselect; i++ )
            area[i] = area[selection[i]];
        if (xbounds)
            for (size_t i = 0; i < nselect; i++ )
            for (size_t iv = 0; iv < nv; iv++)
                xbounds[i * nv + iv] = xbounds[selection[i] * nv + iv];
        if (ybounds)
            for (size_t i = 0; i < nselect; i++ )
            for (size_t iv = 0; iv < nv; iv++)
                ybounds[i * nv + iv] = ybounds[selection[i] * nv + iv];
        Free(selection);


        gridsize = nselect;
        gridptr->size  = (int)gridsize;
        gridptr->x.size = (int)gridsize;
        gridptr->y.size = (int)gridsize;

        double **resizeP[] = { &gridptr->x.vals, &gridptr->y.vals,
                                &gridptr->area,
                                &gridptr->x.bounds, &gridptr->y.bounds };
        size_t newSize[] = { gridsize, gridsize, gridsize, nv*gridsize,
                            nv*gridsize };
        for ( size_t i = 0; i < sizeof (resizeP) / sizeof (resizeP[0]); ++i)
            if ( *(resizeP[i]) )
            *(resizeP[i]) = (double *)Realloc(*(resizeP[i]), newSize[i]*sizeof(double));

        Free(gridptr->mask_gme);
        gridptr->mask_gme = NULL;
        gridMark4Update(gridID);
        }
    }
else
    Warning("Unsupported grid type: %s", gridNamePtr(gridtype));
}

static void
gridDefAreaSerial(grid_t *gridptr, const double *area)
{
size_t size = gridptr->size;

if ( size == 0 )
    Error("size undefined for gridID = %d", gridptr->self);

if ( gridptr->area == NULL )
    gridptr->area = (double *) Malloc(size*sizeof(double));
else if ( CDI_Debug )
    Warning("values already defined!");

memcpy(gridptr->area, area, size * sizeof(double));
}


void gridDefArea(int gridID, const double *area)
{
grid_t *gridptr = grid_to_pointer(gridID);
gridptr->vtable->defArea(gridptr, area);
gridMark4Update(gridID);
}

static void
gridInqAreaSerial(grid_t *gridptr, double *area)
{
if (gridptr->area)
    memcpy(area, gridptr->area, gridptr->size * sizeof (double));
}


void gridInqArea(int gridID, double *area)
{
grid_t *gridptr = grid_to_pointer(gridID);
gridptr->vtable->inqArea(gridptr, area);
}

static int
gridHasAreaBase(grid_t *gridptr)
{
return gridptr->area != NULL;
}

int gridHasArea(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->hasArea(gridptr);
}


static const double *gridInqAreaPtrBase(grid_t *gridptr)
{
return gridptr->area;
}

const double *gridInqAreaPtr(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqAreaPtr(gridptr);
}


void gridDefNvertex(int gridID, int nvertex)
{
grid_t *gridptr = grid_to_pointer(gridID);

if (gridptr->nvertex != nvertex)
    {
    gridptr->nvertex = nvertex;
    gridMark4Update(gridID);
    }
}


int gridInqNvertex(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->nvertex;
}

static void
gridDefBoundsGeneric(grid_t *gridptr, const double *bounds, size_t regularSize,
                    double **field)
{
int irregular = gridptr->type == GRID_CURVILINEAR
    || gridptr->type == GRID_UNSTRUCTURED;
size_t nvertex = (size_t)gridptr->nvertex;
if ( nvertex == 0 )
    {
    Warning("nvertex undefined for gridID = %d. Cannot define bounds!",
            gridptr->self);
    return;
    }
size_t size = nvertex * (irregular ? gridptr->size : regularSize);
if ( size == 0 )
    Error("size undefined for gridID = %d", gridptr->self);

if (*field == NULL)
    *field = (double *)Malloc(size * sizeof (double));
else if ( CDI_Debug )
    Warning("values already defined!");

memcpy(*field, bounds, size * sizeof (double));
}


static void
gridDefXBoundsSerial(grid_t *gridptr, const double *xbounds)
{
gridDefBoundsGeneric(gridptr, xbounds, gridptr->x.size, &gridptr->x.bounds);
}


void gridDefXbounds(int gridID, const double *xbounds)
{
grid_t *gridptr = grid_to_pointer(gridID);
gridptr->vtable->defXBounds(gridptr, xbounds);
gridMark4Update(gridID);
}

static size_t
gridInqXBoundsSerial(grid_t *gridptr, double *xbounds)
{
size_t nvertex = (size_t)gridptr->nvertex;

int irregular = gridptr->type == GRID_CURVILINEAR
    || gridptr->type == GRID_UNSTRUCTURED;
size_t size = nvertex * (irregular ? gridptr->size : gridptr->x.size);

const double *gridptr_xbounds = gridptr->vtable->inqXBoundsPtr(gridptr);
if ( gridptr_xbounds )
    {
    if ( size && xbounds )
        memcpy(xbounds, gridptr_xbounds, size * sizeof (double));
    }
else
    size = 0;

return size;
}


size_t gridInqXbounds(int gridID, double *xbounds)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqXBounds(gridptr, xbounds);
}

static const double *
gridInqXBoundsPtrSerial(grid_t *gridptr)
{
return gridptr->x.bounds;
}


const double *gridInqXboundsPtr(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqXBoundsPtr(gridptr);
}

static void
gridDefYBoundsSerial(grid_t *gridptr, const double *ybounds)
{
gridDefBoundsGeneric(gridptr, ybounds, gridptr->y.size, &gridptr->y.bounds);
}





size_t gridInqXboundsPart(int gridID, int start, size_t size, double *xbounds)
{
grid_t *gridptr = grid_to_pointer(gridID);
const double *gridptr_xbounds = gridptr->vtable->inqXBoundsPtr(gridptr);
if ( gridptr_xbounds )
    if ( size && xbounds )
    memcpy(xbounds, gridptr_xbounds+start, size * sizeof (double));

return size;
}

size_t gridInqYboundsPart(int gridID, int start, size_t size, double *ybounds)
{
grid_t *gridptr = grid_to_pointer(gridID);
const double *gridptr_ybounds = gridptr->vtable->inqYBoundsPtr(gridptr);
if ( gridptr_ybounds )
    if ( size && ybounds )
    memcpy(ybounds, gridptr_ybounds+start, size * sizeof (double));

return size;
}


void gridDefYbounds(int gridID, const double *ybounds)
{
grid_t *gridptr = grid_to_pointer(gridID);
gridptr->vtable->defYBounds(gridptr, ybounds);
gridMark4Update(gridID);
}

static size_t
gridInqYBoundsSerial(grid_t *gridptr, double *ybounds)
{
size_t nvertex = (size_t)gridptr->nvertex;

int irregular = gridptr->type == GRID_CURVILINEAR
    || gridptr->type == GRID_UNSTRUCTURED;
size_t size = nvertex * (irregular ? gridptr->size : gridptr->y.size);

const double *gridptr_ybounds = gridptr->vtable->inqYBoundsPtr(gridptr);
if ( gridptr_ybounds )
    {
    if ( size && ybounds )
        memcpy(ybounds, gridptr_ybounds, size * sizeof (double));
    }
else
    size = 0;

return size;
}



size_t gridInqYbounds(int gridID, double *ybounds)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqYBounds(gridptr, ybounds);
}

static const double *
gridInqYBoundsPtrSerial(grid_t *gridptr)
{
return gridptr->y.bounds;
}


const double *gridInqYboundsPtr(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqYBoundsPtr(gridptr);
}

static void
printDblsPrefixAutoBrk(FILE *fp, int dig, const char prefix[], size_t nbyte0,
                    size_t n, const double vals[])
{
fputs(prefix, fp);
size_t nbyte = nbyte0;
for ( size_t i = 0; i < n; i++ )
    {
    if ( nbyte > 80 )
        {
        fprintf(fp, "\n%*s", (int)nbyte0, "");
        nbyte = nbyte0;
        }
    nbyte += (size_t)fprintf(fp, "%.*g ", dig, vals[i]);
    }
fputs("\n", fp);
}

static void
printIntsPrefixAutoBrk(FILE *fp, const char prefix[], size_t nbyte0,
                    size_t n, const int vals[])
{
fputs(prefix, fp);
size_t nbyte = nbyte0;
for ( size_t i = 0; i < n; i++ )
    {
    if ( nbyte > 80 )
        {
        fprintf(fp, "\n%*s", (int)nbyte0, "");
        nbyte = nbyte0;
        }
    nbyte += (size_t)fprintf(fp, "%d ", vals[i]);
    }
fputs("\n", fp);
}

static void
printBounds(FILE *fp, int dig, const char prefix[], size_t nbyte0,
            size_t n, size_t nvertex, const double bounds[])
{
fputs(prefix, fp);
if ( n > 0 )
    {
    for ( size_t iv = 0; iv < nvertex; iv++ )
        fprintf(fp, "%.*g ", dig, bounds[iv]);
    for ( size_t i = 1; i < n; i++ )
        {
        fprintf(fp, "\n%*s", (int)nbyte0, "");
        for ( size_t iv = 0; iv < nvertex; iv++ )
            fprintf(fp, "%.*g ", dig, bounds[i*nvertex+iv]);
        }
    fputs("\n", fp);
    }
}

static void
printMask(FILE *fp, const char prefix[], size_t nbyte0,
        size_t n, const int mask[])
{
fputs(prefix, fp);
size_t nbyte = nbyte0;
for ( size_t i = 0; i < n; i++ )
    {
    if ( nbyte > 80 )
        {
        fprintf(fp, "\n%*s", (int)nbyte0, "");
        nbyte = nbyte0;
        }
    nbyte += (size_t)fprintf(fp, "%d ", mask[i]);
    }
fputs("\n", fp);
}

static inline
void *resizeBuffer(void **buf, size_t *bufSize, size_t reqSize)
{
if (reqSize > *bufSize)
    {
    *buf = Realloc(*buf, reqSize);
    *bufSize = reqSize;
    }
return *buf;
}

static
void gridPrintAttributes(FILE *fp, int gridID)
{
int cdiID = gridID;
int varID = CDI_GLOBAL;
int atttype, attlen;
char attname[CDI_MAX_NAME+1];
void *attBuf = NULL;
size_t attBufSize = 0;

int natts;
cdiInqNatts(cdiID, varID, &natts);

for ( int iatt = 0; iatt < natts; ++iatt )
    {
    cdiInqAtt(cdiID, varID, iatt, attname, &atttype, &attlen);

    if ( attlen == 0 ) continue;

    if ( atttype == CDI_DATATYPE_TXT )
        {
        size_t attSize = (size_t)(attlen+1)*sizeof(char);
        char *atttxt = (char *)resizeBuffer(&attBuf, &attBufSize, attSize);
        cdiInqAttTxt(cdiID, varID, attname, attlen, atttxt);
        atttxt[attlen] = 0;
        fprintf(fp, "ATTR_TXT: %s = \"%s\"\n", attname, atttxt);
        }
    else if ( atttype == CDI_DATATYPE_INT8  || atttype == CDI_DATATYPE_UINT8  ||
                atttype == CDI_DATATYPE_INT16 || atttype == CDI_DATATYPE_UINT16 ||
                atttype == CDI_DATATYPE_INT32 || atttype == CDI_DATATYPE_UINT32 )
        {
        size_t attSize = (size_t)attlen*sizeof(int);
        int *attint = (int *)resizeBuffer(&attBuf, &attBufSize, attSize);
        cdiInqAttInt(cdiID, varID, attname, attlen, &attint[0]);
        if ( attlen == 1 )
            fprintf(fp, "ATTR_INT: %s =", attname);
        else
            fprintf(fp, "ATTR_INT_%d: %s =", attlen, attname);
        for ( int i = 0; i < attlen; ++i ) fprintf(fp, " %d", attint[i]);
        fprintf(fp, "\n");
        }
    else if ( atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64 )
        {
        size_t attSize = (size_t)attlen * sizeof(double);
        double *attflt = (double *)resizeBuffer(&attBuf, &attBufSize, attSize);
        int dig = (atttype == CDI_DATATYPE_FLT64) ? 15 : 7;
        cdiInqAttFlt(cdiID, varID, attname, attlen, attflt);
        if ( attlen == 1 )
            fprintf(fp, "ATTR_FLT: %s =", attname);
        else
            fprintf(fp, "ATTR_FLT_%d: %s =", attlen, attname);
        for ( int i = 0; i < attlen; ++i ) fprintf(fp, " %.*g", dig, attflt[i]);
        fprintf(fp, "\n");
        }
    }

Free(attBuf);
}

static
void gridPrintKernel(int gridID, int opt, FILE *fp)
{
size_t xdimLen, ydimLen;
char attstr[CDI_MAX_NAME];
char attstr2[CDI_MAX_NAME];
unsigned char uuidOfHGrid[CDI_UUID_SIZE];
const char  **xcvals  = gridInqXCvalsPtr(gridID);
const char  **ycvals  = gridInqYCvalsPtr(gridID);
size_t nxvals = gridInqXvals(gridID, NULL);
size_t nyvals = gridInqYvals(gridID, NULL);
size_t nxbounds = gridInqXbounds(gridID, NULL);
size_t nybounds = gridInqYbounds(gridID, NULL);

int type     = gridInqType(gridID);
size_t gridsize = gridInqSize(gridID);
size_t xsize    = gridInqXsize(gridID);
size_t ysize    = gridInqYsize(gridID);
int xstrlen  = gridInqXIsc(gridID);
int ystrlen  = gridInqYIsc(gridID);
int nvertex  = gridInqNvertex(gridID);
int datatype = gridInqDatatype(gridID);

int dig = (datatype == CDI_DATATYPE_FLT64) ? 15 : 7;

fprintf(fp, "gridtype  = %s\n" "gridsize  = %zu\n", gridNamePtr(type), gridsize);

if ( type != GRID_GME )
    {
    if ( type != GRID_UNSTRUCTURED && type != GRID_SPECTRAL && type != GRID_FOURIER )
        {
        if ( xsize > 0 ) fprintf(fp, "xsize     = %zu\n", xsize);
        if ( ysize > 0 ) fprintf(fp, "ysize     = %zu\n", ysize);
        }

    if ( nxvals > 0 || xcvals )
        {
        if ( xstrlen )  fprintf(fp, "xstringlen= %d\n", xstrlen);
        attstr[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_XNAME, CDI_MAX_NAME, attstr);
        if ( attstr[0] )  fprintf(fp, "xname     = %s\n", attstr);
        attstr2[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_XDIMNAME, CDI_MAX_NAME, attstr2);
        if ( attstr2[0] && strcmp(attstr, attstr2) )  fprintf(fp, "xdimname  = %s\n", attstr2);
        attstr[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_XLONGNAME, CDI_MAX_NAME, attstr);
        if ( attstr[0] )  fprintf(fp, "xlongname = %s\n", attstr);
        attstr[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_XUNITS, CDI_MAX_NAME, attstr);
        if ( attstr[0] )  fprintf(fp, "xunits    = %s\n", attstr);
        }

    if ( nyvals > 0 || ycvals )
        {
        if ( ystrlen )  fprintf(fp, "ystringlen= %d\n", ystrlen);
        attstr[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_YNAME, CDI_MAX_NAME, attstr);
        if ( attstr[0] )  fprintf(fp, "yname     = %s\n", attstr);
        attstr2[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_YDIMNAME, CDI_MAX_NAME, attstr2);
        if ( attstr2[0] && strcmp(attstr, attstr2) )  fprintf(fp, "ydimname  = %s\n", attstr2);
        attstr[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_YLONGNAME, CDI_MAX_NAME, attstr);
        if ( attstr[0] )  fprintf(fp, "ylongname = %s\n", attstr);
        attstr[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_YUNITS, CDI_MAX_NAME, attstr);
        if ( attstr[0] )  fprintf(fp, "yunits    = %s\n", attstr);
        }

    if ( type == GRID_UNSTRUCTURED || type == GRID_CURVILINEAR )
        {
        attstr[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_VDIMNAME, CDI_MAX_NAME, attstr);
        if ( attstr[0] ) fprintf(fp, "vdimname  = %s\n", attstr);
        }
    if ( type == GRID_UNSTRUCTURED && nvertex > 0 ) fprintf(fp, "nvertex   = %d\n", nvertex);
    }

switch (type)
    {
    case GRID_LONLAT:
    case GRID_GAUSSIAN:
    case GRID_GAUSSIAN_REDUCED:
    case GRID_GENERIC:
    case GRID_PROJECTION:
    case GRID_CURVILINEAR:
    case GRID_UNSTRUCTURED:
    case GRID_CHARXY:
    {
        if ( type == GRID_GAUSSIAN || type == GRID_GAUSSIAN_REDUCED ) fprintf(fp, "np        = %d\n", gridInqNP(gridID));

        if ( type == GRID_CURVILINEAR || type == GRID_UNSTRUCTURED )
        {
            xdimLen = gridsize;
            ydimLen = gridsize;
        }
        else if ( type == GRID_GAUSSIAN_REDUCED )
        {
            xdimLen = 2;
            ydimLen = ysize;
        }
        else
        {
            xdimLen = xsize;
            ydimLen = ysize;
        }

        if ( type == GRID_UNSTRUCTURED )
        {
            int number = gridInqNumber(gridID);
            int position = gridInqPosition(gridID);

            if ( number > 0 )
            {
                fprintf(fp, "number    = %d\n", number);
                if ( position >= 0 ) fprintf(fp, "position  = %d\n", position);
            }

            if ( gridInqReference(gridID, NULL) )
            {
                char reference_link[8192];
                gridInqReference(gridID, reference_link);
                fprintf(fp, "uri       = %s\n", reference_link);
            }
        }

        if ( nxvals > 0 )
        {
            double xfirst = 0.0, xinc = 0.0;

            if ( type == GRID_LONLAT     || type == GRID_GAUSSIAN ||
                type == GRID_PROJECTION || type == GRID_GENERIC )
            {
                xfirst = gridInqXval(gridID, 0);
                xinc   = gridInqXinc(gridID);
            }

            if ( IS_NOT_EQUAL(xinc, 0) && opt )
            {
                fprintf(fp, "xfirst    = %.*g\n"
                        "xinc      = %.*g\n", dig, xfirst, dig, xinc);
            }
            else
            {
                double *xvals = (double*) Malloc(nxvals*sizeof(double));
                gridInqXvals(gridID, xvals);
                static const char prefix[] = "xvals     = ";
                printDblsPrefixAutoBrk(fp, dig, prefix, sizeof(prefix)-1, nxvals, xvals);
                Free(xvals);
            }
        }

        if ( xcvals )
        {
            attstr[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_XNAME, CDI_MAX_NAME, attstr);
            if ( attstr[0] )
            fprintf(fp, "x%ss = %.*s\n", attstr, xstrlen, xcvals[0]);
            else
            fprintf(fp, "xstrings  = %.*s\n", xstrlen, xcvals[0]);
            for ( size_t i = 1; i < xsize; i++ )
            fprintf(fp, "          = %.*s\n", xstrlen, xcvals[i]);
        }

        if ( nxbounds )
        {
            double *xbounds = (double*) Malloc(nxbounds*sizeof(double));
            gridInqXbounds(gridID, xbounds);
            static const char prefix[] = "xbounds   = ";
            printBounds(fp, dig, prefix, sizeof(prefix)-1, xdimLen, (size_t)nvertex, xbounds);
            Free(xbounds);
        }

        if ( nyvals > 0 )
        {
            double yfirst = 0.0, yinc = 0.0;

            if ( type == GRID_LONLAT || type == GRID_GENERIC ||
                type == GRID_PROJECTION || type == GRID_GENERIC )
            {
                yfirst = gridInqYval(gridID, 0);
                yinc   = gridInqYinc(gridID);
            }

            if ( IS_NOT_EQUAL(yinc, 0) && opt )
            {
                fprintf(fp, "yfirst    = %.*g\n"
                        "yinc      = %.*g\n", dig, yfirst, dig, yinc);
            }
            else
            {
                double *yvals = (double*) Malloc(nyvals*sizeof(double));
                gridInqYvals(gridID, yvals);
                static const char prefix[] = "yvals     = ";
                printDblsPrefixAutoBrk(fp, dig, prefix, sizeof(prefix)-1, nyvals, yvals);
                Free(yvals);
            }
        }

        if ( ycvals )
        {
            attstr[0] = 0; cdiGridInqKeyStr(gridID, CDI_KEY_YNAME, CDI_MAX_NAME, attstr);
            if ( attstr[0] )
            fprintf(fp, "x%ss = %.*s\n", attstr, ystrlen, ycvals[0]);
            else
            fprintf(fp, "ystrings  = %.*s\n", ystrlen, ycvals[0]);
            for ( size_t i = 1; i < ysize; i++ )
            fprintf(fp, "          = %.*s\n", ystrlen, ycvals[i]);
        }

        if ( nybounds )
        {
            double *ybounds = (double*) Malloc(nybounds*sizeof(double));
            gridInqYbounds(gridID, ybounds);
            static const char prefix[] = "ybounds   = ";
            printBounds(fp, dig, prefix, sizeof(prefix)-1, ydimLen, (size_t)nvertex, ybounds);
            Free(ybounds);
        }

        if ( gridHasArea(gridID) )
        {
            double *area = (double*) Malloc(gridsize*sizeof(double));
            gridInqArea(gridID, area);
            static const char prefix[] = "area      = ";
            printDblsPrefixAutoBrk(fp, dig, prefix, sizeof(prefix)-1, gridsize, area);
            Free(area);
        }

        if ( type == GRID_GAUSSIAN_REDUCED )
        {
            static const char prefix[] = "rowlon    = ";
            int *rowlon = (int *)Malloc(ysize*sizeof(int));
            gridInqRowlon(gridID, rowlon);
            printIntsPrefixAutoBrk(fp, prefix, sizeof(prefix)-1,
                                (ysize > 0 ? ysize : 0), rowlon);
            Free(rowlon);
        }

        if ( type == GRID_PROJECTION ) gridPrintAttributes(fp, gridID);

        break;
    }
    case GRID_SPECTRAL:
    {
        fprintf(fp, "truncation = %d\n"
                "complexpacking = %d\n", gridInqTrunc(gridID), gridInqComplexPacking(gridID) );
        break;
    }
    case GRID_FOURIER:
    {
        fprintf(fp, "truncation = %d\n", gridInqTrunc(gridID));
        break;
    }
    case GRID_GME:
    {
        int nd, ni, ni2, ni3;
        gridInqParamGME(gridID, &nd, &ni, &ni2, &ni3);
        fprintf(fp, "ni        = %d\n", ni );
        break;
    }
default:
    {
        fprintf(stderr, "Unsupported grid type: %s\n", gridNamePtr(type));
        break;
    }
    }

gridInqUUID(gridID, uuidOfHGrid);
if ( !cdiUUIDIsNull(uuidOfHGrid) )
    {
    char uuidOfHGridStr[37];
    cdiUUID2Str(uuidOfHGrid, uuidOfHGridStr);
    if ( uuidOfHGridStr[0] != 0 && strlen(uuidOfHGridStr) == 36 )
        fprintf(fp, "uuid      = %s\n", uuidOfHGridStr);
    }

if ( gridInqMask(gridID, NULL) )
    {
    int *mask = (gridsize>0) ? (int*) Malloc(gridsize*sizeof(int)) : NULL;
    gridInqMask(gridID, mask);
    static const char prefix[] = "mask      = ";
    printMask(fp, prefix, sizeof(prefix)-1,
                (gridsize > 0 ? gridsize : 0), mask);
    if ( mask ) Free(mask);
    }
}


void gridPrint(int gridID, int opt)
{
gridPrintKernel(gridID, opt, stdout);
}


void gridPrintP(void *voidptr, FILE *fp)
{
grid_t *gridptr = (grid_t *) voidptr;
int gridID = gridptr->self;

xassert( gridptr );

gridPrintKernel(gridID, 0, fp);

fprintf(fp,
        "datatype  = %d\n"
        "nd        = %d\n"
        "ni        = %d\n"
        "ni2       = %d\n"
        "ni3       = %d\n"
        "number    = %d\n"
        "position  = %d\n"
        "trunc     = %d\n"
        "lcomplex  = %d\n"
        "nrowlon   = %d\n",
        gridptr->datatype, gridptr->gme.nd, gridptr->gme.ni, gridptr->gme.ni2,
        gridptr->gme.ni3, gridptr->number, gridptr->position, gridptr->trunc,
        gridptr->lcomplex, gridptr->nrowlon );

if ( gridptr->rowlon )
    {
    static const char prefix[] = "rowlon    = ";
    printIntsPrefixAutoBrk(fp, prefix, sizeof(prefix)-1,
                            (size_t)(gridptr->nrowlon > 0
                                    ? gridptr->nrowlon : 0), gridptr->rowlon);
    }

if ( gridInqMaskGME(gridID, NULL) )
    {
    size_t gridsize = gridptr->size;
    int *mask = (gridsize>0) ? (int*) Malloc(gridsize*sizeof(int)) : NULL;
    gridInqMaskGME(gridID, mask);
    static const char prefix[] = "mask_gme  = ";
    printMask(fp, prefix, sizeof(prefix)-1,
                (gridptr->size > 0 ? gridptr->size : 0), mask);
    if ( mask ) Free(mask);
    }
}

static const double *gridInqXValsPtrSerial(grid_t *gridptr)
{
return gridptr->x.vals;
}

static const char **gridInqXCvalsPtrSerial(grid_t *gridptr)
{
return (const char **) gridptr->x.cvals;
}


const double *gridInqXvalsPtr(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqXValsPtr(gridptr);
}


const char **gridInqXCvalsPtr(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqXCvalsPtr(gridptr);
}

static const double *gridInqYValsPtrSerial(grid_t *gridptr)
{
return gridptr->y.vals;
}

static const char **gridInqYCvalsPtrSerial(grid_t *gridptr)
{
return (const char **) gridptr->y.cvals;
}

const double *gridInqYvalsPtr(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqYValsPtr(gridptr);
}

const char **gridInqYCvalsPtr(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->vtable->inqYCvalsPtr(gridptr);
}


void gridDefParamLCC(int gridID, double missval, double lon_0, double lat_0, double lat_1, double lat_2,
                    double a, double rf, double xval_0, double yval_0, double x_0, double y_0)
{
(void)lat_0;
cdiGridDefKeyStr(gridID, CDI_KEY_MAPPING, CDI_MAX_NAME, "Lambert_Conformal");

const char *mapname = "lambert_conformal_conic";
cdiGridDefKeyStr(gridID, CDI_KEY_MAPNAME, CDI_MAX_NAME, mapname);
cdiDefAttTxt(gridID, CDI_GLOBAL, "grid_mapping_name", (int)(strlen(mapname)), mapname);
int nlats = 0;
double lats[2];
lats[nlats++] = lat_1;
if ( IS_NOT_EQUAL(lat_1, lat_2) ) lats[nlats++] = lat_2;
cdiDefAttFlt(gridID, CDI_GLOBAL, "standard_parallel", CDI_DATATYPE_FLT64, nlats, lats);
cdiDefAttFlt(gridID, CDI_GLOBAL, "longitude_of_central_meridian", CDI_DATATYPE_FLT64, 1, &lon_0);
cdiDefAttFlt(gridID, CDI_GLOBAL, "latitude_of_projection_origin", CDI_DATATYPE_FLT64, 1, &lat_2);
if ( a > 0 ) cdiDefAttFlt(gridID, CDI_GLOBAL, "earth_radius", CDI_DATATYPE_FLT64, 1, &a);
if ( rf > 0 ) cdiDefAttFlt(gridID, CDI_GLOBAL, "inverse_flattening", CDI_DATATYPE_FLT64, 1, &rf);
if ( IS_NOT_EQUAL(x_0, missval) ) cdiDefAttFlt(gridID, CDI_GLOBAL, "false_easting", CDI_DATATYPE_FLT64, 1, &x_0);
if ( IS_NOT_EQUAL(y_0, missval) ) cdiDefAttFlt(gridID, CDI_GLOBAL, "false_northing", CDI_DATATYPE_FLT64, 1, &y_0);
if ( IS_NOT_EQUAL(xval_0, missval) ) cdiDefAttFlt(gridID, CDI_GLOBAL, "longitudeOfFirstGridPointInDegrees", CDI_DATATYPE_FLT64, 1, &xval_0);
if ( IS_NOT_EQUAL(yval_0, missval) ) cdiDefAttFlt(gridID, CDI_GLOBAL, "latitudeOfFirstGridPointInDegrees", CDI_DATATYPE_FLT64, 1, &yval_0);

grid_t *gridptr = grid_to_pointer(gridID);
gridptr->projtype = CDI_PROJ_LCC;

gridVerifyProj(gridID);
}


int gridInqParamLCC(int gridID, double missval, double *lon_0, double *lat_0, double *lat_1, double *lat_2,
                    double *a, double *rf, double *xval_0, double *yval_0, double *x_0, double *y_0)
{
*a = 0; *rf = 0;
*lon_0 = missval; *lat_0 = missval, *lat_1 = missval, *lat_2 = missval;
*xval_0 = missval; *yval_0 = missval; *x_0 = missval, *y_0 = missval;

int status = -1;
if ( gridInqType(gridID) != GRID_PROJECTION ) return status;

status = -2;
const char *projection = "lambert_conformal_conic";
char mapname[CDI_MAX_NAME]; mapname[0] = 0;
cdiGridInqKeyStr(gridID, CDI_KEY_MAPNAME, CDI_MAX_NAME, mapname);
if ( mapname[0] && strcmp(mapname, projection) == 0 )
    {
    int atttype, attlen;
    char attname[CDI_MAX_NAME+1];

    int natts;
    cdiInqNatts(gridID, CDI_GLOBAL, &natts);

    if ( natts ) status = 0;

    for ( int iatt = 0; iatt < natts; ++iatt )
        {
        cdiInqAtt(gridID, CDI_GLOBAL, iatt, attname, &atttype, &attlen);
        if ( attlen > 2 ) continue;

        double attflt[2];
        if ( cdiInqAttConvertedToFloat(gridID, atttype, attname, attlen, attflt) )
            {
            if      ( strcmp(attname, "earth_radius") == 0 )                       *a      = attflt[0];
            else if ( strcmp(attname, "inverse_flattening") == 0 )                 *rf     = attflt[0];
            else if ( strcmp(attname, "longitude_of_central_meridian") == 0 )      *lon_0  = attflt[0];
            else if ( strcmp(attname, "latitude_of_projection_origin") == 0 )      *lat_0  = attflt[0];
            else if ( strcmp(attname, "false_easting")  == 0 )                     *x_0    = attflt[0];
            else if ( strcmp(attname, "false_northing") == 0 )                     *y_0    = attflt[0];
            else if ( strcmp(attname, "longitudeOfFirstGridPointInDegrees") == 0 ) *xval_0 = attflt[0];
            else if ( strcmp(attname, "latitudeOfFirstGridPointInDegrees")  == 0 ) *yval_0 = attflt[0];
            else if ( strcmp(attname, "standard_parallel") == 0 )
                {
                *lat_1 = attflt[0];
                *lat_2 = (attlen == 2) ? attflt[1] : attflt[0];
                }
            }
        }
    }

return status;
}


int gridVerifyGribParamLCC(double missval, double *lon_0, double *lat_0, double *lat_1, double *lat_2,
                        double *a, double *rf, double *xval_0, double *yval_0, double *x_0, double *y_0)
{
static bool lwarn = true;

if ( lwarn )
    {

    const char *projection = "lambert_conformal_conic";
    if ( IS_EQUAL(*lon_0, missval) ) { Warning("%s mapping parameter %s missing!", projection, "longitude_of_central_meridian"); }
    if ( IS_EQUAL(*lat_0, missval) ) { Warning("%s mapping parameter %s missing!", projection, "latitude_of_central_meridian"); }
    if ( IS_EQUAL(*lat_1, missval) ) { Warning("%s mapping parameter %s missing!", projection, "standard_parallel"); }
    if ( IS_NOT_EQUAL(*x_0, missval) && IS_NOT_EQUAL(*y_0, missval) && (IS_EQUAL(*xval_0, missval) || IS_EQUAL(*yval_0, missval)) )
        {
        if ( proj_lcc_to_lonlat_func )
            {
            *xval_0 = -(*x_0); *yval_0 = -(*y_0);
            proj_lcc_to_lonlat_func(missval, *lon_0, *lat_0, *lat_1, *lat_2, *a, *rf, 0.0, 0.0, (size_t)1, xval_0, yval_0);
            }
        if ( IS_EQUAL(*xval_0, missval) || IS_EQUAL(*yval_0, missval) )
            Warning("%s mapping parameter %s missing!", projection, "longitudeOfFirstGridPointInDegrees and latitudeOfFirstGridPointInDegrees");
        }
    }

return 0;
}


int gridVerifyGribParamSTERE(double missval, double *lon_0, double *lat_ts, double *lat_0,
                            double *a, double *xval_0, double *yval_0, double *x_0, double *y_0)
{
static bool lwarn = true;

if ( lwarn )
    {

    const char *projection = "lambert_conformal_conic";
    if ( IS_EQUAL(*lon_0, missval) ) { Warning("%s mapping parameter %s missing!", projection, "straight_vertical_longitude_from_pole"); }
    if ( IS_EQUAL(*lat_0, missval) ) { Warning("%s mapping parameter %s missing!", projection, "latitude_of_projection_origin"); }
    if ( IS_EQUAL(*lat_ts, missval) ) { Warning("%s mapping parameter %s missing!", projection, "standard_parallel"); }
    if ( IS_NOT_EQUAL(*x_0, missval) && IS_NOT_EQUAL(*y_0, missval) && (IS_EQUAL(*xval_0, missval) || IS_EQUAL(*yval_0, missval)) )
        {
        if ( proj_stere_to_lonlat_func )
            {
            *xval_0 = -(*x_0); *yval_0 = -(*y_0);
            proj_stere_to_lonlat_func(missval, *lon_0, *lat_ts, *lat_0, *a, 0.0, 0.0, (size_t)1, xval_0, yval_0);
            }
        if ( IS_EQUAL(*xval_0, missval) || IS_EQUAL(*yval_0, missval) )
            Warning("%s mapping parameter %s missing!", projection, "longitudeOfFirstGridPointInDegrees and latitudeOfFirstGridPointInDegrees");
        }
    }

return 0;
}


void gridDefParamSTERE(int gridID, double missval, double lon_0, double lat_ts, double lat_0,
                    double a, double xval_0, double yval_0, double x_0, double y_0)
{
cdiGridDefKeyStr(gridID, CDI_KEY_MAPPING, CDI_MAX_NAME, "Polar_Stereographic");

const char *mapname = "polar_stereographic";
cdiGridDefKeyStr(gridID, CDI_KEY_MAPNAME, CDI_MAX_NAME, mapname);
cdiDefAttTxt(gridID, CDI_GLOBAL, "grid_mapping_name", (int)(strlen(mapname)), mapname);
cdiDefAttFlt(gridID, CDI_GLOBAL, "standard_parallel", CDI_DATATYPE_FLT64, 1, &lat_ts);
cdiDefAttFlt(gridID, CDI_GLOBAL, "straight_vertical_longitude_from_pole", CDI_DATATYPE_FLT64, 1, &lon_0);
cdiDefAttFlt(gridID, CDI_GLOBAL, "latitude_of_projection_origin", CDI_DATATYPE_FLT64, 1, &lat_0);
if ( a > 0 ) cdiDefAttFlt(gridID, CDI_GLOBAL, "earth_radius", CDI_DATATYPE_FLT64, 1, &a);
if ( IS_NOT_EQUAL(x_0, missval) ) cdiDefAttFlt(gridID, CDI_GLOBAL, "false_easting", CDI_DATATYPE_FLT64, 1, &x_0);
if ( IS_NOT_EQUAL(y_0, missval) ) cdiDefAttFlt(gridID, CDI_GLOBAL, "false_northing", CDI_DATATYPE_FLT64, 1, &y_0);
if ( IS_NOT_EQUAL(xval_0, missval) ) cdiDefAttFlt(gridID, CDI_GLOBAL, "longitudeOfFirstGridPointInDegrees", CDI_DATATYPE_FLT64, 1, &xval_0);
if ( IS_NOT_EQUAL(yval_0, missval) ) cdiDefAttFlt(gridID, CDI_GLOBAL, "latitudeOfFirstGridPointInDegrees", CDI_DATATYPE_FLT64, 1, &yval_0);

grid_t *gridptr = grid_to_pointer(gridID);
gridptr->projtype = CDI_PROJ_STERE;

gridVerifyProj(gridID);
}


int gridInqParamSTERE(int gridID, double missval, double *lon_0, double *lat_ts, double *lat_0,
                    double *a, double *xval_0, double *yval_0, double *x_0, double *y_0)
{
*a = 0;
*lon_0 = missval; *lat_ts = missval, *lat_0 = missval;
*xval_0 = missval; *yval_0 = missval; *x_0 = missval, *y_0 = missval;

int status = -1;
if ( gridInqType(gridID) != GRID_PROJECTION ) return status;

status = -2;
const char *projection = "polar_stereographic";
char mapname[CDI_MAX_NAME]; mapname[0] = 0;
cdiGridInqKeyStr(gridID, CDI_KEY_MAPNAME, CDI_MAX_NAME, mapname);
if ( mapname[0] && strcmp(mapname, projection) == 0 )
    {
    int atttype, attlen;
    char attname[CDI_MAX_NAME+1];

    int natts;
    cdiInqNatts(gridID, CDI_GLOBAL, &natts);

    if ( natts ) status = 0;

    for ( int iatt = 0; iatt < natts; ++iatt )
        {
        cdiInqAtt(gridID, CDI_GLOBAL, iatt, attname, &atttype, &attlen);
        if ( attlen > 2 ) continue;

        double attflt[2];
        if ( cdiInqAttConvertedToFloat(gridID, atttype, attname, attlen, attflt) )
            {
            if      ( strcmp(attname, "earth_radius") == 0 )                          *a      = attflt[0];
            else if ( strcmp(attname, "standard_parallel") == 0 )                     *lat_ts = attflt[0];
            else if ( strcmp(attname, "straight_vertical_longitude_from_pole") == 0 ) *lon_0  = attflt[0];
            else if ( strcmp(attname, "latitude_of_projection_origin") == 0 )         *lat_0  = attflt[0];
            else if ( strcmp(attname, "false_easting")  == 0 )                        *x_0    = attflt[0];
            else if ( strcmp(attname, "false_northing") == 0 )                        *y_0    = attflt[0];
            else if ( strcmp(attname, "longitudeOfFirstGridPointInDegrees") == 0 )    *xval_0 = attflt[0];
            else if ( strcmp(attname, "latitudeOfFirstGridPointInDegrees")  == 0 )    *yval_0 = attflt[0];
            }
        }
    }

return status;
}


void gridDefComplexPacking(int gridID, int lcomplex)
{
grid_t *gridptr = grid_to_pointer(gridID);

if (gridptr->lcomplex != lcomplex)
    {
    gridptr->lcomplex = lcomplex != 0;
    gridMark4Update(gridID);
    }
}


int gridInqComplexPacking(int gridID)
{
grid_t* gridptr = grid_to_pointer(gridID);

return (int)gridptr->lcomplex;
}


void gridDefHasDims(int gridID, int hasdims)
{
grid_t* gridptr = grid_to_pointer(gridID);

if ( gridptr->hasdims != (hasdims != 0) )
    {
    gridptr->hasdims = hasdims != 0;
    gridMark4Update(gridID);
    }
}


int gridInqHasDims(int gridID)
{
grid_t* gridptr = grid_to_pointer(gridID);

return (int)gridptr->hasdims;
}


void gridDefNumber(int gridID, int number)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( gridptr->number != number )
    {
    gridptr->number = number;
    gridMark4Update(gridID);
    }
}


int gridInqNumber(int gridID)
{
grid_t* gridptr = grid_to_pointer(gridID);
return gridptr->number;
}


void gridDefPosition(int gridID, int position)
{
grid_t* gridptr = grid_to_pointer(gridID);

if ( gridptr->position != position )
    {
    gridptr->position = position;
    gridMark4Update(gridID);
    }
}


int gridInqPosition(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->position;
}


void gridDefReference(int gridID, const char *reference)
{
grid_t* gridptr = grid_to_pointer(gridID);

if ( reference )
    {
    if ( gridptr->reference )
        {
        Free(gridptr->reference);
        gridptr->reference = NULL;
        }

    gridptr->reference = strdupx(reference);
    gridMark4Update(gridID);
    }
}


int gridInqReference(int gridID, char *reference)
{
size_t len = 0;
grid_t* gridptr = grid_to_pointer(gridID);

if ( gridptr->reference )
    {
    len = strlen(gridptr->reference);
    if ( reference )
        strcpy(reference, gridptr->reference);
    }

return (int)len;
}

const char *gridInqReferencePtr(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->reference;
}


void gridDefUUID(int gridID, const unsigned char uuid[CDI_UUID_SIZE])
{
grid_t* gridptr = grid_to_pointer(gridID);

memcpy(gridptr->uuid, uuid, CDI_UUID_SIZE);
gridMark4Update(gridID);
}


void gridInqUUID(int gridID, unsigned char uuid[CDI_UUID_SIZE])
{
grid_t *gridptr = grid_to_pointer(gridID);

memcpy(uuid, gridptr->uuid, CDI_UUID_SIZE);
}


void gridDefUvRelativeToGrid(int gridID, int uvRelativeToGrid)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( gridptr->uvRelativeToGrid != uvRelativeToGrid )
    {
    gridMark4Update(gridID);
    gridptr->uvRelativeToGrid = (bool)uvRelativeToGrid;
    }
}


int gridInqUvRelativeToGrid(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);
return gridptr->uvRelativeToGrid;
}


void gridDefScanningMode(int gridID, int mode)
{
grid_t *gridptr = grid_to_pointer(gridID);

if ( gridptr->scanningMode != mode )
    {
    gridMark4Update(gridID);
    gridptr->scanningMode = mode;
    }
}


int gridInqScanningMode(int gridID)
{
grid_t *gridptr = grid_to_pointer(gridID);

int scanningModeTMP  = 128 * gridptr->iScansNegatively + 64 * gridptr->jScansPositively + 32 * gridptr->jPointsAreConsecutive;
if ( scanningModeTMP != gridptr->scanningMode )
    Message("WARNING: scanningMode (%d) ! = (%d) 128 * iScansNegatively(%d) + 64 * jScansPositively(%d) + 32 * jPointsAreConsecutive(%d) ",
            gridptr->scanningMode, scanningModeTMP, gridptr->iScansNegatively,gridptr->jScansPositively,gridptr->jPointsAreConsecutive );

return gridptr->scanningMode;
}


void cdiGridGetIndexList(unsigned ngrids, int * gridIndexList)
{
reshGetResHListOfType(ngrids, gridIndexList, &gridOps);
}


static int
gridTxCode ()
{
return GRID;
}

enum {
GRID_PACK_INT_IDX_SELF,
GRID_PACK_INT_IDX_TYPE,
GRID_PACK_INT_IDX_DATATYPE,
GRID_PACK_INT_IDX_IS_CYCLIC,
GRID_PACK_INT_IDX_X_FLAG,
GRID_PACK_INT_IDX_Y_FLAG,
GRID_PACK_INT_IDX_GME_ND,
GRID_PACK_INT_IDX_GME_NI,
GRID_PACK_INT_IDX_GME_NI2,
GRID_PACK_INT_IDX_GME_NI3,
GRID_PACK_INT_IDX_NUMBER,
GRID_PACK_INT_IDX_POSITION,
GRID_PACK_INT_IDX_TRUNC,
GRID_PACK_INT_IDX_NVERTEX,
GRID_PACK_INT_IDX_NROWLON,
GRID_PACK_INT_IDX_SIZE,
GRID_PACK_INT_IDX_X_SIZE,
GRID_PACK_INT_IDX_Y_SIZE,
GRID_PACK_INT_IDX_LCOMPLEX,
GRID_PACK_INT_IDX_MEMBERMASK,
GRID_PACK_INT_IDX_XTSTDNNAME,
GRID_PACK_INT_IDX_YTSTDNNAME,
GRID_PACK_INT_IDX_UVRELATIVETOGRID,
GRID_PACK_INT_IDX_ISCANSNEGATIVELY,
GRID_PACK_INT_IDX_JSCANSPOSITIVELY,
GRID_PACK_INT_IDX_JPOINTSARECONSECUTIVE,
GRID_PACK_INT_IDX_SCANNINGMODE,
gridNint
};

enum {
GRID_PACK_DBL_IDX_X_FIRST,
GRID_PACK_DBL_IDX_Y_FIRST,
GRID_PACK_DBL_IDX_X_LAST,
GRID_PACK_DBL_IDX_Y_LAST,
GRID_PACK_DBL_IDX_X_INC,
GRID_PACK_DBL_IDX_Y_INC,
gridNdouble
};

enum {
    gridHasMaskFlag = 1 << 0,
    gridHasGMEMaskFlag = 1 << 1,
    gridHasXValsFlag = 1 << 2,
    gridHasYValsFlag = 1 << 3,
    gridHasAreaFlag = 1 << 4,
    gridHasXBoundsFlag = 1 << 5,
    gridHasYBoundsFlag = 1 << 6,
    gridHasReferenceFlag = 1 << 7,
    gridHasRowLonFlag = 1 << 8,
    gridHasUUIDFlag = 1 << 9,
};


static int gridGetComponentFlags(const grid_t * gridP)
{
int flags = (gridHasMaskFlag & (int)((unsigned)(gridP->mask == NULL) - 1U))
    | (gridHasGMEMaskFlag & (int)((unsigned)(gridP->mask_gme == NULL) - 1U))
    | (gridHasXValsFlag
    & (int)((unsigned)(gridP->vtable->inqXValsPtr((grid_t *)gridP) == NULL) - 1U))
    | (gridHasYValsFlag
    & (int)((unsigned)(gridP->vtable->inqYValsPtr((grid_t *)gridP) == NULL) - 1U))
    | (gridHasAreaFlag
    & (int)((unsigned)(gridP->vtable->inqAreaPtr((grid_t *)gridP) == NULL)
            - 1U))
    | (gridHasXBoundsFlag & (int)((unsigned)(gridP->x.bounds == NULL) - 1U))
    | (gridHasYBoundsFlag & (int)((unsigned)(gridP->y.bounds == NULL) - 1U))
    | (gridHasReferenceFlag & (int)((unsigned)(gridP->reference == NULL) - 1U))
    | (gridHasRowLonFlag & (int)((unsigned)(gridP->rowlon == NULL) - 1U))
    | (gridHasUUIDFlag & (int)((unsigned)cdiUUIDIsNull(gridP->uuid) - 1U));
return flags;
}

static int
gridGetPackSize(void * voidP, void *context)
{
grid_t * gridP = ( grid_t * ) voidP;
int packBuffSize = 0, count;

packBuffSize += serializeGetSize(gridNint, CDI_DATATYPE_INT, context)
    + serializeGetSize(1, CDI_DATATYPE_UINT32, context);

if (gridP->rowlon)
    {
    xassert(gridP->nrowlon);
    packBuffSize += serializeGetSize(gridP->nrowlon, CDI_DATATYPE_INT, context)
        + serializeGetSize( 1, CDI_DATATYPE_UINT32, context);
    }

packBuffSize += serializeGetSize(gridNdouble, CDI_DATATYPE_FLT64, context);

if (gridP->vtable->inqXValsPtr(gridP))
    {
    if (gridP->type == GRID_UNSTRUCTURED || gridP->type == GRID_CURVILINEAR)
        count = gridP->size;
    else
        count = gridP->x.size;
    xassert(count);
    packBuffSize += serializeGetSize(count, CDI_DATATYPE_FLT64, context)
        + serializeGetSize(1, CDI_DATATYPE_UINT32, context);
    }

if (gridP->vtable->inqYValsPtr(gridP))
    {
    if (gridP->type == GRID_UNSTRUCTURED || gridP->type == GRID_CURVILINEAR)
        count = gridP->size;
    else
        count = gridP->y.size;
    xassert(count);
    packBuffSize += serializeGetSize(count, CDI_DATATYPE_FLT64, context)
        + serializeGetSize(1, CDI_DATATYPE_UINT32, context);
    }

if (gridP->vtable->inqAreaPtr(gridP))
    {
    xassert(gridP->size);
    packBuffSize +=
        serializeGetSize(gridP->size, CDI_DATATYPE_FLT64, context)
        + serializeGetSize(1, CDI_DATATYPE_UINT32, context);
    }

if (gridP->x.bounds)
    {
    xassert(gridP->nvertex);
    if (gridP->type == GRID_CURVILINEAR || gridP->type == GRID_UNSTRUCTURED)
        count = gridP->size;
    else
        count = gridP->x.size;
    xassert(count);
    packBuffSize
        += (serializeGetSize(gridP->nvertex * count, CDI_DATATYPE_FLT64, context)
            + serializeGetSize(1, CDI_DATATYPE_UINT32, context));
    }

if (gridP->y.bounds)
    {
    xassert(gridP->nvertex);
    if (gridP->type == GRID_CURVILINEAR || gridP->type == GRID_UNSTRUCTURED)
        count = gridP->size;
    else
        count = gridP->y.size;
    xassert(count);
    packBuffSize
        += (serializeGetSize(gridP->nvertex * count, CDI_DATATYPE_FLT64, context)
            + serializeGetSize(1, CDI_DATATYPE_UINT32, context));
    }

{
    const char *strTab[] = GRID_STR_SERIALIZE(gridP);
    int numStr = (int)(sizeof (strTab) / sizeof (strTab[0]));
    packBuffSize
    += serializeStrTabGetPackSize(strTab, numStr, context);
}

if (gridP->reference)
    {
    size_t len = strlen(gridP->reference);
    packBuffSize += serializeGetSize(1, CDI_DATATYPE_INT, context)
        + serializeGetSize((int)len + 1, CDI_DATATYPE_TXT, context)
        + serializeGetSize(1, CDI_DATATYPE_UINT32, context);
    }

if (gridP->mask)
    {
    xassert(gridP->size);
    packBuffSize
        += serializeGetSize(gridP->size, CDI_DATATYPE_UCHAR, context)
        + serializeGetSize(1, CDI_DATATYPE_UINT32, context);
    }

if (gridP->mask_gme)
    {
    xassert(gridP->size);
    packBuffSize += serializeGetSize(gridP->size, CDI_DATATYPE_UCHAR, context)
        + serializeGetSize(1, CDI_DATATYPE_UINT32, context);
    }

if (!cdiUUIDIsNull(gridP->uuid))
    packBuffSize += serializeGetSize(CDI_UUID_SIZE, CDI_DATATYPE_UCHAR, context);

return packBuffSize;
}

void
gridUnpack(char * unpackBuffer, int unpackBufferSize,
        int * unpackBufferPos, int originNamespace, void *context,
        int force_id)
{
grid_t * gridP;
uint32_t d;
int memberMask, size;

gridInit();

{
    int intBuffer[gridNint];
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    intBuffer, gridNint, CDI_DATATYPE_INT, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);

    xassert(cdiCheckSum(CDI_DATATYPE_INT, gridNint, intBuffer) == d);
    int targetID = namespaceAdaptKey(intBuffer[0], originNamespace);
    gridP = gridNewEntry(force_id?targetID:CDI_UNDEFID);

    xassert(!force_id || targetID == gridP->self);

    gridP->type          =   intBuffer[GRID_PACK_INT_IDX_TYPE];
    gridP->datatype      =   intBuffer[GRID_PACK_INT_IDX_DATATYPE];
    gridP->isCyclic      =   (signed char)intBuffer[GRID_PACK_INT_IDX_IS_CYCLIC];
    gridP->x.flag        =   (short)intBuffer[GRID_PACK_INT_IDX_X_FLAG];
    gridP->y.flag        =   (short)intBuffer[GRID_PACK_INT_IDX_Y_FLAG];
    gridP->gme.nd        =   intBuffer[GRID_PACK_INT_IDX_GME_ND];
    gridP->gme.ni        =   intBuffer[GRID_PACK_INT_IDX_GME_NI];
    gridP->gme.ni2       =   intBuffer[GRID_PACK_INT_IDX_GME_NI2];
    gridP->gme.ni3       =   intBuffer[GRID_PACK_INT_IDX_GME_NI3];
    gridP->number        =   intBuffer[GRID_PACK_INT_IDX_NUMBER];
    gridP->position      =   intBuffer[GRID_PACK_INT_IDX_POSITION];
    gridP->trunc         =   intBuffer[GRID_PACK_INT_IDX_TRUNC];
    gridP->nvertex       =   intBuffer[GRID_PACK_INT_IDX_NVERTEX];
    gridP->nrowlon       =   intBuffer[GRID_PACK_INT_IDX_NROWLON];
    gridP->size          =   intBuffer[GRID_PACK_INT_IDX_SIZE];
    gridP->x.size        =   intBuffer[GRID_PACK_INT_IDX_X_SIZE];
    gridP->y.size        =   intBuffer[GRID_PACK_INT_IDX_Y_SIZE];
    gridP->lcomplex      =   (bool)intBuffer[GRID_PACK_INT_IDX_LCOMPLEX];
    memberMask           =   intBuffer[GRID_PACK_INT_IDX_MEMBERMASK];
    //gridP->x.stdname     =
    //xystdname_tab[intBuffer[GRID_PACK_INT_IDX_XTSTDNNAME]][0];
    //gridP->y.stdname     =
    //xystdname_tab[intBuffer[GRID_PACK_INT_IDX_YTSTDNNAME]][1];
    gridP->uvRelativeToGrid         =   intBuffer[GRID_PACK_INT_IDX_UVRELATIVETOGRID];
    gridP->iScansNegatively         =   (bool)intBuffer[GRID_PACK_INT_IDX_ISCANSNEGATIVELY];
    gridP->jScansPositively         =   (bool)intBuffer[GRID_PACK_INT_IDX_JSCANSPOSITIVELY];
    gridP->jPointsAreConsecutive    =   (bool)intBuffer[GRID_PACK_INT_IDX_JPOINTSARECONSECUTIVE];
    gridP->scanningMode             =   intBuffer[GRID_PACK_INT_IDX_SCANNINGMODE];
}

if (memberMask & gridHasRowLonFlag)
    {
    xassert(gridP->nrowlon);
    gridP->rowlon = (int *) Malloc((size_t)gridP->nrowlon * sizeof (int));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    gridP->rowlon, gridP->nrowlon , CDI_DATATYPE_INT, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_INT, gridP->nrowlon, gridP->rowlon) == d);
    }

{
    double doubleBuffer[gridNdouble];
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    doubleBuffer, gridNdouble, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(d == cdiCheckSum(CDI_DATATYPE_FLT, gridNdouble, doubleBuffer));

    gridP->x.first = doubleBuffer[GRID_PACK_DBL_IDX_X_FIRST];
    gridP->y.first = doubleBuffer[GRID_PACK_DBL_IDX_Y_FIRST];
    gridP->x.last = doubleBuffer[GRID_PACK_DBL_IDX_X_LAST];
    gridP->y.last = doubleBuffer[GRID_PACK_DBL_IDX_Y_LAST];
    gridP->x.inc = doubleBuffer[GRID_PACK_DBL_IDX_X_INC];
    gridP->y.inc = doubleBuffer[GRID_PACK_DBL_IDX_Y_INC];
}

bool irregular = gridP->type == GRID_UNSTRUCTURED
    || gridP->type == GRID_CURVILINEAR;
if (memberMask & gridHasXValsFlag)
    {
    size = irregular ? gridP->size : gridP->x.size;

    gridP->x.vals = (double *) Malloc(size * sizeof (double));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    gridP->x.vals, size, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_FLT, size, gridP->x.vals) == d );
    }

if (memberMask & gridHasYValsFlag)
    {
    size = irregular ? gridP->size : gridP->y.size;

    gridP->y.vals = (double *) Malloc(size * sizeof (double));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    gridP->y.vals, size, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_FLT, size, gridP->y.vals) == d);
    }

if (memberMask & gridHasAreaFlag)
    {
    size = gridP->size;
    xassert(size);
    gridP->area = (double *) Malloc(size * sizeof (double));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    gridP->area, size, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_FLT, size, gridP->area) == d);
    }

if (memberMask & gridHasXBoundsFlag)
    {
    size = gridP->nvertex * (irregular ? gridP->size : gridP->x.size);
    xassert(size);

    gridP->x.bounds = (double *) Malloc(size * sizeof (double));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    gridP->x.bounds, size, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_FLT, size, gridP->x.bounds) == d);
    }

if (memberMask & gridHasYBoundsFlag)
    {
    size = gridP->nvertex * (irregular ? gridP->size : gridP->y.size);
    xassert(size);

    gridP->y.bounds = (double *) Malloc(size * sizeof (double));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                        gridP->y.bounds, size, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_FLT, size, gridP->y.bounds) == d);
    }

{
    char *strTab[] = GRID_STR_SERIALIZE(gridP);
    int numStr = sizeof (strTab) / sizeof (strTab[0]);
    serializeStrTabUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                        strTab, numStr, context);
}

if (memberMask & gridHasReferenceFlag)
    {
    int referenceSize;
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &referenceSize, 1, CDI_DATATYPE_INT, context);
    gridP->reference = (char *) Malloc(referenceSize);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    gridP->reference, referenceSize, CDI_DATATYPE_TXT, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_TXT, referenceSize, gridP->reference) == d);
    }

if (memberMask & gridHasMaskFlag)
    {
    xassert((size = gridP->size));
    gridP->mask = (mask_t *) Malloc(size * sizeof (mask_t));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    gridP->mask, gridP->size, CDI_DATATYPE_UCHAR, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_UCHAR, gridP->size, gridP->mask) == d);
    }

if (memberMask & gridHasGMEMaskFlag)
    {
    xassert((size = gridP->size));
    gridP->mask_gme = (mask_t *) Malloc(size * sizeof (mask_t));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    gridP->mask_gme, gridP->size, CDI_DATATYPE_UCHAR, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_UCHAR, gridP->size, gridP->mask_gme) == d);
    }
if (memberMask & gridHasUUIDFlag)
    {
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    gridP->uuid, CDI_UUID_SIZE, CDI_DATATYPE_UCHAR, context);
    }

reshSetStatus(gridP->self, &gridOps,
                reshGetStatus(gridP->self, &gridOps) & ~RESH_SYNC_BIT);
}


static void
gridPack(void * voidP, void * packBuffer, int packBufferSize,
        int * packBufferPos, void *context)
{
grid_t   * gridP = ( grid_t * )   voidP;
int size;
uint32_t d;
int memberMask;

{
    int intBuffer[gridNint];

    intBuffer[GRID_PACK_INT_IDX_SELF]         = gridP->self;
    intBuffer[GRID_PACK_INT_IDX_TYPE]         = gridP->type;
    intBuffer[GRID_PACK_INT_IDX_DATATYPE]     = gridP->datatype;
    intBuffer[GRID_PACK_INT_IDX_IS_CYCLIC]    = gridP->isCyclic;
    intBuffer[GRID_PACK_INT_IDX_X_FLAG]       = gridP->x.flag;
    intBuffer[GRID_PACK_INT_IDX_Y_FLAG]       = gridP->y.flag;
    intBuffer[GRID_PACK_INT_IDX_GME_ND]       = gridP->gme.nd;
    intBuffer[GRID_PACK_INT_IDX_GME_NI]       = gridP->gme.ni;
    intBuffer[GRID_PACK_INT_IDX_GME_NI2]      = gridP->gme.ni2;
    intBuffer[GRID_PACK_INT_IDX_GME_NI3]      = gridP->gme.ni3;
    intBuffer[GRID_PACK_INT_IDX_NUMBER]       = gridP->number;
    intBuffer[GRID_PACK_INT_IDX_POSITION]     = gridP->position;
    intBuffer[GRID_PACK_INT_IDX_TRUNC]        = gridP->trunc;
    intBuffer[GRID_PACK_INT_IDX_NVERTEX]      = gridP->nvertex;
    intBuffer[GRID_PACK_INT_IDX_NROWLON]      = gridP->nrowlon;
    intBuffer[GRID_PACK_INT_IDX_SIZE]         = gridP->size;
    intBuffer[GRID_PACK_INT_IDX_X_SIZE]       = gridP->x.size;
    intBuffer[GRID_PACK_INT_IDX_Y_SIZE]       = gridP->y.size;
    intBuffer[GRID_PACK_INT_IDX_LCOMPLEX]     = gridP->lcomplex;
    intBuffer[GRID_PACK_INT_IDX_MEMBERMASK]   = memberMask
                                            = gridGetComponentFlags(gridP);
    intBuffer[GRID_PACK_INT_IDX_XTSTDNNAME]   =
    //(int)((const char (*)[2][30])gridP->x.stdname - xystdname_tab);
    intBuffer[GRID_PACK_INT_IDX_YTSTDNNAME]   =
    //(int)((const char (*)[2][30])gridP->y.stdname
    //        - (const char (*)[2][30])xystdname_tab[0][1]);

    intBuffer[GRID_PACK_INT_IDX_UVRELATIVETOGRID] = gridP->uvRelativeToGrid;
    intBuffer[GRID_PACK_INT_IDX_ISCANSNEGATIVELY] = gridP->iScansNegatively;
    intBuffer[GRID_PACK_INT_IDX_JSCANSPOSITIVELY] = gridP->jScansPositively;
    intBuffer[GRID_PACK_INT_IDX_JPOINTSARECONSECUTIVE] = gridP->jPointsAreConsecutive;
    intBuffer[GRID_PACK_INT_IDX_SCANNINGMODE] = gridP->scanningMode;

    serializePack(intBuffer, gridNint, CDI_DATATYPE_INT,
                packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_INT, gridNint, intBuffer);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                packBuffer, packBufferSize, packBufferPos, context);
}

if (memberMask & gridHasRowLonFlag)
    {
    size = gridP->nrowlon;
    xassert(size > 0);
    serializePack(gridP->rowlon, size, CDI_DATATYPE_INT,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_INT , size, gridP->rowlon);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

{
    double doubleBuffer[gridNdouble];

    doubleBuffer[GRID_PACK_DBL_IDX_X_FIRST]        = gridP->x.first;
    doubleBuffer[GRID_PACK_DBL_IDX_Y_FIRST]        = gridP->y.first;
    doubleBuffer[GRID_PACK_DBL_IDX_X_LAST]         = gridP->x.last;
    doubleBuffer[GRID_PACK_DBL_IDX_Y_LAST]         = gridP->y.last;
    doubleBuffer[GRID_PACK_DBL_IDX_X_INC]          = gridP->x.inc;
    doubleBuffer[GRID_PACK_DBL_IDX_Y_INC]          = gridP->y.inc;

    serializePack(doubleBuffer, gridNdouble, CDI_DATATYPE_FLT64,
                packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT, gridNdouble, doubleBuffer);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                packBuffer, packBufferSize, packBufferPos, context);
}

if (memberMask & gridHasXValsFlag)
    {
    if (gridP->type == GRID_UNSTRUCTURED || gridP->type == GRID_CURVILINEAR)
        size = gridP->size;
    else
        size = gridP->x.size;
    xassert(size);

    const double *gridP_xvals = gridP->vtable->inqXValsPtr(gridP);
    serializePack(gridP_xvals, size, CDI_DATATYPE_FLT64,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT, size, gridP_xvals);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & gridHasYValsFlag)
    {
    if (gridP->type == GRID_UNSTRUCTURED || gridP->type == GRID_CURVILINEAR )
        size = gridP->size;
    else
        size = gridP->y.size;
    xassert(size);
    const double *gridP_yvals = gridP->vtable->inqYValsPtr(gridP);
    serializePack(gridP_yvals, size, CDI_DATATYPE_FLT64,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT, size, gridP_yvals);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & gridHasAreaFlag)
    {
    xassert(gridP->size);

    serializePack(gridP->area, gridP->size, CDI_DATATYPE_FLT64,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT, gridP->size, gridP->area);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & gridHasXBoundsFlag)
    {
    xassert ( gridP->nvertex );
    if (gridP->type == GRID_CURVILINEAR || gridP->type == GRID_UNSTRUCTURED)
        size = gridP->nvertex * gridP->size;
    else
        size = gridP->nvertex * gridP->x.size;
    xassert ( size );

    serializePack(gridP->x.bounds, size, CDI_DATATYPE_FLT64,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT, size, gridP->x.bounds);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & gridHasYBoundsFlag)
    {
    xassert(gridP->nvertex);
    if (gridP->type == GRID_CURVILINEAR || gridP->type == GRID_UNSTRUCTURED)
        size = gridP->nvertex * gridP->size;
    else
        size = gridP->nvertex * gridP->y.size;
    xassert ( size );

    serializePack(gridP->y.bounds, size, CDI_DATATYPE_FLT64,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT, size, gridP->y.bounds);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

{
    const char *strTab[] = GRID_STR_SERIALIZE(gridP);
    int numStr = sizeof (strTab) / sizeof (strTab[0]);
    serializeStrTabPack(strTab, numStr,
                        packBuffer, packBufferSize, packBufferPos, context);
}

if (memberMask & gridHasReferenceFlag)
    {
    size = (int)strlen(gridP->reference) + 1;
    serializePack(&size, 1, CDI_DATATYPE_INT,
                    packBuffer, packBufferSize, packBufferPos, context);
    serializePack(gridP->reference, size, CDI_DATATYPE_TXT,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_TXT, size, gridP->reference);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & gridHasMaskFlag)
    {
    xassert((size = gridP->size));
    serializePack(gridP->mask, size, CDI_DATATYPE_UCHAR,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_UCHAR, size, gridP->mask);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & gridHasGMEMaskFlag)
    {
    xassert((size = gridP->size));

    serializePack(gridP->mask_gme, size, CDI_DATATYPE_UCHAR,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_UCHAR, size, gridP->mask_gme);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & gridHasUUIDFlag)
    serializePack(gridP->uuid, CDI_UUID_SIZE, CDI_DATATYPE_UCHAR,
                packBuffer, packBufferSize, packBufferPos, context);
}

#undef GRID_STR_SERIALIZE


struct gridCompareSearchState
{
int resIDValue;
const grid_t *queryKey;
};

static enum cdiApplyRet
gridCompareSearch(int id, void *res, void *data)
{
struct gridCompareSearchState *state = (struct gridCompareSearchState*)data;
(void)res;
if ( gridCompare(id, state->queryKey, true) == false )
    {
    state->resIDValue = id;
    return CDI_APPLY_STOP;
    }
else
    return CDI_APPLY_GO_ON;
}


struct addIfNewRes cdiVlistAddGridIfNew(int vlistID, grid_t *grid, int mode)
{

bool gridglobdefined = false;
bool griddefined = false;
int gridID = CDI_UNDEFID;
vlist_t *vlistptr = vlist_to_pointer(vlistID);

unsigned ngrids = (unsigned)vlistptr->ngrids;

if ( mode == 0 )
    for ( unsigned index = 0; index < ngrids; index++ )
    {
        if ( (gridID = vlistptr->gridIDs[index]) != CDI_UNDEFID )
        {
            if ( gridCompare(gridID, grid, false) == false )
            {
                griddefined = true;
                break;
            }
        }
        else
        Error("Internal problem: undefined gridID in vlist %d, position %u!", vlistID, index);
    }

if ( ! griddefined )
    {
    struct gridCompareSearchState query;
    query.queryKey = grid;
    if ( (gridglobdefined = (cdiResHFilterApply(&gridOps, gridCompareSearch, &query)
            == CDI_APPLY_STOP)) )
        gridID = query.resIDValue;

    if ( mode == 1 && gridglobdefined )
        for ( unsigned index = 0; index < ngrids; index++ )
        if ( vlistptr->gridIDs[index] == gridID )
            {
            gridglobdefined = false;
            break;
            }
    }

if ( ! griddefined )
    {
    if ( ! gridglobdefined )
        {
        grid->self = gridID = reshPut(grid, &gridOps);
        gridComplete(grid);
        }
    if ( mode < 2 )
        {
        vlistptr->gridIDs[ngrids] = gridID;
        vlistptr->ngrids++;
        }
    }

return (struct addIfNewRes){ .Id = gridID, .isNew = !griddefined && !gridglobdefined };
}


const struct gridVirtTable cdiGridVtable
= {
.destroy = gridDestroyKernel,
.copy = grid_copy_base,
.copyScalarFields = grid_copy_base_scalar_fields,
.copyArrayFields = grid_copy_base_array_fields,
.defXVals = gridDefXValsSerial,
.defYVals = gridDefYValsSerial,
.defMask = gridDefMaskSerial,
.defMaskGME = gridDefMaskGMESerial,
.defXBounds = gridDefXBoundsSerial,
.defYBounds = gridDefYBoundsSerial,
.defArea = gridDefAreaSerial,
.inqXVal = gridInqXValSerial,
.inqYVal = gridInqYValSerial,
.inqXVals = gridInqXValsSerial,
.inqXValsPart = gridInqXValsPartSerial,
.inqXCvals = gridInqXCvalsSerial,
.inqXIsc = gridInqXIscSerial,
.inqYVals = gridInqYValsSerial,
.inqYValsPart = gridInqYValsPartSerial,
.inqYCvals = gridInqYCvalsSerial,
.inqYIsc = gridInqYIscSerial,
.inqXValsPtr = gridInqXValsPtrSerial,
.inqYValsPtr = gridInqYValsPtrSerial,
.inqXCvalsPtr = gridInqXCvalsPtrSerial,
.inqYCvalsPtr = gridInqYCvalsPtrSerial,
.compareXYFull = compareXYvals,
.compareXYAO = compareXYvals2,
.inqArea = gridInqAreaSerial,
.inqAreaPtr = gridInqAreaPtrBase,
.hasArea = gridHasAreaBase,
.inqMask = gridInqMaskSerial,
.inqMaskGME = gridInqMaskGMESerial,
.inqXBounds = gridInqXBoundsSerial,
.inqYBounds = gridInqYBoundsSerial,
.inqXBoundsPtr = gridInqXBoundsPtrSerial,
.inqYBoundsPtr = gridInqYBoundsPtrSerial,
};


#ifndef INSTITUTION_H
#define INSTITUTION_H

int
instituteUnpack(void *buf, int size, int *position, int originNamespace,
                void *context, int force_id);

void instituteDefaultEntries(void);

#endif


#include <assert.h>
#include <limits.h>



typedef struct
{
int    self;
int    used;
int    center;
int    subcenter;
char  *name;
char  *longname;
}
institute_t;


static int instituteCompareKernel(institute_t *ip1, institute_t *ip2);
static void instituteDestroyP(institute_t *instituteptr);
static void   institutePrintP(institute_t *instituteptr, FILE * fp);
static int instituteGetPackSize(institute_t *instituteptr, void *context);
static void   institutePackP    ( void * instituteptr, void *buf, int size, int *position, void *context );
static int    instituteTxCode   ( void );

static const resOps instituteOps = {
(int (*)(void *, void *))instituteCompareKernel,
(void (*)(void *))instituteDestroyP,
(void (*)(void *, FILE *))institutePrintP,
(int (*)(void *, void *))instituteGetPackSize,
institutePackP,
instituteTxCode
};

static
void instituteDefaultValue ( institute_t * instituteptr )
{
instituteptr->self       = CDI_UNDEFID;
instituteptr->used       = 0;
instituteptr->center     = CDI_UNDEFID;
instituteptr->subcenter  = CDI_UNDEFID;
instituteptr->name       = NULL;
instituteptr->longname   = NULL;
}

void instituteDefaultEntries ( void )
{
cdiResH resH[]
    = { institutDef( 98,   0, "ECMWF",     "European Centre for Medium-Range Weather Forecasts"),
        institutDef(252,   1, "MPIMET",    "Max-Planck-Institute for Meteorology"),
        institutDef( 98, 232, "MPIMET",    "Max Planck Institute for Meteorology"),
        institutDef( 98, 255, "MPIMET",    "Max-Planck-Institute for Meteorology"),
        institutDef( 78, 255, "DWD",       "Deutscher Wetterdienst"),
        institutDef( 78,   0, "DWD",       "Deutscher Wetterdienst"),
        institutDef(215, 255, "MCH",       "MeteoSwiss"),
        institutDef(  7,   0, "NCEP",      "National Centers for Environmental Prediction"),
        institutDef(  7,   1, "NCEP",      "National Centers for Environmental Prediction"),
        institutDef( 60,   0, "NCAR",      "National Center for Atmospheric Research"),
        institutDef( 74,   0, "METOFFICE", "U.K. Met Office"),
        institutDef( 97,   0, "ESA",       "European Space Agency"),
        institutDef( 99,   0, "KNMI",      "Royal Netherlands Meteorological Institute"),
};


size_t n = sizeof(resH)/sizeof(*resH);

for (size_t i = 0; i < n ; i++ )
    reshSetStatus(resH[i], &instituteOps, RESH_IN_USE);
}


static int
instituteCompareKernel(institute_t *  ip1, institute_t * ip2)
{
int differ = 0;
size_t len1, len2;

if ( ip1->name )
    {
    if ( ip1->center    > 0 && ip2->center    != ip1->center )    differ = 1;
    if ( ip1->subcenter > 0 && ip2->subcenter != ip1->subcenter ) differ = 1;

    if ( !differ )
        {
        if ( ip2->name )
            {
            len1 = strlen(ip1->name);
            len2 = strlen(ip2->name);
            if ( (len1 != len2) || memcmp(ip2->name, ip1->name, len2) ) differ = 1;
            }
        }
    }
else if ( ip1->longname )
    {
    if ( ip2->longname )
        {
        len1 = strlen(ip1->longname);
        len2 = strlen(ip2->longname);
        if ( (len1 < len2) || memcmp(ip2->longname, ip1->longname, len2) ) differ = 1;
        }
    }
else
    {
    if ( !( ip2->center    == ip1->center &&
            ip2->subcenter == ip1->subcenter )) differ = 1;
    }

return differ;
}


struct instLoc
{
institute_t *ip;
int id;
};

static enum cdiApplyRet
findInstitute(int id, void *res, void *data)
{
institute_t * ip1 = ((struct instLoc *)data)->ip;
institute_t * ip2 = (institute_t*) res;
if (ip2->used && !instituteCompareKernel(ip1, ip2))
    {
    ((struct instLoc *)data)->id = id;
    return CDI_APPLY_STOP;
    }
else
    return CDI_APPLY_GO_ON;
}


int institutInq(int center, int subcenter, const char *name, const char *longname)
{
institute_t * ip_ref = (institute_t *) Malloc(sizeof (*ip_ref));
ip_ref->self       = CDI_UNDEFID;
ip_ref->used       = 0;
ip_ref->center     = center;
ip_ref->subcenter  = subcenter;
ip_ref->name       = name && name[0] ? (char *)name : NULL;
ip_ref->longname   = longname && longname[0] ? (char *)longname : NULL;

struct instLoc state = { .ip = ip_ref, .id = CDI_UNDEFID };
cdiResHFilterApply(&instituteOps, findInstitute, &state);

Free(ip_ref);

return state.id;
}

static
institute_t *instituteNewEntry(cdiResH resH, int center, int subcenter,
                            const char *name, const char *longname)
{
institute_t *instituteptr = (institute_t*) Malloc(sizeof(institute_t));
instituteDefaultValue(instituteptr);
if (resH == CDI_UNDEFID)
    instituteptr->self = reshPut(instituteptr, &instituteOps);
else
    {
    instituteptr->self = resH;
    reshReplace(resH, instituteptr, &instituteOps);
    }
instituteptr->used = 1;
instituteptr->center = center;
instituteptr->subcenter = subcenter;
if ( name && *name )
    instituteptr->name = strdupx(name);
if (longname && *longname)
    instituteptr->longname = strdupx(longname);
return  instituteptr;
}


int institutDef(int center, int subcenter, const char *name, const char *longname)
{
institute_t * instituteptr
    = instituteNewEntry(CDI_UNDEFID, center, subcenter, name, longname);
return instituteptr->self;
}


int institutInqCenter(int instID)
{
institute_t * instituteptr = NULL;

if ( instID != CDI_UNDEFID )
    instituteptr = ( institute_t * ) reshGetVal ( instID, &instituteOps );

return  instituteptr ? instituteptr->center : CDI_UNDEFID;
}


int institutInqSubcenter(int instID)
{
institute_t * instituteptr = NULL;

if ( instID != CDI_UNDEFID )
    instituteptr = ( institute_t * ) reshGetVal ( instID, &instituteOps );

return instituteptr ? instituteptr->subcenter: CDI_UNDEFID;
}


const char *institutInqNamePtr(int instID)
{
institute_t * instituteptr = NULL;

if ( instID != CDI_UNDEFID )
    instituteptr = ( institute_t * ) reshGetVal ( instID, &instituteOps );

return instituteptr ? instituteptr->name : NULL;
}


const char *institutInqLongnamePtr(int instID)
{
institute_t * instituteptr = NULL;

if ( instID != CDI_UNDEFID )
    instituteptr = ( institute_t * ) reshGetVal ( instID, &instituteOps );

return instituteptr ? instituteptr->longname : NULL;
}

static enum cdiApplyRet
activeInstitutes(int id, void *res, void *data)
{
(void)id;
if (res && ((institute_t *)res)->used)
    ++(*(int *)data);
return CDI_APPLY_GO_ON;
}

int institutInqNumber(void)
{
int instNum = 0;

cdiResHFilterApply(&instituteOps, activeInstitutes, &instNum);
return instNum;
}


static void
instituteDestroyP(institute_t *instituteptr)
{
xassert(instituteptr);

int instituteID = instituteptr->self;
Free(instituteptr->name);
Free(instituteptr->longname);
reshRemove(instituteID, &instituteOps);
Free(instituteptr);
}


static void institutePrintP(institute_t *ip, FILE * fp )
{
if (ip)
    fprintf(fp, "#\n"
            "# instituteID %d\n"
            "#\n"
            "self          = %d\n"
            "used          = %d\n"
            "center        = %d\n"
            "subcenter     = %d\n"
            "name          = %s\n"
            "longname      = %s\n",
            ip->self, ip->self, ip->used, ip->center, ip->subcenter,
            ip->name ? ip->name : "NN",
            ip->longname ? ip->longname : "NN");
}


static int
instituteTxCode ( void )
{
return INSTITUTE;
}

enum {
institute_nints = 5,
};

static int instituteGetPackSize(institute_t *ip, void *context)
{
size_t namelen = strlen(ip->name), longnamelen = strlen(ip->longname);
xassert(namelen < INT_MAX && longnamelen < INT_MAX);
size_t txsize = (size_t)serializeGetSize(institute_nints, CDI_DATATYPE_INT, context)
    + (size_t)serializeGetSize((int)namelen + 1, CDI_DATATYPE_TXT, context)
    + (size_t)serializeGetSize((int)longnamelen + 1, CDI_DATATYPE_TXT, context);
xassert(txsize <= INT_MAX);
return (int)txsize;
}

static void institutePackP(void * instituteptr, void *buf, int size, int *position, void *context)
{
institute_t *p = (institute_t*) instituteptr;
int tempbuf[institute_nints];
tempbuf[0] = p->self;
tempbuf[1] = p->center;
tempbuf[2] = p->subcenter;
tempbuf[3] = (int)strlen(p->name) + 1;
tempbuf[4] = (int)strlen(p->longname) + 1;
serializePack(tempbuf, institute_nints, CDI_DATATYPE_INT, buf, size, position, context);
serializePack(p->name, tempbuf[3], CDI_DATATYPE_TXT, buf, size, position, context);
serializePack(p->longname, tempbuf[4], CDI_DATATYPE_TXT, buf, size, position, context);
}

int instituteUnpack(void *buf, int size, int *position, int originNamespace,
                    void *context, int force_id)
{
int tempbuf[institute_nints];
int instituteID;
serializeUnpack(buf, size, position, tempbuf, institute_nints, CDI_DATATYPE_INT, context);
char *name = (char *) Malloc((size_t)tempbuf[3] + (size_t)tempbuf[4]);
char *longname = name + tempbuf[3];
serializeUnpack(buf, size, position, name, tempbuf[3], CDI_DATATYPE_TXT, context);
serializeUnpack(buf, size, position, longname, tempbuf[4], CDI_DATATYPE_TXT, context);
int targetID = namespaceAdaptKey(tempbuf[0], originNamespace);
institute_t *ip = instituteNewEntry(force_id?targetID:CDI_UNDEFID,
                                    tempbuf[1], tempbuf[2], name, longname);
instituteID = ip->self;
xassert(!force_id || instituteID == targetID);
Free(name);
reshSetStatus(instituteID, &instituteOps,
                reshGetStatus(instituteID, &instituteOps) & ~RESH_SYNC_BIT);
return instituteID;
}


#ifndef MODEL_H
#define MODEL_H

int
modelUnpack(void *buf, int size, int *position,
            int originNamespace, void *context, int force_id);

void modelDefaultEntries(void);

#endif

#include <limits.h>


#undef  CDI_UNDEFID
#define CDI_UNDEFID -1

typedef struct
{
int      self;
int      used;
int      instID;
int      modelgribID;
char    *name;
}
model_t;


static int  MODEL_Debug = 0;

static void modelInit(void);


static int modelCompareP(void *modelptr1, void *modelptr2);
static void   modelDestroyP ( void * modelptr );
static void   modelPrintP   ( void * modelptr, FILE * fp );
static int    modelGetSizeP ( void * modelptr, void *context);
static void   modelPackP    ( void * modelptr, void * buff, int size,
                            int *position, void *context);
static int    modelTxCode   ( void );

static const resOps modelOps = {
modelCompareP,
modelDestroyP,
modelPrintP,
modelGetSizeP,
modelPackP,
modelTxCode
};

static
void modelDefaultValue ( model_t *modelptr )
{
modelptr->self        = CDI_UNDEFID;
modelptr->used        = 0;
modelptr->instID      = CDI_UNDEFID;
modelptr->modelgribID = CDI_UNDEFID;
modelptr->name        = NULL;
}

static model_t *
modelNewEntry(cdiResH resH, int instID, int modelgribID, const char *name)
{
model_t *modelptr = (model_t *) Malloc(sizeof(model_t));
modelDefaultValue ( modelptr );
if (resH == CDI_UNDEFID)
    modelptr->self = reshPut(modelptr, &modelOps);
else
    {
    modelptr->self = resH;
    reshReplace(resH, modelptr, &modelOps);
    }
modelptr->used = 1;
modelptr->instID = instID;
modelptr->modelgribID = modelgribID;
if ( name && *name ) modelptr->name = strdupx(name);

return (modelptr);
}

void modelDefaultEntries ( void )
{
int instID;
enum { nDefModels = 10 };
cdiResH resH[nDefModels];

instID  = institutInq(  0,   0, "ECMWF", NULL);



instID  = institutInq( 98, 232, "MPIMET", NULL);
resH[0] = modelDef(instID,  64, "ECHAM5.4");
resH[1] = modelDef(instID,  63, "ECHAM5.3");
resH[2] = modelDef(instID,  62, "ECHAM5.2");
resH[3] = modelDef(instID,  61, "ECHAM5.1");

instID  = institutInq( 98, 255, "MPIMET", NULL);
resH[4] = modelDef(instID,  60, "ECHAM5.0");
resH[5] = modelDef(instID,  50, "ECHAM4");
resH[6] = modelDef(instID, 110, "MPIOM1");

instID  = institutInq(  0,   0, "DWD", NULL);
resH[7] = modelDef(instID, 149, "GME");

instID  = institutInq(  0,   0, "MCH", NULL);

resH[8] = modelDef(instID, 255, "COSMO");

instID  = institutInq(  0,   1, "NCEP", NULL);
resH[9] = modelDef(instID,  80, "T62L28MRF");


for ( int i = 0; i < nDefModels ; i++ )
    reshSetStatus(resH[i], &modelOps, RESH_IN_USE);
}

static
void modelInit(void)
{
static bool modelInitialized = false;

if (modelInitialized) return;

modelInitialized = true;
char *env = getenv("MODEL_DEBUG");
if ( env ) MODEL_Debug = atoi(env);
}

struct modelLoc
{
const char *name;
int instID, modelgribID, resID;
};

static enum cdiApplyRet
findModelByID(int resID, void *res, void *data)
{
model_t *modelptr = (model_t*) res;
struct modelLoc *ret = (struct modelLoc*) data;
int instID = ret->instID, modelgribID = ret->modelgribID;
if (modelptr->used
    && modelptr->instID == instID
    && modelptr->modelgribID == modelgribID)
    {
    ret->resID = resID;
    return CDI_APPLY_STOP;
    }
else
    return CDI_APPLY_GO_ON;
}

static enum cdiApplyRet
findModelByName(int resID, void *res, void *data)
{
model_t *modelptr = (model_t*) res;
struct modelLoc *ret = (struct modelLoc*) data;
int instID = ret->instID, modelgribID = ret->modelgribID;
const char *name = ret->name;
if (modelptr->used
    && (instID == -1 || modelptr->instID == instID)
    && (modelgribID == 0 || modelptr->modelgribID == modelgribID)
    && modelptr->name)
    {
    const char *p = name, *q = modelptr->name;
    while (*p != '\0' && *p == *q)
        ++p, ++q;
    if (*p == '\0' || *q == '\0')
        {
        ret->resID = resID;
        return CDI_APPLY_STOP;
        }
    }
return CDI_APPLY_GO_ON;
}

int modelInq(int instID, int modelgribID, const char *name)
{
modelInit ();

struct modelLoc searchState = { .name = name, .instID = instID,
                                .modelgribID = modelgribID,
                                .resID = CDI_UNDEFID };
if (name && *name)
    cdiResHFilterApply(&modelOps, findModelByName, &searchState);
else
    cdiResHFilterApply(&modelOps, findModelByID, &searchState);
return searchState.resID;
}


int modelDef(int instID, int modelgribID, const char *name)
{
model_t *modelptr;

modelInit ();

modelptr = modelNewEntry(CDI_UNDEFID, instID, modelgribID, name);

return modelptr->self;
}


int modelInqInstitut(int modelID)
{
model_t *modelptr = NULL;

modelInit ();

if ( modelID != CDI_UNDEFID )
    modelptr = ( model_t * ) reshGetVal ( modelID, &modelOps );

return modelptr ? modelptr->instID : CDI_UNDEFID;
}


int modelInqGribID(int modelID)
{
model_t *modelptr = NULL;

modelInit ();

if ( modelID != CDI_UNDEFID )
    modelptr = ( model_t * ) reshGetVal ( modelID, &modelOps );

return modelptr ? modelptr->modelgribID : CDI_UNDEFID;
}


const char *modelInqNamePtr(int modelID)
{
model_t *modelptr = NULL;

modelInit ();

if ( modelID != CDI_UNDEFID )
    modelptr = ( model_t * ) reshGetVal ( modelID, &modelOps );

return modelptr ? modelptr->name : NULL;
}


static int
modelCompareP(void *modelptr1, void *modelptr2)
{
model_t *model1 = (model_t *)modelptr1, *model2 = (model_t *)modelptr2;
int diff = (namespaceResHDecode(model1->instID).idx
            != namespaceResHDecode(model2->instID).idx)
    | (model1->modelgribID != model2->modelgribID)
    | (strcmp(model1->name, model2->name) != 0);
return diff;
}


void modelDestroyP ( void * modelptr )
{
model_t *mp = (model_t*) modelptr;
if (mp->name)
    Free(mp->name);
Free(mp);
}


void modelPrintP   ( void * modelptr, FILE * fp )
{
model_t *mp = (model_t*) modelptr;

if ( !mp ) return;

fprintf ( fp, "#\n");
fprintf ( fp, "# modelID %d\n", mp->self);
fprintf ( fp, "#\n");
fprintf ( fp, "self          = %d\n", mp->self );
fprintf ( fp, "used          = %d\n", mp->used );
fprintf ( fp, "instID        = %d\n", mp->instID );
fprintf ( fp, "modelgribID   = %d\n", mp->modelgribID );
fprintf ( fp, "name          = %s\n", mp->name ? mp->name : "NN" );
}


static int
modelTxCode ( void )
{
return MODEL;
}

enum {
model_nints = 4,
};


static int modelGetSizeP(void * modelptr, void *context)
{
model_t *p = (model_t*)modelptr;
size_t txsize = (size_t)serializeGetSize(model_nints, CDI_DATATYPE_INT, context)
    + (size_t)serializeGetSize(p->name?(int)strlen(p->name) + 1:0, CDI_DATATYPE_TXT, context);
xassert(txsize <= INT_MAX);
return (int)txsize;
}


static void modelPackP(void * modelptr, void * buf, int size, int *position, void *context)
{
model_t *p = (model_t*) modelptr;
int tempbuf[model_nints];
tempbuf[0] = p->self;
tempbuf[1] = p->instID;
tempbuf[2] = p->modelgribID;
tempbuf[3] = p->name ? (int)strlen(p->name) + 1 : 0;
serializePack(tempbuf, model_nints, CDI_DATATYPE_INT, buf, size, position, context);
if (p->name)
    serializePack(p->name, tempbuf[3], CDI_DATATYPE_TXT, buf, size, position, context);
}

int
modelUnpack(void *buf, int size, int *position, int originNamespace, void *context,
            int force_id)
{
int tempbuf[model_nints];
char *name;
serializeUnpack(buf, size, position, tempbuf, model_nints, CDI_DATATYPE_INT, context);
if (tempbuf[3] != 0)
    {
    name = (char *) Malloc((size_t)tempbuf[3]);
    serializeUnpack(buf, size, position,
                    name, tempbuf[3], CDI_DATATYPE_TXT, context);
    }
else
    {
    name = (char*)"";
    }
int targetID = namespaceAdaptKey(tempbuf[0], originNamespace);
model_t *mp = modelNewEntry(force_id?targetID:CDI_UNDEFID,
                            namespaceAdaptKey(tempbuf[1], originNamespace),
                            tempbuf[2], name);
if (tempbuf[3] != 0)
    Free(name);
xassert(!force_id
        || (mp->self == namespaceAdaptKey(tempbuf[0], originNamespace)));
reshSetStatus(mp->self, &modelOps,
                reshGetStatus(mp->self, &modelOps) & ~RESH_SYNC_BIT);
return mp->self;
}


#ifdef HAVE_CONFIG_H
#endif

#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif

#include <limits.h>
#include <stdio.h>


static unsigned nNamespaces = 1;
static int activeNamespace = 0;

#ifdef HAVE_LIBNETCDF
#define CDI_NETCDF_SWITCHES                     \
{ .func = (void (*)()) nc__create },          \
{ .func = (void (*)()) cdf_def_var_serial },  \
{ .func = (void (*)()) cdfDefTimestep },      \
{ .func = (void (*)()) cdfDefVars }

#else
#define CDI_NETCDF_SWITCHES
#endif

#define defaultSwitches {                                   \
    { .func = (void (*)()) cdiAbortC_serial },              \
    { .func = (void (*)()) cdiWarning },                    \
    { .func = (void (*)()) serializeGetSizeInCore },        \
    { .func = (void (*)()) serializePackInCore },           \
    { .func = (void (*)()) serializeUnpackInCore },         \
    { .func = (void (*)()) fileOpen_serial },               \
    { .func = (void (*)()) fileWrite },                     \
    { .func = (void (*)()) fileClose_serial },              \
    { .func = (void (*)()) cdiStreamOpenDefaultDelegate },  \
    { .func = (void (*)()) cdiStreamDefVlist_ },            \
    { .func = (void (*)()) cdiStreamSetupVlist_ },          \
    { .func = (void (*)()) cdiStreamWriteVar_ },            \
    { .func = (void (*)()) cdiStreamWriteVarChunk_ },       \
    { .func = (void (*)()) 0 },                             \
    { .func = (void (*)()) 0 },                             \
    { .func = (void (*)()) cdiStreamCloseDefaultDelegate }, \
    { .func = (void (*)()) cdiStreamDefTimestep_ }, \
    { .func = (void (*)()) cdiStreamSync_ },                \
    CDI_NETCDF_SWITCHES                        \
    }

#if defined (SX) || defined (__cplusplus)
static const union namespaceSwitchValue
defaultSwitches_[NUM_NAMESPACE_SWITCH] = defaultSwitches;
#endif

enum namespaceStatus {
NAMESPACE_STATUS_INUSE,
NAMESPACE_STATUS_UNUSED,
};

static struct Namespace
{
enum namespaceStatus resStage;
union namespaceSwitchValue switches[NUM_NAMESPACE_SWITCH];
} initialNamespace = {
.resStage = NAMESPACE_STATUS_INUSE,
.switches = defaultSwitches
};

static struct Namespace *namespaces = &initialNamespace;

static unsigned namespacesSize = 1;

#if  defined  (HAVE_LIBPTHREAD)
#  include <pthread.h>

static pthread_once_t  namespaceOnce = PTHREAD_ONCE_INIT;
static pthread_mutex_t namespaceMutex;

static void
namespaceInitialize(void)
{
pthread_mutexattr_t ma;
pthread_mutexattr_init(&ma);
pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&namespaceMutex, &ma);
pthread_mutexattr_destroy(&ma);
}

#  define NAMESPACE_LOCK()         pthread_mutex_lock(&namespaceMutex)
#  define NAMESPACE_UNLOCK()       pthread_mutex_unlock(&namespaceMutex)
#  define NAMESPACE_INIT()         pthread_once(&namespaceOnce, \
                                                namespaceInitialize)


#else

#  define NAMESPACE_INIT() do { } while (0)
#  define NAMESPACE_LOCK()
#  define NAMESPACE_UNLOCK()

#endif


enum {
intbits = sizeof(int) * CHAR_BIT,
nspbits = 4,
idxbits = intbits - nspbits,
nspmask = (int)((( (unsigned)1 << nspbits ) - 1) << idxbits),
idxmask = ( 1 << idxbits ) - 1,
};

enum {
NUM_NAMESPACES = 1 << nspbits,
NUM_IDX = 1 << idxbits,
};


int namespaceIdxEncode ( namespaceTuple_t tin )
{
xassert ( tin.nsp < NUM_NAMESPACES && tin.idx < NUM_IDX);
return ( tin.nsp << idxbits ) + tin.idx;
}

int namespaceIdxEncode2 ( int nsp, int idx )
{
xassert(nsp < NUM_NAMESPACES && idx < NUM_IDX);
return ( nsp << idxbits ) + idx;
}


namespaceTuple_t namespaceResHDecode ( int resH )
{
namespaceTuple_t tin;

tin.idx = resH & idxmask;
tin.nsp = (int)(((unsigned)( resH & nspmask )) >> idxbits);

return tin;
}

int
namespaceNew()
{
int newNamespaceID = -1;
NAMESPACE_INIT();
NAMESPACE_LOCK();
if (namespacesSize > nNamespaces)
    {

    for (unsigned i = 0; i < namespacesSize; ++i)
        if (namespaces[i].resStage == NAMESPACE_STATUS_UNUSED)
        {
            newNamespaceID = (int)i;
            break;
        }
    }
else if (namespacesSize == 1)
    {

    struct Namespace *newNameSpaces
        = (struct Namespace *) Malloc(((size_t)namespacesSize + 1) * sizeof (namespaces[0]));
    memcpy(newNameSpaces, namespaces, sizeof (namespaces[0]));
    namespaces = newNameSpaces;
    ++namespacesSize;
    newNamespaceID = 1;
    }
else if (namespacesSize < NUM_NAMESPACES)
    {

    newNamespaceID = (int)namespacesSize;
    namespaces
        = (struct Namespace *) Realloc(namespaces, ((size_t)namespacesSize + 1) * sizeof (namespaces[0]));
    ++namespacesSize;
    }
else
    {
    NAMESPACE_UNLOCK();
    return -1;
    }
xassert(newNamespaceID >= 0 && newNamespaceID < NUM_NAMESPACES);
++nNamespaces;
namespaces[newNamespaceID].resStage = NAMESPACE_STATUS_INUSE;
#if defined (SX) || defined (__cplusplus)
memcpy(namespaces[newNamespaceID].switches,
        defaultSwitches_,
        sizeof (namespaces[newNamespaceID].switches));
#else
    memcpy(namespaces[newNamespaceID].switches,
        (union namespaceSwitchValue[NUM_NAMESPACE_SWITCH])defaultSwitches,
        sizeof (namespaces[newNamespaceID].switches));
#endif
reshListCreate(newNamespaceID);
NAMESPACE_UNLOCK();
return newNamespaceID;
}

void
namespaceDelete(int namespaceID)
{
NAMESPACE_INIT();
NAMESPACE_LOCK();
xassert(namespaceID >= 0 && (unsigned)namespaceID < namespacesSize
        && nNamespaces);
reshListDestruct(namespaceID);
namespaces[namespaceID].resStage = NAMESPACE_STATUS_UNUSED;
--nNamespaces;
NAMESPACE_UNLOCK();
}

int namespaceGetNumber ()
{
return (int)nNamespaces;
}


void namespaceSetActive ( int nId )
{
xassert((unsigned)nId < namespacesSize
        && namespaces[nId].resStage != NAMESPACE_STATUS_UNUSED);
activeNamespace = nId;
}


int namespaceGetActive ()
{
return activeNamespace;
}

int namespaceAdaptKey ( int originResH, int originNamespace )
{
namespaceTuple_t tin;
int nsp;

if ( originResH == CDI_UNDEFID ) return CDI_UNDEFID;

tin.idx = originResH & idxmask;
tin.nsp = (int)(((unsigned)( originResH & nspmask )) >> idxbits);

xassert ( tin.nsp == originNamespace );

nsp = namespaceGetActive ();

return namespaceIdxEncode2 ( nsp, tin.idx );
}


int namespaceAdaptKey2 ( int originResH )
{
namespaceTuple_t tin;
int nsp;

if ( originResH == CDI_UNDEFID ) return CDI_UNDEFID;

tin.idx = originResH & idxmask;
tin.nsp = (int)(((unsigned)( originResH & nspmask )) >> idxbits);

nsp = namespaceGetActive ();

return namespaceIdxEncode2 ( nsp, tin.idx );
}

void namespaceSwitchSet(enum namespaceSwitch sw, union namespaceSwitchValue value)
{
xassert(sw > NSSWITCH_NO_SUCH_SWITCH && sw < NUM_NAMESPACE_SWITCH);
int nsp = namespaceGetActive();
namespaces[nsp].switches[sw] = value;
}

union namespaceSwitchValue namespaceSwitchGet(enum namespaceSwitch sw)
{
xassert(sw > NSSWITCH_NO_SUCH_SWITCH && sw < NUM_NAMESPACE_SWITCH);
int nsp = namespaceGetActive();
return namespaces[nsp].switches[sw];
}

void cdiReset(void)
{
NAMESPACE_INIT();
NAMESPACE_LOCK();
for (unsigned namespaceID = 0; namespaceID < namespacesSize; ++namespaceID)
    if (namespaces[namespaceID].resStage != NAMESPACE_STATUS_UNUSED)
    namespaceDelete((int)namespaceID);
if (namespaces != &initialNamespace)
    {
    Free(namespaces);
    namespaces = &initialNamespace;
    namespaces[0].resStage = NAMESPACE_STATUS_UNUSED;
    }
namespacesSize = 1;
nNamespaces = 0;
NAMESPACE_UNLOCK();
}


#ifndef INCLUDE_GUARD_CDI_REFERENCE_COUNTING
#define INCLUDE_GUARD_CDI_REFERENCE_COUNTING


#include <sys/types.h>
#include <stdlib.h>


typedef struct CdiReferencedObject CdiReferencedObject;
struct CdiReferencedObject {

    void (*destructor)(CdiReferencedObject* me);


    size_t refCount;
};

void cdiRefObject_construct(CdiReferencedObject* me);
void cdiRefObject_retain(CdiReferencedObject* me);
void cdiRefObject_release(CdiReferencedObject* me);
void cdiRefObject_destruct(CdiReferencedObject* me);

#endif




void cdiRefObject_construct(CdiReferencedObject* me)
{
me->destructor = cdiRefObject_destruct;
me->refCount = 1;
}

void cdiRefObject_retain(CdiReferencedObject* me)
{
size_t oldCount = me->refCount++;
xassert(oldCount && "A reference counted object was used after it was destructed.");
}

void cdiRefObject_release(CdiReferencedObject* me)
{
size_t oldCount = me->refCount--;
xassert(oldCount && "A reference counted object was released too often.");
if(oldCount == 1)
    {
    me->destructor(me);
    Free(me);
    }
}

void cdiRefObject_destruct(CdiReferencedObject* me)
{
(void)me;

}


#ifdef HAVE_CONFIG_H
#endif

#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#if defined (HAVE_EXECINFO_H)
#include <execinfo.h>
#endif

static
void show_stackframe()
{
#if defined HAVE_EXECINFO_H && defined backtrace_size_t && defined HAVE_BACKTRACE
void *trace[16];
backtrace_size_t trace_size = backtrace(trace, 16);
char **messages = backtrace_symbols(trace, trace_size);

fprintf(stderr, "[bt] Execution path:\n");
if ( messages ) {
    for ( backtrace_size_t i = 0; i < trace_size; ++i )
    fprintf(stderr, "[bt] %s\n", messages[i]);
    free(messages);
}
#endif
}


enum { MIN_LIST_SIZE = 128 };

static void listInitialize(void);

typedef struct listElem {
union
{

    struct
    {
    int next, prev;
    } free;

    struct
    {
    const resOps *ops;
    void         *val;
    } v;
} res;
int           status;
} listElem_t;

struct resHList_t
{
int size, freeHead, hasDefaultRes;
listElem_t *resources;
};

static struct resHList_t *resHList;

static int resHListSize = 0;

#if  defined  (HAVE_LIBPTHREAD)
#  include <pthread.h>

static pthread_once_t  listInitThread = PTHREAD_ONCE_INIT;
static pthread_mutex_t listMutex;

#  define LIST_LOCK()         pthread_mutex_lock(&listMutex)
#  define LIST_UNLOCK()       pthread_mutex_unlock(&listMutex)
#  define LIST_INIT(init0)         do {                         \
    pthread_once(&listInitThread, listInitialize);              \
    pthread_mutex_lock(&listMutex);                             \
    if ((init0) && (!resHList || !resHList[0].resources))       \
    reshListCreate(0);                                        \
    pthread_mutex_unlock(&listMutex);                           \
} while (0)



#else

static int listInit = 0;

#  define LIST_LOCK()
#  define LIST_UNLOCK()
#  define LIST_INIT(init0)        do {                          \
if ( !listInit )                                              \
    {                                                           \
    listInitialize();                                         \
    if ((init0) && (!resHList || !resHList[0].resources))     \
        reshListCreate(0);                                      \
    listInit = 1;                                             \
    }                                                           \
} while(0)

#endif



static void
listInitResources(int nsp)
{
xassert(nsp < resHListSize && nsp >= 0);
int size = resHList[nsp].size = MIN_LIST_SIZE;
xassert(resHList[nsp].resources == NULL);
resHList[nsp].resources = (listElem_t*) Calloc(MIN_LIST_SIZE, sizeof(listElem_t));
listElem_t *p = resHList[nsp].resources;

for (int i = 0; i < size; i++ )
    {
    p[i].res.free.next = i + 1;
    p[i].res.free.prev = i - 1;
    p[i].status = RESH_UNUSED;
    }

p[size-1].res.free.next = -1;
resHList[nsp].freeHead = 0;
int oldNsp = namespaceGetActive();
namespaceSetActive(nsp);
instituteDefaultEntries();
modelDefaultEntries();
namespaceSetActive(oldNsp);
}

static inline void
reshListClearEntry(int i)
{
resHList[i].size = 0;
resHList[i].resources = NULL;
resHList[i].freeHead = -1;
}

void
reshListCreate(int namespaceID)
{
LIST_INIT(namespaceID != 0);
LIST_LOCK();
if (resHListSize <= namespaceID)
    {
    resHList = (struct resHList_t *) Realloc(resHList, (size_t)(namespaceID + 1) * sizeof (resHList[0]));
    for (int i = resHListSize; i <= namespaceID; ++i)
        reshListClearEntry(i);
    resHListSize = namespaceID + 1;
    }
listInitResources(namespaceID);
LIST_UNLOCK();
}




void
reshListDestruct(int namespaceID)
{
LIST_LOCK();
xassert(resHList && namespaceID >= 0 && namespaceID < resHListSize);
int callerNamespaceID = namespaceGetActive();
namespaceSetActive(namespaceID);
if (resHList[namespaceID].resources)
    {
    for ( int j = 0; j < resHList[namespaceID].size; j++ )
        {
        listElem_t *listElem = resHList[namespaceID].resources + j;
        if (listElem->status & RESH_IN_USE_BIT)
            listElem->res.v.ops->valDestroy(listElem->res.v.val);
        }
    Free(resHList[namespaceID].resources);
    resHList[namespaceID].resources = NULL;
    reshListClearEntry(namespaceID);
    }
if (resHList[callerNamespaceID].resources)
    namespaceSetActive(callerNamespaceID);
LIST_UNLOCK();
}


static void listDestroy ( void )
{
LIST_LOCK();
for (int i = resHListSize; i > 0; --i)
    if (resHList[i-1].resources)
    namespaceDelete(i-1);
resHListSize = 0;
Free(resHList);
resHList = NULL;
cdiReset();
LIST_UNLOCK();
}



static
void listInitialize ( void )
{
#if  defined  (HAVE_LIBPTHREAD)
pthread_mutexattr_t ma;
pthread_mutexattr_init(&ma);
pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE);

pthread_mutex_init ( &listMutex, &ma);
pthread_mutexattr_destroy(&ma);
#endif

{
    int null_id;
    null_id = fileOpen_serial("/dev/null", "r");
    if (null_id != -1)
    fileClose_serial(null_id);
}
atexit ( listDestroy );
}



static
void listSizeExtend()
{
int nsp = namespaceGetActive ();
int oldSize = resHList[nsp].size;
size_t newListSize = (size_t)oldSize + MIN_LIST_SIZE;

resHList[nsp].resources = (listElem_t*) Realloc(resHList[nsp].resources,
                                                newListSize * sizeof(listElem_t));

listElem_t *r = resHList[nsp].resources;
for (size_t i = (size_t)oldSize; i < newListSize; ++i)
    {
    r[i].res.free.next = (int)i + 1;
    r[i].res.free.prev = (int)i - 1;
    r[i].status = RESH_UNUSED;
    }

if (resHList[nsp].freeHead != -1)
    r[resHList[nsp].freeHead].res.free.prev = (int)newListSize - 1;
r[newListSize-1].res.free.next = resHList[nsp].freeHead;
r[oldSize].res.free.prev = -1;
resHList[nsp].freeHead = oldSize;
resHList[nsp].size = (int)newListSize;
}



static void
reshPut_(int nsp, int entry, void *p, const resOps *ops)
{
listElem_t *newListElem = resHList[nsp].resources + entry;
int next = newListElem->res.free.next,
    prev = newListElem->res.free.prev;
if (next != -1)
    resHList[nsp].resources[next].res.free.prev = prev;
if (prev != -1)
    resHList[nsp].resources[prev].res.free.next = next;
else
    resHList[nsp].freeHead = next;
newListElem->res.v.val = p;
newListElem->res.v.ops = ops;
newListElem->status = RESH_DESYNC_IN_USE;
}

int reshPut ( void *p, const resOps *ops )
{
xassert ( p && ops );

LIST_INIT(1);

LIST_LOCK();

int nsp = namespaceGetActive ();

if ( resHList[nsp].freeHead == -1) listSizeExtend();
int entry = resHList[nsp].freeHead;
cdiResH resH = namespaceIdxEncode2(nsp, entry);
reshPut_(nsp, entry, p, ops);

LIST_UNLOCK();

return resH;
}



static void
reshRemove_(int nsp, int idx)
{
int curFree = resHList[nsp].freeHead;
listElem_t *r = resHList[nsp].resources;
r[idx].res.free.next = curFree;
r[idx].res.free.prev = -1;
if (curFree != -1)
    r[curFree].res.free.prev = idx;
r[idx].status = RESH_DESYNC_DELETED;
resHList[nsp].freeHead = idx;
}

void reshDestroy(cdiResH resH)
{
int nsp;
namespaceTuple_t nspT;

LIST_LOCK();

nsp = namespaceGetActive ();

nspT = namespaceResHDecode ( resH );

xassert ( nspT.nsp == nsp
            && nspT.idx >= 0
            && nspT.idx < resHList[nsp].size
            && resHList[nsp].resources[nspT.idx].res.v.ops);

if (resHList[nsp].resources[nspT.idx].status & RESH_IN_USE_BIT)
    reshRemove_(nsp, nspT.idx);

LIST_UNLOCK();
}

void reshRemove ( cdiResH resH, const resOps * ops )
{
int nsp;
namespaceTuple_t nspT;

LIST_LOCK();

nsp = namespaceGetActive ();

nspT = namespaceResHDecode ( resH );

xassert ( nspT.nsp == nsp
            && nspT.idx >= 0
            && nspT.idx < resHList[nsp].size
            && (resHList[nsp].resources[nspT.idx].status & RESH_IN_USE_BIT)
            && resHList[nsp].resources[nspT.idx].res.v.ops
            && resHList[nsp].resources[nspT.idx].res.v.ops == ops );

reshRemove_(nsp, nspT.idx);

LIST_UNLOCK();
}



void reshReplace(cdiResH resH, void *p, const resOps *ops)
{
xassert(p && ops);
LIST_INIT(1);
LIST_LOCK();
int nsp = namespaceGetActive();
namespaceTuple_t nspT = namespaceResHDecode(resH);
while (resHList[nsp].size <= nspT.idx)
    listSizeExtend();
listElem_t *q = resHList[nsp].resources + nspT.idx;
if (q->status & RESH_IN_USE_BIT)
    {
    q->res.v.ops->valDestroy(q->res.v.val);
    reshRemove_(nsp, nspT.idx);
    }
reshPut_(nsp, nspT.idx, p, ops);
LIST_UNLOCK();
}


static listElem_t *
reshGetElem(const char *caller, const char* expressionString, cdiResH resH, const resOps *ops)
{
listElem_t *listElem;
xassert ( ops );

LIST_INIT(1);

LIST_LOCK();

int nsp = namespaceGetActive ();

namespaceTuple_t nspT = namespaceResHDecode ( resH );
assert(nspT.idx >= 0);

if (nspT.nsp == nsp &&
    nspT.idx < resHList[nsp].size)
    {
    listElem = resHList[nsp].resources + nspT.idx;
    LIST_UNLOCK();
    }
else
    {
    LIST_UNLOCK();
    show_stackframe();

    if ( resH == CDI_UNDEFID )
        {
        xabortC(caller, "Error while trying to resolve the ID \"%s\" in `%s()`: the value is CDI_UNDEFID (= %d).\n\tThis is most likely the result of a failed earlier call. Please check the IDs returned by CDI.", expressionString, caller, resH);
        }
    else
        {
        xabortC(caller, "Error while trying to resolve the ID \"%s\" in `%s()`: the value is garbage (= %d, which resolves to namespace = %d, index = %d).\n\tThis is either the result of using an uninitialized variable,\n\tof using a value as an ID that is not an ID,\n\tor of using an ID after it has been invalidated.", expressionString, caller, resH, nspT.nsp, nspT.idx);
        }
    }

if ( !(listElem && listElem->res.v.ops == ops) )
    {
    show_stackframe();

    xabortC(caller, "Error while trying to resolve the ID \"%s\" in `%s()`: list element not found. The failed ID is %d", expressionString, caller, (int)resH);
    }

return listElem;
}

void *reshGetValue(const char * caller, const char* expressionString, cdiResH resH, const resOps * ops)
{
return reshGetElem(caller, expressionString, resH, ops)->res.v.val;
}



void reshGetResHListOfType(unsigned numIDs, int resHs[], const resOps *ops)
{
xassert ( resHs && ops );

LIST_INIT(1);

LIST_LOCK();

int nsp = namespaceGetActive();
unsigned j = 0;
for (int i = 0; i < resHList[nsp].size && j < numIDs; i++ )
    if ((resHList[nsp].resources[i].status & RESH_IN_USE_BIT)
        && resHList[nsp].resources[i].res.v.ops == ops)
    resHs[j++] = namespaceIdxEncode2(nsp, i);

LIST_UNLOCK();
}

enum cdiApplyRet
cdiResHApply(enum cdiApplyRet (*func)(int id, void *res, const resOps *p,
                                    void *data), void *data)
{
xassert(func);

LIST_INIT(1);

LIST_LOCK();

int nsp = namespaceGetActive ();
enum cdiApplyRet ret = CDI_APPLY_GO_ON;
for (int i = 0; i < resHList[nsp].size && ret > 0; ++i)
    if (resHList[nsp].resources[i].status & RESH_IN_USE_BIT)
    ret = func(namespaceIdxEncode2(nsp, i),
                resHList[nsp].resources[i].res.v.val,
                resHList[nsp].resources[i].res.v.ops, data);
LIST_UNLOCK();
return ret;
}


enum cdiApplyRet
cdiResHFilterApply(const resOps *p,
                enum cdiApplyRet (*func)(int id, void *res, void *data),
                void *data)
{
xassert(p && func);

LIST_INIT(1);

LIST_LOCK();

int nsp = namespaceGetActive ();
enum cdiApplyRet ret = CDI_APPLY_GO_ON;
listElem_t *r = resHList[nsp].resources;
for (int i = 0; i < resHList[nsp].size && ret > 0; ++i)
    if ((r[i].status & RESH_IN_USE_BIT) && r[i].res.v.ops == p)
    ret = func(namespaceIdxEncode2(nsp, i), r[i].res.v.val,
                data);
LIST_UNLOCK();
return ret;
}






unsigned reshCountType(const resOps *ops)
{
unsigned countType = 0;

xassert(ops);

LIST_INIT(1);

LIST_LOCK();

int nsp = namespaceGetActive ();

listElem_t *r = resHList[nsp].resources;
size_t len = (size_t)resHList[nsp].size;
for (size_t i = 0; i < len; i++ )
    countType += ((r[i].status & RESH_IN_USE_BIT) && r[i].res.v.ops == ops);

LIST_UNLOCK();

return countType;
}



int
reshResourceGetPackSize_intern(int resH, const resOps *ops, void *context, const char* caller, const char* expressionString)
{
listElem_t *curr = reshGetElem(caller, expressionString, resH, ops);
return curr->res.v.ops->valGetPackSize(curr->res.v.val, context);
}

void
reshPackResource_intern(int resH, const resOps *ops, void *buf, int buf_size, int *position, void *context,
                        const char* caller, const char* expressionString)
{
listElem_t *curr = reshGetElem(caller, expressionString, resH, ops);
curr->res.v.ops->valPack(curr->res.v.val, buf, buf_size, position, context);
}

enum {
resHPackHeaderNInt = 2,
resHDeleteNInt = 2,
};

static int getPackBufferSize(void *context)
{
int intpacksize, packBufferSize = 0;

int nsp = namespaceGetActive ();


packBufferSize += resHPackHeaderNInt * (intpacksize = serializeGetSize(1, CDI_DATATYPE_INT, context));


listElem_t *r = resHList[nsp].resources;
for ( int i = 0; i < resHList[nsp].size; i++)
    if (r[i].status & RESH_SYNC_BIT)
    {
        if (r[i].status == RESH_DESYNC_DELETED)
        {
            packBufferSize += resHDeleteNInt * intpacksize;
        }
        else if (r[i].status == RESH_DESYNC_IN_USE)
        {
            xassert ( r[i].res.v.ops );

            packBufferSize +=
            r[i].res.v.ops->valGetPackSize(r[i].res.v.val, context)
            + intpacksize;
        }
    }

packBufferSize += intpacksize;

return packBufferSize;
}



void reshPackBufferDestroy ( char ** buffer )
{
if ( buffer ) free ( *buffer );
}



int reshGetTxCode(cdiResH resH)
{
int type = 0;

LIST_LOCK();

int nsp = namespaceGetActive ();

namespaceTuple_t nspT = namespaceResHDecode ( resH );
assert(nspT.idx >= 0);

if (nspT.nsp == nsp &&
    nspT.idx < resHList[nsp].size)
    {
    listElem_t *listElem = resHList[nsp].resources + nspT.idx;
    xassert ( listElem->res.v.ops );
    type = listElem->res.v.ops->valTxCode();
    }

LIST_UNLOCK();

return type;
}



int reshPackBufferCreate(char **packBuffer, int *packBufferSize, void *context)
{
int packBufferPos = 0;
int end = END;

xassert ( packBuffer );

LIST_LOCK();

int nsp = namespaceGetActive ();

int pBSize = *packBufferSize = getPackBufferSize(context);
char *pB = *packBuffer = (char *) Malloc((size_t)pBSize);

{
    int header[resHPackHeaderNInt] = { START, nsp };
    serializePack(header, resHPackHeaderNInt,  CDI_DATATYPE_INT, pB, pBSize, &packBufferPos, context);
}

listElem_t *r = resHList[nsp].resources;
for ( int i = 0; i < resHList[nsp].size; i++ )
    if (r[i].status & RESH_SYNC_BIT)
    {
        if (r[i].status == RESH_DESYNC_DELETED)
        {
            int temp[resHDeleteNInt]
            = { RESH_DELETE, namespaceIdxEncode2(nsp, i) };
            serializePack(temp, resHDeleteNInt, CDI_DATATYPE_INT,
                        pB, pBSize, &packBufferPos, context);
        }
        else
        {
            listElem_t * curr = r + i;
            xassert ( curr->res.v.ops );
            int type = curr->res.v.ops->valTxCode();
            if ( ! type ) continue;
            serializePack(&type, 1, CDI_DATATYPE_INT, pB,
                        pBSize, &packBufferPos, context);
            curr->res.v.ops->valPack(curr->res.v.val,
                                    pB, pBSize, &packBufferPos, context);
        }
        r[i].status &= ~RESH_SYNC_BIT;
    }

LIST_UNLOCK();

serializePack(&end, 1,  CDI_DATATYPE_INT, pB, pBSize, &packBufferPos, context);

return packBufferPos;
}





void reshSetStatus ( cdiResH resH, const resOps * ops, int status )
{
int nsp;
namespaceTuple_t nspT;
listElem_t * listElem;

xassert((ops != NULL) ^ !(status & RESH_IN_USE_BIT));

LIST_INIT(1);

LIST_LOCK();

nsp = namespaceGetActive ();

nspT = namespaceResHDecode ( resH );

xassert ( nspT.nsp == nsp &&
            nspT.idx >= 0 &&
            nspT.idx < resHList[nsp].size );

xassert ( resHList[nsp].resources );
listElem = resHList[nsp].resources + nspT.idx;

xassert((!ops || (listElem->res.v.ops == ops))
        && (listElem->status & RESH_IN_USE_BIT) == (status & RESH_IN_USE_BIT));

listElem->status = status;

LIST_UNLOCK();
}



int reshGetStatus ( cdiResH resH, const resOps * ops )
{
LIST_INIT(1);

LIST_LOCK();

int nsp = namespaceGetActive ();

namespaceTuple_t nspT = namespaceResHDecode ( resH );

xassert ( nspT.nsp == nsp &&
            nspT.idx >= 0 );

int status = RESH_UNUSED;
if (nspT.idx < resHList[nsp].size)
    {
    listElem_t *listElem = resHList[nsp].resources + nspT.idx;
    const resOps *elemOps = listElem->res.v.ops;
    xassert(listElem && (!(listElem->status & RESH_IN_USE_BIT) || elemOps == ops));
    status = listElem->status;
    }

LIST_UNLOCK();

return status;
}



void reshLock ()
{
LIST_LOCK();
}



void reshUnlock ()
{
LIST_UNLOCK();
}



int reshListCompare ( int nsp0, int nsp1 )
{
LIST_INIT(1);
LIST_LOCK();

xassert(resHListSize > nsp0 && resHListSize > nsp1 &&
        nsp0 >= 0 && nsp1 >= 0);

int valCompare = 0;
int i, listSizeMin = (resHList[nsp0].size <= resHList[nsp1].size)
    ? resHList[nsp0].size : resHList[nsp1].size;
listElem_t *resources0 = resHList[nsp0].resources,
    *resources1 = resHList[nsp1].resources;
for (i = 0; i < listSizeMin; i++)
    {
    int occupied0 = (resources0[i].status & RESH_IN_USE_BIT) != 0,
        occupied1 = (resources1[i].status & RESH_IN_USE_BIT) != 0;

    int diff = occupied0 ^ occupied1;
    valCompare |= (diff << cdiResHListOccupationMismatch);
    if (!diff && occupied0)
        {

        diff = (resources0[i].res.v.ops != resources1[i].res.v.ops
                || resources0[i].res.v.ops == NULL);
        valCompare |= (diff << cdiResHListResourceTypeMismatch);
        if (!diff)
            {

            diff
                = resources0[i].res.v.ops->valCompare(resources0[i].res.v.val,
                                                    resources1[i].res.v.val);
            valCompare |= (diff << cdiResHListResourceContentMismatch);
            }
        }
    }

for (int j = listSizeMin; j < resHList[nsp0].size; ++j)
    valCompare |= (((resources0[j].status & RESH_IN_USE_BIT) != 0)
                << cdiResHListOccupationMismatch);

for (; i < resHList[nsp1].size; ++i)
    valCompare |= (((resources1[i].status & RESH_IN_USE_BIT) != 0)
                << cdiResHListOccupationMismatch);

LIST_UNLOCK();

return valCompare;
}



void reshListPrint(FILE *fp)
{
int i, j, temp;
listElem_t * curr;

LIST_INIT(1);


temp = namespaceGetActive ();

fprintf ( fp, "\n\n##########################################\n#\n#  print " \
            "global resource list \n#\n" );

for ( i = 0; i < namespaceGetNumber (); i++ )
    {
    namespaceSetActive ( i );

    fprintf ( fp, "\n" );
    fprintf ( fp, "##################################\n" );
    fprintf ( fp, "#\n" );
    fprintf ( fp, "# namespace=%d\n", i );
    fprintf ( fp, "#\n" );
    fprintf ( fp, "##################################\n\n" );

    fprintf ( fp, "resHList[%d].size=%d\n", i, resHList[i].size );

    for ( j = 0; j < resHList[i].size; j++ )
        {
        curr = resHList[i].resources + j;
        if (!(curr->status & RESH_IN_USE_BIT))
            {
            curr->res.v.ops->valPrint(curr->res.v.val, fp);
            fprintf ( fp, "\n" );
            }
        }
    }
fprintf ( fp, "#\n#  end global resource list" \
            "\n#\n##########################################\n\n" );

namespaceSetActive ( temp );
}



#include <inttypes.h>
#include <limits.h>
#include <string.h>


int
serializeGetSize(int count, int datatype, void *context)
{
int (*serialize_get_size_p)(int count, int datatype, void *context)
    = (int (*)(int, int, void *))
    namespaceSwitchGet(NSSWITCH_SERIALIZE_GET_SIZE).func;
return serialize_get_size_p(count, datatype, context);
}

void serializePack(const void *data, int count, int datatype,
                void *buf, int buf_size, int *position, void *context)
{
void (*serialize_pack_p)(const void *data, int count, int datatype,
                        void *buf, int buf_size, int *position, void *context)
    = (void (*)(const void *, int, int, void *, int, int *, void *))
    namespaceSwitchGet(NSSWITCH_SERIALIZE_PACK).func;
serialize_pack_p(data, count, datatype, buf, buf_size, position, context);
}

void serializeUnpack(const void *buf, int buf_size, int *position,
                    void *data, int count, int datatype, void *context)
{
void (*serialize_unpack_p)(const void *buf, int buf_size, int *position,
                            void *data, int count, int datatype, void *context)
    = (void (*)(const void *, int, int *, void *, int, int, void *))
    namespaceSwitchGet(NSSWITCH_SERIALIZE_UNPACK).func;
serialize_unpack_p(buf, buf_size, position, data, count, datatype, context);
}



int
serializeGetSizeInCore(int count, int datatype, void *context)
{
int elemSize;
(void)context;
switch (datatype)
{
case CDI_DATATYPE_INT8:
    elemSize = sizeof (int8_t);
    break;
case CDI_DATATYPE_INT16:
    elemSize = sizeof (int16_t);
    break;
case CDI_DATATYPE_UINT32:
    elemSize = sizeof (uint32_t);
    break;
case CDI_DATATYPE_INT:
    elemSize = sizeof (int);
    break;
case CDI_DATATYPE_UINT:
    elemSize = sizeof (unsigned);
    break;
case CDI_DATATYPE_FLT:
case CDI_DATATYPE_FLT64:
    elemSize = sizeof (double);
    break;
case CDI_DATATYPE_TXT:
case CDI_DATATYPE_UCHAR:
    elemSize = 1;
    break;
case CDI_DATATYPE_LONG:
    elemSize = sizeof (long);
    break;
default:
    xabort("Unexpected datatype");
}
return count * elemSize;
}

void serializePackInCore(const void *data, int count, int datatype,
                        void *buf, int buf_size, int *position, void *context)
{
int size = serializeGetSize(count, datatype, context);
int pos = *position;
xassert(INT_MAX - pos >= size && buf_size - pos >= size);
memcpy((unsigned char *)buf + pos, data, (size_t)size);
pos += size;
*position = pos;
}

void serializeUnpackInCore(const void *buf, int buf_size, int *position,
                        void *data, int count, int datatype, void *context)
{
int size = serializeGetSize(count, datatype, context);
int pos = *position;
xassert(INT_MAX - pos >= size && buf_size - pos >= size);
memcpy(data, (unsigned char *)buf + pos, (size_t)size);
pos += size;
*position = pos;
}


#ifndef _STREAM_GRB_H
#define _STREAM_GRB_H

static inline bool gribbyte_get_bit(int number, int bit) { return (bool)((number >> (8-bit)) & 1); }
static inline void gribbyte_set_bit(int *number, int bit) { *number |= 1 << (8-bit); }
static inline void gribbyte_clear_bit(int *number, int bit) { *number &= ~(1 << (8-bit)); }

int   grbBitsPerValue(int datatype);

int   grbInqContents(stream_t *streamptr);
int   grbInqTimestep(stream_t *streamptr, int tsID);

int   grbInqRecord(stream_t *streamptr, int *varID, int *levelID);
void  grbDefRecord(stream_t *streamptr);
void  grb_read_record(stream_t *streamptr, int memtype, void *data, size_t *nmiss);
void  grb_write_record(stream_t *streamptr, int memtype, const void *data, size_t nmiss);
void  grbCopyRecord(stream_t *streamptr2, stream_t *streamptr1);

void  grb_read_var(stream_t *streamptr, int varID, int memtype, void *data, size_t *nmiss);
void  grb_write_var(stream_t *streamptr, int varID, int memtype, const void *data, size_t nmiss);

void  grb_read_var_slice(stream_t *streamptr, int varID, int levelID, int memtype, void *data, size_t *nmiss);
void  grb_write_var_slice(stream_t *streamptr, int varID, int levelID, int memtype, const void *data, size_t nmiss);

int   grib1ltypeToZaxisType(int grib_ltype);
int   grib2ltypeToZaxisType(int grib_ltype);

int   zaxisTypeToGrib1ltype(int zaxistype);
int   zaxisTypeToGrib2ltype(int zaxistype);

int grbGetGridtype(int gridID, size_t gridsize, bool *gridIsRotated, bool *gridIsCurvilinear);

struct cdiGribParamChange
{
int code, ltype, lev;
bool active;
};

struct cdiGribModeChange
{
bool mode;
bool active;
};

struct cdiGribScanModeChange
{
int value;
bool active;
};

extern struct cdiGribParamChange cdiGribChangeParameterID;
extern struct cdiGribModeChange cdiGribChangeModeUvRelativeToGrid;
extern struct cdiGribScanModeChange cdiGribDataScanningMode;


void streamGrbChangeParameterIdentification(int code, int ltype, int lev);
void streamGrbChangeModeUvRelativeToGrid(int mode);
void streamGrbDefDataScanningMode(int scanmode);
int  streamGrbInqDataScanningMode(void);

#endif

#ifdef  HAVE_CONFIG_H
#endif

#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif

#include <sys/stat.h>
#include <ctype.h>



static stream_t *stream_new_entry(int resH);
static void stream_delete_entry(stream_t *streamptr);
static int streamCompareP(void * streamptr1, void * streamptr2);
static void streamDestroyP(void * streamptr);
static void streamPrintP(void * streamptr, FILE * fp);
static int streamGetPackSize(void * streamptr, void *context);
static void streamPack(void * streamptr, void * buff, int size, int * position, void *context);
static int streamTxCode(void);

const resOps streamOps = {
streamCompareP,
streamDestroyP,
streamPrintP,
streamGetPackSize,
streamPack,
streamTxCode
};



static
int getByteorder(int byteswap)
{
int byteorder = -1;

switch (HOST_ENDIANNESS)
    {
    case CDI_BIGENDIAN:
    byteorder = byteswap ? CDI_LITTLEENDIAN : CDI_BIGENDIAN;
    break;
    case CDI_LITTLEENDIAN:
    byteorder = byteswap ? CDI_BIGENDIAN : CDI_LITTLEENDIAN;
    break;

    case CDI_PDPENDIAN:
    default:
    Error("unhandled endianness");
    }
return byteorder;
}


int cdiGetFiletype(const char *filename, int *byteorder)
{
int filetype = CDI_EUFTYPE;
int swap = 0;
int version;
long recpos;

int fileID = fileOpen(filename, "r");

if ( fileID == CDI_UNDEFID )
    {
    if ( strncmp(filename, "http:", 5) == 0 || strncmp(filename, "https:", 6) == 0 )
        return CDI_FILETYPE_NC;
    else
        return CDI_ESYSTEM;
    }
else if ( fileID == -2 ) return CDI_ETMOF;

char buffer[8];
if ( fileRead(fileID, buffer, 8) != 8 )
    {
    struct stat buf;
    if ( stat(filename, &buf) == 0 )
        {
        if ( buf.st_size == 0 ) return CDI_EISEMPTY;
        if ( buf.st_mode&S_IFDIR ) return CDI_EISDIR;
        }

    return CDI_EUFTYPE;
    }

fileRewind(fileID);

if ( memcmp(buffer, "GRIB", 4) == 0 )
    {
    version = buffer[7];
    if ( version <= 1 )
        {
        filetype = CDI_FILETYPE_GRB;
        if ( CDI_Debug ) Message("found GRIB file = %s, version %d", filename, version);
        }
    else if ( version == 2 )
        {
        filetype = CDI_FILETYPE_GRB2;
        if ( CDI_Debug ) Message("found GRIB2 file = %s", filename);
        }
    }
else if ( memcmp(buffer, "CDF\001", 4) == 0 )
    {
    filetype = CDI_FILETYPE_NC;
    if ( CDI_Debug ) Message("found CDF1 file = %s", filename);
    }
else if ( memcmp(buffer, "CDF\002", 4) == 0 )
    {
    filetype = CDI_FILETYPE_NC2;
    if ( CDI_Debug ) Message("found CDF2 file = %s", filename);
    }
else if ( memcmp(buffer, "CDF\005", 4) == 0 )
    {
    filetype = CDI_FILETYPE_NC5;
    if ( CDI_Debug ) Message("found CDF5 file = %s", filename);
    }
else if ( memcmp(buffer+1, "HDF", 3) == 0 )
    {
    filetype = CDI_FILETYPE_NC4;
    if ( CDI_Debug ) Message("found HDF file = %s", filename);
    }
#ifdef  HAVE_LIBSERVICE
else if ( srvCheckFiletype(fileID, &swap) )
    {
    filetype = CDI_FILETYPE_SRV;
    if ( CDI_Debug ) Message("found SRV file = %s", filename);
    }
#endif
#ifdef  HAVE_LIBEXTRA
else if ( extCheckFiletype(fileID, &swap) )
    {
    filetype = CDI_FILETYPE_EXT;
    if ( CDI_Debug ) Message("found EXT file = %s", filename);
    }
#endif
#ifdef  HAVE_LIBIEG
else if ( iegCheckFiletype(fileID, &swap) )
    {
    filetype = CDI_FILETYPE_IEG;
    if ( CDI_Debug ) Message("found IEG file = %s", filename);
    }
#endif
#ifdef HAVE_LIBGRIB
else if ( gribCheckSeek(fileID, &recpos, &version) == 0 )
    {
    if ( version <= 1 )
        {
        filetype = CDI_FILETYPE_GRB;
        if ( CDI_Debug ) Message("found seeked GRIB file = %s", filename);
        }
    else if ( version == 2 )
        {
        filetype = CDI_FILETYPE_GRB2;
        if ( CDI_Debug ) Message("found seeked GRIB2 file = %s", filename);
        }
    }
#endif

fileClose(fileID);

*byteorder = getByteorder(swap);

return filetype;
}


int streamInqFiletype(int streamID)
{
stream_t *streamptr = stream_to_pointer(streamID);
return streamptr->filetype;
}


int getByteswap(int byteorder)
{
int byteswap = -1;

switch (byteorder)
    {
    case CDI_BIGENDIAN:
    case CDI_LITTLEENDIAN:
    case CDI_PDPENDIAN:
    byteswap = (HOST_ENDIANNESS != byteorder);
    break;
    case -1:
    break;
    default:
    Error("unexpected byteorder %d query!", byteorder);
    }

return byteswap;
}


void streamDefByteorder(int streamID, int byteorder)
{
stream_t *streamptr = stream_to_pointer(streamID);
streamptr->byteorder = byteorder;
int filetype = streamptr->filetype;

switch (filetype)
    {
#ifdef  HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    {
        srvrec_t *srvp = (srvrec_t*) streamptr->record->exsep;
        srvp->byteswap = getByteswap(byteorder);

        break;
    }
#endif
#ifdef  HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    {
        extrec_t *extp = (extrec_t*) streamptr->record->exsep;
        extp->byteswap = getByteswap(byteorder);

        break;
    }
#endif
#ifdef  HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    {
        iegrec_t *iegp = (iegrec_t*) streamptr->record->exsep;
        iegp->byteswap = getByteswap(byteorder);

        break;
    }
#endif
    }
reshSetStatus(streamID, &streamOps, RESH_DESYNC_IN_USE);
}


int streamInqByteorder(int streamID)
{
stream_t *streamptr = stream_to_pointer(streamID);
return streamptr->byteorder;
}


const char *streamFilesuffix(int filetype)
{
static const char *noSuffix  = "";
static const char *ncSuffix  = ".nc";
static const char *grbSuffix = ".grb";
static const char *srvSuffix = ".srv";
static const char *extSuffix = ".ext";
static const char *iegSuffix = ".ieg";

switch (filetype)
    {
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:   return ncSuffix;
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:  return grbSuffix;
    case CDI_FILETYPE_SRV:   return srvSuffix;
    case CDI_FILETYPE_EXT:   return extSuffix;
    case CDI_FILETYPE_IEG:   return iegSuffix;
    default:                 return noSuffix;
    }
}


const char *streamFilename(int streamID)
{
stream_t *streamptr = stream_to_pointer(streamID);
return streamptr->filename;
}

static
long cdiInqTimeSize(int streamID)
{
int tsID = 0, nrecs;
stream_t *streamptr = stream_to_pointer(streamID);
long ntsteps = streamptr->ntsteps;

if ( ntsteps == (long)CDI_UNDEFID )
    while ( (nrecs = streamInqTimestep(streamID, tsID++)) )
    ntsteps = streamptr->ntsteps;

return ntsteps;
}

static
int cdiInqContents(stream_t *streamptr)
{
int status = 0;
int filetype = streamptr->filetype;

switch (filetype)
    {
#ifdef  HAVE_LIBGRIB
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
    {
        status = grbInqContents(streamptr);
        break;
    }
#endif
#ifdef  HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    {
        status = srvInqContents(streamptr);
        break;
    }
#endif
#ifdef  HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    {
        status = extInqContents(streamptr);
        break;
    }
#endif
#ifdef  HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    {
        status = iegInqContents(streamptr);
        break;
    }
#endif
#ifdef  HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    {
        status = cdfInqContents(streamptr);
        break;
    }
#endif
    default:
    {
        if ( CDI_Debug )
        Message("%s support not compiled in!", strfiletype(filetype));

        status = CDI_ELIBNAVAIL;
        break;
    }
    }

if ( status == 0 )
    {
    int vlistID = streamptr->vlistID;
    int taxisID = vlistInqTaxis(vlistID);
    if ( taxisID != CDI_UNDEFID )
        {
        taxis_t *taxisptr1 = &streamptr->tsteps[0].taxis;
        taxis_t *taxisptr2 = taxisPtr(taxisID);
        ptaxisCopy(taxisptr2, taxisptr1);
        }
    }

return status;
}

int cdiStreamOpenDefaultDelegate(const char *filename, char filemode,
                                int filetype, stream_t *streamptr,
                                int recordBufIsToBeCreated)
{
int fileID;
switch (filetype)
    {
#ifdef  HAVE_LIBGRIB
    case CDI_FILETYPE_GRB:
#ifdef  HAVE_LIBGRIB_API
    case CDI_FILETYPE_GRB2:
#endif
    {
#ifndef __cplusplus
        fileID = gribOpen(filename, (char [2]){filemode, 0});
#else
        char temp[2] = { filemode, 0 };
        fileID = gribOpen(filename, temp);
#endif
        if ( fileID < 0 ) fileID = CDI_ESYSTEM;
        if (recordBufIsToBeCreated)
        {
            streamptr->record = (Record *) Malloc(sizeof(Record));
            streamptr->record->buffer = NULL;
        }
        break;
    }
#endif
#ifdef  HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    {
#ifndef __cplusplus
        fileID = fileOpen(filename, (char [2]){filemode, 0});
#else
        char temp[2] = { filemode, 0 };
        fileID = fileOpen(filename, temp);
#endif
        if ( fileID < 0 ) fileID = CDI_ESYSTEM;
        if (recordBufIsToBeCreated)
        {
            streamptr->record = (Record *) Malloc(sizeof(Record));
            streamptr->record->buffer = NULL;
            streamptr->record->exsep  = srvNew();
        }
        break;
    }
#endif
#ifdef  HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    {
#ifndef __cplusplus
        fileID = fileOpen(filename, (char [2]){filemode, 0});
#else
        char temp[2] = { filemode, 0 };
        fileID = fileOpen(filename, temp);
#endif

        if ( fileID < 0 ) fileID = CDI_ESYSTEM;
        if (recordBufIsToBeCreated)
        {
            streamptr->record = (Record *) Malloc(sizeof(Record));
            streamptr->record->buffer = NULL;
            streamptr->record->exsep  = extNew();
        }
        break;
    }
#endif
#ifdef  HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    {
#ifndef __cplusplus
        fileID = fileOpen(filename, (char [2]){filemode, 0});
#else
        char temp[2] = { filemode, 0 };
        fileID = fileOpen(filename, temp);
#endif
        if ( fileID < 0 ) fileID = CDI_ESYSTEM;
        if (recordBufIsToBeCreated)
        {
            streamptr->record = (Record *) Malloc(sizeof(Record));
            streamptr->record->buffer = NULL;
            streamptr->record->exsep  = iegNew();
        }
        break;
    }
#endif
#ifdef  HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC5:
    {
#ifndef __cplusplus
        fileID = cdfOpen(filename, (char [2]){filemode, 0}, filetype);
#else
        char temp[2] = { filemode, 0 };
        fileID = cdfOpen(filename, temp, filetype);
#endif
        break;
    }
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    {
#ifndef __cplusplus
        fileID = cdf4Open(filename, (char [2]){filemode, 0}, &filetype);
#else
        char temp[2] = { filemode, 0 };
        fileID = cdf4Open(filename, temp, &filetype);
#endif
        break;
    }
#endif
    default:
    {
        if ( CDI_Debug ) Message("%s support not compiled in!", strfiletype(filetype));
        return CDI_ELIBNAVAIL;
    }
    }

streamptr->filetype = filetype;

return fileID;
}


int streamOpenID(const char *filename, char filemode, int filetype, int resH)
{
if ( CDI_Debug )
    Message("Open %s mode %c file %s", strfiletype(filetype), filemode,
            filename?filename:"(NUL)");

if ( ! filename || filetype < 0 ) return CDI_EINVAL;

stream_t *streamptr = stream_new_entry(resH);
int streamID = CDI_ESYSTEM;

int (*streamOpenDelegate)(const char *filename, char filemode,
                            int filetype, stream_t *streamptr, int recordBufIsToBeCreated)
    = (int (*)(const char *, char, int, stream_t *, int))
    namespaceSwitchGet(NSSWITCH_STREAM_OPEN_BACKEND).func;

int fileID = streamOpenDelegate(filename, filemode, filetype, streamptr, 1);
if ( fileID < 0 )
    {
    streamID = fileID;
    }
else
    {
    streamID = streamptr->self;
    if ( streamID < 0 ) return CDI_ELIMIT;

    streamptr->filemode = filemode;
    streamptr->filename = strdupx(filename);
    streamptr->fileID   = fileID;

    if ( filemode == 'r' )
        {
        int vlistID = vlistCreate();
        if ( vlistID < 0 ) return CDI_ELIMIT;

        cdiVlistMakeInternal(vlistID);
        streamptr->vlistID = vlistID;

        int status = cdiInqContents(streamptr);
        if ( status < 0 )
            {
            streamID = status;
            }
        else
            {
            vlist_t *vlistptr = vlist_to_pointer(streamptr->vlistID);
            vlistptr->ntsteps = streamptr->ntsteps;
            cdiVlistMakeImmutable(vlistID);
            }
        }
    }

if ( streamID < 0 )
    {
    Free(streamptr->record);
    stream_delete_entry(streamptr);
    }

return streamID;
}

static
int streamOpen(const char *filename, const char *filemode, int filetype)
{
if ( !filemode || strlen(filemode) != 1 ) return CDI_EINVAL;
return streamOpenID(filename, (char)tolower(filemode[0]), filetype, CDI_UNDEFID);
}

static
int streamOpenA(const char *filename, const char *filemode, int filetype)
{
if ( CDI_Debug )
    Message("Open %s file (mode=%c); filename: %s", strfiletype(filetype), (int) *filemode, filename);
if ( CDI_Debug ) printf("streamOpenA: %s\n", filename);

if ( ! filename || ! filemode || filetype < 0 ) return CDI_EINVAL;

stream_t *streamptr = stream_new_entry(CDI_UNDEFID);
int fileID = CDI_UNDEFID;

{
    int (*streamOpenDelegate)(const char *filename, char filemode,
                            int filetype, stream_t *streamptr, int recordBufIsToBeCreated)
    = (int (*)(const char *, char, int, stream_t *, int))
    namespaceSwitchGet(NSSWITCH_STREAM_OPEN_BACKEND).func;

    fileID = streamOpenDelegate(filename, 'r', filetype, streamptr, 1);
}

if ( fileID == CDI_UNDEFID || fileID == CDI_ELIBNAVAIL || fileID == CDI_ESYSTEM ) return fileID;

int streamID = streamptr->self;

streamptr->filemode = tolower(*filemode);
streamptr->filename = strdupx(filename);
streamptr->fileID   = fileID;

streamptr->vlistID = vlistCreate();
cdiVlistMakeInternal(streamptr->vlistID);

int status = cdiInqContents(streamptr);
if ( status < 0 ) return status;
vlist_t *vlistptr = vlist_to_pointer(streamptr->vlistID);
vlistptr->ntsteps = (int)cdiInqTimeSize(streamID);


for ( int varID = 0; varID < vlistptr->nvars; ++varID )
    streamptr->vars[varID].defmiss = true;

if ( !strcmp(filemode, "r") ) cdiVlistMakeImmutable(streamptr->vlistID);

{
    void (*streamCloseDelegate)(stream_t *streamptr, int recordBufIsToBeDeleted)
    = (void (*)(stream_t *, int))
    namespaceSwitchGet(NSSWITCH_STREAM_CLOSE_BACKEND).func;

    streamCloseDelegate(streamptr, 0);
}

switch (filetype)
    {
#ifdef  HAVE_LIBGRIB
    case CDI_FILETYPE_GRB:
#ifdef  HAVE_LIBGRIB_API
    case CDI_FILETYPE_GRB2:
#endif
    {
        fileID = gribOpen(filename, filemode);
        if ( fileID != CDI_UNDEFID ) gribContainersNew(streamptr);
        break;
    }
#endif
#ifdef  HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    {
        fileID = fileOpen(filename, filemode);
        break;
    }
#endif
#ifdef  HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    {
        fileID = fileOpen(filename, filemode);
        break;
    }
#endif
#ifdef  HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    {
        fileID = fileOpen(filename, filemode);
        break;
    }
#endif
#ifdef  HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC5:
    {
        fileID = cdfOpen(filename, filemode, filetype);
        streamptr->ncmode = 2;
        break;
    }
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    {
        fileID = cdf4Open(filename, filemode, &filetype);
        streamptr->ncmode = 2;
        break;
    }
#endif
    default:
    {
        if ( CDI_Debug ) Message("%s support not compiled in!", strfiletype(filetype));
        return CDI_ELIBNAVAIL;
    }
    }

if ( fileID == CDI_UNDEFID )
    streamID = CDI_UNDEFID;
else
    streamptr->fileID = fileID;

return streamID;
}


int streamOpenRead(const char *filename)
{
cdiInitialize();

int byteorder = 0;
int filetype = cdiGetFiletype(filename, &byteorder);

if ( filetype < 0 ) return filetype;

int streamID = streamOpen(filename, "r", filetype);

if ( streamID >= 0 )
    {
    stream_t *streamptr = stream_to_pointer(streamID);
    streamptr->byteorder = byteorder;
    }

return streamID;
}


int streamOpenAppend(const char *filename)
{
cdiInitialize();

int byteorder = 0;
int filetype = cdiGetFiletype(filename, &byteorder);

if ( filetype < 0 ) return filetype;

int streamID = streamOpenA(filename, "a", filetype);

if ( streamID >= 0 )
    {
    stream_t *streamptr = stream_to_pointer(streamID);
    streamptr->byteorder = byteorder;
    }

return streamID;
}


int streamOpenWrite(const char *filename, int filetype)
{
cdiInitialize();

return streamOpen(filename, "w", filetype);
}

static
void streamDefaultValue ( stream_t * streamptr )
{
streamptr->self              = CDI_UNDEFID;
streamptr->accesstype        = CDI_UNDEFID;
streamptr->accessmode        = 0;
streamptr->filetype          = CDI_FILETYPE_UNDEF;
streamptr->byteorder         = CDI_UNDEFID;
streamptr->fileID            = 0;
streamptr->filemode          = 0;
streamptr->numvals           = 0;
streamptr->filename          = NULL;
streamptr->record            = NULL;
streamptr->varsAllocated     = 0;
streamptr->nrecs             = 0;
streamptr->nvars             = 0;
streamptr->vars              = NULL;
streamptr->ncmode            = 0;
streamptr->curTsID           = CDI_UNDEFID;
streamptr->rtsteps           = 0;
streamptr->ntsteps           = CDI_UNDEFID;
streamptr->tsteps            = NULL;
streamptr->tstepsTableSize   = 0;
streamptr->tstepsNextID      = 0;
streamptr->historyID         = CDI_UNDEFID;
streamptr->vlistID           = CDI_UNDEFID;
streamptr->globalatts        = 0;
streamptr->localatts         = 0;
streamptr->unreduced         = cdiDataUnreduced;
streamptr->sortname          = cdiSortName > 0;
streamptr->sortparam         = cdiSortParam > 0;
streamptr->have_missval      = cdiHaveMissval;
streamptr->comptype          = CDI_COMPRESS_NONE;
streamptr->complevel         = 0;

basetimeInit(&streamptr->basetime);

#ifdef HAVE_LIBNETCDF
for ( int i = 0; i < MAX_ZAXES_PS; i++ ) streamptr->zaxisID[i]  = CDI_UNDEFID;
for ( int i = 0; i < MAX_ZAXES_PS; i++ ) streamptr->nczvarID[i] = CDI_UNDEFID;

for ( int i = 0; i < MAX_GRIDS_PS; i++ )
    {
    streamptr->ncgrid[i].gridID = CDI_UNDEFID;
    for (size_t j = 0; j < CDF_SIZE_ncIDs; ++j)
        streamptr->ncgrid[i].ncIDs[j] = CDI_UNDEFID;
    }

streamptr->vct.ilev          = 0;
streamptr->vct.mlev          = 0;
streamptr->vct.ilevID        = CDI_UNDEFID;
streamptr->vct.mlevID        = CDI_UNDEFID;
#endif

streamptr->gribContainers    = NULL;
}

static
stream_t *stream_new_entry(int resH)
{
cdiInitialize();

stream_t *streamptr = (stream_t *) Malloc(sizeof(stream_t));
streamDefaultValue ( streamptr );

if (resH == CDI_UNDEFID)
    streamptr->self = reshPut(streamptr, &streamOps);
else
    {
    streamptr->self = resH;
    reshReplace(resH, streamptr, &streamOps);
    }

return streamptr;
}


void cdiStreamCloseDefaultDelegate(stream_t *streamptr, int recordBufIsToBeDeleted)
{
int fileID   = streamptr->fileID;
int filetype = streamptr->filetype;
if ( fileID == CDI_UNDEFID )
    Warning("File %s not open!", streamptr->filename);
else
    switch (filetype)
    {
#ifdef  HAVE_LIBGRIB
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
        {
        gribClose(fileID);
        if ( recordBufIsToBeDeleted ) gribContainersDelete(streamptr);
        break;
        }
#endif
#ifdef  HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
        {
        fileClose(fileID);
        if ( recordBufIsToBeDeleted ) srvDelete(streamptr->record->exsep);
        break;
        }
#endif
#ifdef  HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
        {
        fileClose(fileID);
        if ( recordBufIsToBeDeleted ) extDelete(streamptr->record->exsep);
        break;
        }
#endif
#ifdef  HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
        {
        fileClose(fileID);
        if ( recordBufIsToBeDeleted ) iegDelete(streamptr->record->exsep);
        break;
        }
#endif
#ifdef  HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
        {
        cdfClose(fileID);
        if (streamptr->ntsteps == 0)
            {
            if ( streamptr->tsteps[0].records )
                {
                Free(streamptr->tsteps[0].records);
                streamptr->tsteps[0].records = NULL;
                }
            if ( streamptr->tsteps[0].recIDs )
                {
                Free(streamptr->tsteps[0].recIDs);
                streamptr->tsteps[0].recIDs = NULL;
                }
            }
        break;
        }
#endif
    default:
        {
        Error("%s support not compiled in (fileID = %d)!", strfiletype(filetype), fileID);
        break;
        }
    }
}


static
void deallocate_sleveltable_t(sleveltable_t *entry)
{
if (entry->recordID) Free(entry->recordID);
if (entry->lindex)   Free(entry->lindex);
entry->recordID = NULL;
entry->lindex   = NULL;
}



void streamClose(int streamID)
{
stream_t *streamptr = stream_to_pointer(streamID);

if ( CDI_Debug )
    Message("streamID = %d filename = %s", streamID, streamptr->filename);

int vlistID  = streamptr->vlistID;

void (*streamCloseDelegate)(stream_t *streamptr, int recordBufIsToBeDeleted)
    = (void (*)(stream_t *, int))
    namespaceSwitchGet(NSSWITCH_STREAM_CLOSE_BACKEND).func;

if ( streamptr->filetype != -1 ) streamCloseDelegate(streamptr, 1);

if ( streamptr->record )
    {
    if ( streamptr->record->buffer )
        Free(streamptr->record->buffer);

    Free(streamptr->record);
    }

streamptr->filetype = 0;
if ( streamptr->filename ) Free(streamptr->filename);

for ( int index = 0; index < streamptr->nvars; index++ )
    {
    sleveltable_t *pslev = streamptr->vars[index].recordTable;
    unsigned nsub = streamptr->vars[index].subtypeSize >= 0
        ? (unsigned)streamptr->vars[index].subtypeSize : 0U;
    for (size_t isub=0; isub < nsub; isub++)
        {
        deallocate_sleveltable_t(pslev + isub);
        }
    if (pslev) Free(pslev);
    }
Free(streamptr->vars);
streamptr->vars = NULL;

for ( int index = 0; index < streamptr->ntsteps; ++index )
    {
    if ( streamptr->tsteps[index].records ) Free(streamptr->tsteps[index].records);
    if ( streamptr->tsteps[index].recIDs  ) Free(streamptr->tsteps[index].recIDs);
    taxisDestroyKernel(&streamptr->tsteps[index].taxis);
    }

if ( streamptr->tsteps ) Free(streamptr->tsteps);

if ( streamptr->basetime.timevar_cache ) Free(streamptr->basetime.timevar_cache);

if ( vlistID != -1 )
    {
    if ( streamptr->filemode != 'w' && vlistInqTaxis(vlistID) != -1 )
        taxisDestroy(vlistInqTaxis(vlistID));

    cdiVlistDestroy_(vlistID);
    }

stream_delete_entry(streamptr);
}

static
void stream_delete_entry(stream_t *streamptr)
{
xassert ( streamptr );

int idx = streamptr->self;
Free(streamptr);
reshRemove ( idx, &streamOps );

if ( CDI_Debug )
    Message("Removed idx %d from stream list", idx);
}


void cdiStreamSync_(stream_t *streamptr)
{
int fileID   = streamptr->fileID;
int filetype = streamptr->filetype;
int vlistID  = streamptr->vlistID;
int nvars    = vlistNvars(vlistID);

if      ( fileID == CDI_UNDEFID )  Warning("File %s not open!", streamptr->filename);
else if ( vlistID == CDI_UNDEFID ) Warning("Vlist undefined for file %s!", streamptr->filename);
else if ( nvars == 0 )             Warning("No variables defined!");
else
    {
    if ( streamptr->filemode == 'w' || streamptr->filemode == 'a' )
        {
        switch (filetype)
            {
#ifdef  HAVE_LIBNETCDF
            case CDI_FILETYPE_NC:
            case CDI_FILETYPE_NC2:
            case CDI_FILETYPE_NC4:
            case CDI_FILETYPE_NC4C:
            case CDI_FILETYPE_NC5:
            {
                void cdf_sync(int ncid);
                if ( streamptr->ncmode == 2 ) cdf_sync(fileID);
                break;
            }
#endif
            default:
            {
                fileFlush(fileID);
                break;
            }
            }
        }
    }
}


void streamSync(int streamID)
{
stream_t *streamptr = stream_to_pointer(streamID);

void (*myStreamSync_)(stream_t *streamptr)
    = (void (*)(stream_t *))namespaceSwitchGet(NSSWITCH_STREAM_SYNC).func;
myStreamSync_(streamptr);
}


int cdiStreamDefTimestep_(stream_t *streamptr, int tsID)
{
stream_check_ptr(__func__, streamptr);

if ( CDI_Debug ) Message("streamID = %d  tsID = %d", streamptr->self, tsID);

int vlistID = streamptr->vlistID;
int time_is_varying = vlistHasTime(vlistID);
int taxisID = vlistInqTaxis(vlistID) ;


if ( tsID > 0 )
    {
    int newtsID = tstepsNewEntry(streamptr);
    if ( tsID != newtsID )
        Error("Internal problem: tsID = %d newtsID = %d", tsID, newtsID);
    }

if ( time_is_varying )
    ptaxisCopy(&streamptr->tsteps[tsID].taxis, taxisPtr(taxisID));

streamptr->curTsID = tsID;
streamptr->ntsteps = tsID + 1;

#ifdef HAVE_LIBNETCDF
if ((streamptr->filetype == CDI_FILETYPE_NC  ||
    streamptr->filetype == CDI_FILETYPE_NC2 ||
    streamptr->filetype == CDI_FILETYPE_NC5 ||
    streamptr->filetype == CDI_FILETYPE_NC4 ||
    streamptr->filetype == CDI_FILETYPE_NC4C)
    && time_is_varying)
    {
    void (*myCdfDefTimestep)(stream_t *streamptr, int tsID)
        = (void (*)(stream_t *, int))
        namespaceSwitchGet(NSSWITCH_CDF_DEF_TIMESTEP).func;
    myCdfDefTimestep(streamptr, tsID);
    }
#endif

cdi_create_records(streamptr, tsID);

return (int)streamptr->ntsteps;
}


int streamDefTimestep(int streamID, int tsID)
{
stream_t *streamptr = stream_to_pointer(streamID);
int (*myStreamDefTimestep_)(stream_t *streamptr, int tsID)
    = (int (*)(stream_t *, int))
    namespaceSwitchGet(NSSWITCH_STREAM_DEF_TIMESTEP_).func;
return myStreamDefTimestep_(streamptr, tsID);
}


int streamInqCurTimestepID(int streamID)
{
stream_t *streamptr = stream_to_pointer(streamID);
return streamptr->curTsID;
}


int streamInqTimestep(int streamID, int tsID)
{
int nrecs = 0;
int taxisID;
stream_t *streamptr = stream_to_pointer(streamID);
int vlistID = streamptr->vlistID;

if ( tsID < streamptr->rtsteps )
    {
    streamptr->curTsID = tsID;
    nrecs = streamptr->tsteps[tsID].nrecs;
    streamptr->tsteps[tsID].curRecID = CDI_UNDEFID;
    taxisID = vlistInqTaxis(vlistID);
    if ( taxisID == -1 )
        Error("Timestep undefined for fileID = %d", streamID);
    ptaxisCopy(taxisPtr(taxisID), &streamptr->tsteps[tsID].taxis);

    return nrecs;
    }

if ( tsID >= streamptr->ntsteps && streamptr->ntsteps != CDI_UNDEFID )
    {
    return 0;
    }

int filetype = streamptr->filetype;

if ( CDI_Debug )
    Message("streamID = %d  tsID = %d  filetype = %d", streamID, tsID, filetype);

switch (filetype)
    {
#ifdef  HAVE_LIBGRIB
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
    {
        nrecs = grbInqTimestep(streamptr, tsID);
        break;
    }
#endif
#ifdef  HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    {
        nrecs = srvInqTimestep(streamptr, tsID);
        break;
    }
#endif
#ifdef  HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    {
        nrecs = extInqTimestep(streamptr, tsID);
        break;
    }
#endif
#ifdef  HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    {
        nrecs = iegInqTimestep(streamptr, tsID);
        break;
    }
#endif
#ifdef  HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    {
        nrecs = cdfInqTimestep(streamptr, tsID);
        break;
    }
#endif
    default:
    {
        Error("%s support not compiled in!", strfiletype(filetype));
        break;
    }
    }

taxisID = vlistInqTaxis(vlistID);
if ( taxisID == -1 )
    Error("Timestep undefined for fileID = %d", streamID);

ptaxisCopy(taxisPtr(taxisID), &streamptr->tsteps[tsID].taxis);

return nrecs;
}

#if 0
void streamWriteContents(int streamID, char *cname)
{
stream_t *streamptr = stream_to_pointer(streamID);

int vlistID = streamptr->vlistID;

FILE *cnp = fopen(cname, "w");

if ( cnp == NULL ) SysError(cname);

fprintf(cnp, "#CDI library version %s\n"
        "#\n", cdiLibraryVersion());

int filetype = streamptr->filetype;
fprintf(cnp, "filename: %s\n"
        "filetype: %s\n", streamptr->filename, strfiletype(filetype));

fputs("#\n#grids:\n", cnp);

int ngrids = vlistNgrids(vlistID);
for ( int i = 0; i < ngrids; i++ )
    {
    int gridID   = vlistGrid(vlistID, i);
    int gridtype = gridInqType(gridID);
    size_t xsize    = gridInqXsize(gridID);
    size_t ysize    = gridInqYsize(gridID);
    fprintf(cnp, "%4d:%4d:%4zu:%4zu\n", i+1, gridtype, xsize, ysize);
    }

fputs("#\nvarID:code:gridID:zaxisID:tsteptype:datatype\n", cnp);

int nvars = vlistNvars(vlistID);
for ( int varID = 0; varID < nvars; varID++ )
    {
    int code      = vlistInqVarCode(vlistID, varID);
    int gridID    = vlistInqVarGrid(vlistID, varID);
    int zaxisID   = vlistInqVarZaxis(vlistID, varID);
    int tsteptype = vlistInqVarTsteptype(vlistID, varID);
    int datatype  = vlistInqVarDatatype(vlistID, varID);
    fprintf(cnp, "%4d:%4d:%4d:%4d:%4d:%4d:\n",
            varID+1, code, gridID, zaxisID, tsteptype, datatype);
    }

fputs("#\ntsID:nrecs:date:time\n", cnp);

int tsID = 0;
while (1)
    {
    int nrecs      = streamptr->tsteps[tsID].nallrecs;
    int date       = streamptr->tsteps[tsID].taxis.vdate;
    int time       = streamptr->tsteps[tsID].taxis.vtime;
    off_t position = streamptr->tsteps[tsID].position;

    fprintf(cnp, "%4d:%4d:%4d:%4d:%ld\n",
            tsID, nrecs, date, time, (long) position);

    if ( streamptr->tsteps[tsID].next )
        tsID++;
    else
        break;
    }

fputs("#\ntsID:recID:varID:levID:size:pos\n", cnp);

tsID = 0;
while (1)
    {
    int nrecs = streamptr->tsteps[tsID].nallrecs;
    for ( int recID = 0; recID < nrecs; recID++ )
        {
        int varID   = streamptr->tsteps[tsID].records[recID].varID;
        int levelID = streamptr->tsteps[tsID].records[recID].levelID;
        off_t recpos = streamptr->tsteps[tsID].records[recID].position;
        long recsize = (long)streamptr->tsteps[tsID].records[recID].size;
        fprintf(cnp, "%4d:%4d:%4d:%4d:%4ld:%ld\n",
                tsID, recID, varID, levelID, recsize, (long) recpos);
        }

    if ( streamptr->tsteps[tsID].next )
        tsID++;
    else
        break;
    }

fclose(cnp);
}
#endif


size_t streamNvals(int streamID)
{
stream_t *streamptr = stream_to_pointer(streamID);
return streamptr->numvals;
}


void streamDefVlist(int streamID, int vlistID)
{
void (*myStreamDefVlist)(int streamID, int vlistID)
    = (void (*)(int, int))namespaceSwitchGet(NSSWITCH_STREAM_DEF_VLIST_).func;
myStreamDefVlist(streamID, vlistID);
}


void cdiStreamDefVlist_(int streamID, int vlistID)
{
stream_t *streamptr = stream_to_pointer(streamID);

if ( streamptr->vlistID == CDI_UNDEFID )
    {
    int vlistCopy = vlistDuplicate(vlistID);
    cdiVlistMakeInternal(vlistCopy);
    cdiVlistMakeImmutable(vlistID);
    cdiStreamSetupVlist(streamptr, vlistCopy);
    }
else
    Warning("vlist already defined for %s!", streamptr->filename);
}


int streamInqVlist(int streamID)
{
stream_t *streamptr = stream_to_pointer(streamID);
return streamptr->vlistID;
}


void streamDefCompType(int streamID, int comptype)
{
stream_t *streamptr = stream_to_pointer(streamID);
if ( streamptr->comptype != comptype )
    {
    streamptr->comptype = comptype;
    reshSetStatus(streamID, &streamOps, RESH_DESYNC_IN_USE);
    }
}


void streamDefCompLevel(int streamID, int complevel)
{
stream_t *streamptr = stream_to_pointer(streamID);
if ( streamptr->complevel != complevel )
    {
    streamptr->complevel = complevel;
    reshSetStatus(streamID, &streamOps, RESH_DESYNC_IN_USE);
    }
}


int streamInqCompType(int streamID)
{
stream_t *streamptr = stream_to_pointer(streamID);
return streamptr->comptype;
}


int streamInqCompLevel(int streamID)
{
stream_t *streamptr = stream_to_pointer(streamID);
return streamptr->complevel;
}

int streamInqFileID(int streamID)
{
stream_t *streamptr = ( stream_t *) reshGetVal ( streamID, &streamOps );
return streamptr->fileID;
}


void cdiDefAccesstype(int streamID, int type)
{
stream_t *streamptr = (stream_t *)reshGetVal(streamID, &streamOps);

if ( streamptr->accesstype == CDI_UNDEFID )
    {
    streamptr->accesstype = type;
    }
else if ( streamptr->accesstype != type )
    Error("Changing access type from %s not allowed!",
        streamptr->accesstype == TYPE_REC ? "REC to VAR" : "VAR to REC");
}


int cdiInqAccesstype(int streamID)
{
stream_t *streamptr = (stream_t *) reshGetVal ( streamID, &streamOps );
return streamptr->accesstype;
}

static
int streamTxCode(void)
{
return STREAM;
}

void cdiStreamSetupVlist(stream_t *streamptr, int vlistID)
{
void (*myStreamSetupVlist)(stream_t *streamptr, int vlistID)
    = (void (*)(stream_t *, int)) namespaceSwitchGet(NSSWITCH_STREAM_SETUP_VLIST).func;
myStreamSetupVlist(streamptr, vlistID);
}


void cdiStreamSetupVlist_(stream_t *streamptr, int vlistID)
{
streamptr->vlistID = vlistID;
int nvars = vlistNvars(vlistID);
for ( int varID = 0; varID < nvars; varID++ )
    {
    int gridID    = vlistInqVarGrid(vlistID, varID);
    int zaxisID   = vlistInqVarZaxis(vlistID, varID);
    int tilesetID = vlistInqVarSubtype(vlistID, varID);
    stream_new_var(streamptr, gridID, zaxisID, tilesetID);
    if ( streamptr->have_missval )
        vlistDefVarMissval(vlistID, varID, vlistInqVarMissval(vlistID, varID));
    }

if (streamptr->filemode == 'w')
    {
    tstepsNewEntry(streamptr);
    int vlistID = streamptr->vlistID;
    int time_is_varying = vlistHasTime(vlistID);
    if ( time_is_varying )
        {
        int taxisID = vlistInqTaxis(vlistID);
        if ( taxisID == CDI_UNDEFID )
            {
            Warning("taxisID undefined for fileID = %d! Using absolute time axis.", streamptr->self);
            taxisID = taxisCreate(TAXIS_ABSOLUTE);
            vlistDefTaxis(vlistID, taxisID);
            }

        if ( taxisInqType(taxisID) == TAXIS_RELATIVE )
            switch (streamptr->filetype)
            {
#ifdef HAVE_LIBNETCDF
            case CDI_FILETYPE_NC:
            case CDI_FILETYPE_NC2:
            case CDI_FILETYPE_NC4:
            case CDI_FILETYPE_NC4C:
            case CDI_FILETYPE_NC5:
                {
                taxis_t *taxisptr = taxisPtr(taxisID);
                if ( taxisptr->rdate == -1 )
                    {
                    int vdate = taxisInqVdate(taxisID);
                    if ( vdate == 0 ) vdate = 10101;
                    taxisDefRdate(taxisID, vdate);
                    }
                }
                break;
#endif
            default:
                ;
            }
        ptaxisCopy(&streamptr->tsteps[0].taxis, taxisPtr(taxisID));
        }

    switch (streamptr->filetype)
        {
#ifdef HAVE_LIBNETCDF
        case CDI_FILETYPE_NC:
        case CDI_FILETYPE_NC2:
        case CDI_FILETYPE_NC4:
        case CDI_FILETYPE_NC4C:
        case CDI_FILETYPE_NC5:
        {

            void (*myCdfDefVars)(stream_t *streamptr)
            = (void (*)(stream_t *)) namespaceSwitchGet(NSSWITCH_CDF_STREAM_SETUP).func;
            myCdfDefVars(streamptr);
        }
        break;
#endif
#ifdef HAVE_LIBGRIB
        case CDI_FILETYPE_GRB:
        case CDI_FILETYPE_GRB2:
        gribContainersNew(streamptr);
        break;
#endif
        default:
        ;
        }
    }
}


void cdiStreamGetIndexList(unsigned numIDs, int *IDs)
{
reshGetResHListOfType(numIDs, IDs, &streamOps);
}

int streamInqNvars ( int streamID )
{
stream_t *streamptr = (stream_t *)reshGetVal(streamID, &streamOps);
return streamptr->nvars;
}


static int streamCompareP(void * streamptr1, void * streamptr2)
{
stream_t * s1 = ( stream_t * ) streamptr1;
stream_t * s2 = ( stream_t * ) streamptr2;
enum {
    differ = -1,
    equal  = 0,
};

xassert ( s1 );
xassert ( s2 );

if ( s1->filetype  != s2->filetype  ) return differ;
if ( s1->byteorder != s2->byteorder ) return differ;
if ( s1->comptype  != s2->comptype  ) return differ;
if ( s1->complevel != s2->complevel ) return differ;

if ( s1->filename )
    {
    if (strcmp(s1->filename, s2->filename))
        return differ;
    }
else if ( s2->filename )
    return differ;

return equal;
}


void streamDestroyP ( void * streamptr )
{
stream_t *sp = ( stream_t * ) streamptr;

xassert ( sp );

int id = sp->self;
streamClose ( id );
}


void streamPrintP   ( void * streamptr, FILE * fp )
{
stream_t * sp = ( stream_t * ) streamptr;

if ( !sp ) return;

fprintf(fp, "#\n"
        "# streamID %d\n"
        "#\n"
        "self          = %d\n"
        "accesstype    = %d\n"
        "accessmode    = %d\n"
        "filetype      = %d\n"
        "byteorder     = %d\n"
        "fileID        = %d\n"
        "filemode      = %d\n"
        "filename      = %s\n"
        "nrecs         = %d\n"
        "nvars         = %d\n"
        "varsAllocated = %d\n"
        "curTsID       = %d\n"
        "rtsteps       = %d\n"
        "ntsteps       = %ld\n"
        "tstepsTableSize= %d\n"
        "tstepsNextID  = %d\n"
        "ncmode        = %d\n"
        "vlistID       = %d\n"
        "historyID     = %d\n"
        "globalatts    = %d\n"
        "localatts     = %d\n"
        "unreduced     = %d\n"
        "sortname      = %d\n"
        "have_missval  = %d\n"
        "ztype         = %d\n"
        "zlevel        = %d\n",
        sp->self, sp->self, sp->accesstype, sp->accessmode,
        sp->filetype, sp->byteorder, sp->fileID, sp->filemode,
        sp->filename, sp->nrecs, sp->nvars, sp->varsAllocated,
        sp->curTsID, sp->rtsteps, sp->ntsteps, sp->tstepsTableSize,
        sp->tstepsNextID, sp->ncmode, sp->vlistID, sp->historyID,
        sp->globalatts, sp->localatts, sp->unreduced, sp->sortname,
        sp->have_missval, sp->comptype, sp->complevel);
}

enum {
streamNint = 10,
};

static int
streamGetPackSize(void * voidP, void *context)
{
stream_t * streamP = ( stream_t * ) voidP;
int packBufferSize
    = serializeGetSize(streamNint, CDI_DATATYPE_INT, context)
    + serializeGetSize(2, CDI_DATATYPE_UINT32, context)
    + serializeGetSize((int)strlen(streamP->filename) + 1,
                    CDI_DATATYPE_TXT, context)
    + serializeGetSize(1, CDI_DATATYPE_FLT64, context);
return packBufferSize;
}


static void
streamPack(void * streamptr, void * packBuffer, int packBufferSize,
        int * packBufferPos, void *context)
{
stream_t * streamP = ( stream_t * ) streamptr;
int intBuffer[streamNint];

intBuffer[0] = streamP->self;
intBuffer[1] = streamP->filetype;
intBuffer[2] = (int)strlen(streamP->filename) + 1;
intBuffer[3] = streamP->vlistID;
intBuffer[4] = streamP->byteorder;
intBuffer[5] = streamP->comptype;
intBuffer[6] = streamP->complevel;
intBuffer[7] = streamP->unreduced;
intBuffer[8] = streamP->sortname;
intBuffer[9] = streamP->have_missval;

serializePack(intBuffer, streamNint, CDI_DATATYPE_INT, packBuffer, packBufferSize, packBufferPos, context);
uint32_t d = cdiCheckSum(CDI_DATATYPE_INT, streamNint, intBuffer);
serializePack(&d, 1, CDI_DATATYPE_UINT32, packBuffer, packBufferSize, packBufferPos, context);

serializePack(&CDI_default_missval, 1, CDI_DATATYPE_FLT64, packBuffer, packBufferSize, packBufferPos, context);
serializePack(streamP->filename, intBuffer[2], CDI_DATATYPE_TXT, packBuffer, packBufferSize, packBufferPos, context);
d = cdiCheckSum(CDI_DATATYPE_TXT, intBuffer[2], streamP->filename);
serializePack(&d, 1, CDI_DATATYPE_UINT32, packBuffer, packBufferSize, packBufferPos, context);
}

struct streamAssoc
streamUnpack(char * unpackBuffer, int unpackBufferSize,
            int * unpackBufferPos, int originNamespace, void *context)
{
int intBuffer[streamNint];
uint32_t d;
char filename[CDI_MAX_NAME];

serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                intBuffer, streamNint, CDI_DATATYPE_INT, context);
serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                &d, 1, CDI_DATATYPE_UINT32, context);
xassert(cdiCheckSum(CDI_DATATYPE_INT, streamNint, intBuffer) == d);

serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                &CDI_default_missval, 1, CDI_DATATYPE_FLT64, context);
serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                &filename, intBuffer[2], CDI_DATATYPE_TXT, context);
serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                &d, 1, CDI_DATATYPE_UINT32, context);
xassert(d == cdiCheckSum(CDI_DATATYPE_TXT, intBuffer[2], filename));
int targetStreamID = namespaceAdaptKey(intBuffer[0], originNamespace),
    streamID = streamOpenID(filename, 'w', intBuffer[1], targetStreamID);
xassert(streamID >= 0 && targetStreamID == streamID);
streamDefByteorder(streamID, intBuffer[4]);
streamDefCompType(streamID, intBuffer[5]);
streamDefCompLevel(streamID, intBuffer[6]);
stream_t *streamptr = stream_to_pointer(streamID);
streamptr->unreduced = intBuffer[7];
streamptr->sortname = intBuffer[8];
streamptr->have_missval = intBuffer[9];
struct streamAssoc retval = { streamID, intBuffer[3] };
return retval;
}


#ifdef HAVE_CONFIG_H
#endif




int cdiStreamWriteVar_(int streamID, int varID, int memtype, const void *data, size_t nmiss)
{


int status = 0;

if ( CDI_Debug ) Message("streamID = %d varID = %d", streamID, varID);

check_parg(data);

stream_t *streamptr = stream_to_pointer(streamID);
if (subtypeInqActiveIndex(streamptr->vars[varID].subtypeID) != 0)
    Error("Writing of non-trivial subtypes not yet implemented!");


if ( streamptr->curTsID == CDI_UNDEFID ) streamDefTimestep(streamID, 0);

int filetype = streamptr->filetype;

switch (filetype)
    {
#if  defined  (HAVE_LIBGRIB)
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
    {
        grb_write_var(streamptr, varID, memtype, data, nmiss);
        break;
    }
#endif
#ifdef HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        srvWriteVarDP(streamptr, varID, (double *)data);
        break;
    }
#endif
#ifdef HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        extWriteVarDP(streamptr, varID, (double *)data);
        break;
    }
#endif
#ifdef HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        iegWriteVarDP(streamptr, varID, (double *)data);
        break;
    }
#endif
#ifdef HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    {
        cdf_write_var(streamptr, varID, memtype, data, nmiss);
        break;
    }
#endif
    default:
    {
        Error("%s support not compiled in!", strfiletype(filetype));
        break;
    }
    }

return status;
}


void streamWriteVar(int streamID, int varID, const double *data, size_t nmiss)
{
void (*myCdiStreamWriteVar_)(int streamID, int varID, int memtype, const void *data, size_t nmiss)
    = (void (*)(int, int, int, const void *, size_t))
    namespaceSwitchGet(NSSWITCH_STREAM_WRITE_VAR_).func;

myCdiStreamWriteVar_(streamID, varID, MEMTYPE_DOUBLE, (const void *) data, nmiss);
}


void streamWriteVarF(int streamID, int varID, const float *data, size_t nmiss)
{
int (*myCdiStreamWriteVar_)(int streamID, int varID, int memtype, const void *data, size_t nmiss)
    = (int (*)(int, int, int, const void *, size_t))
    namespaceSwitchGet(NSSWITCH_STREAM_WRITE_VAR_).func;

if ( myCdiStreamWriteVar_(streamID, varID, MEMTYPE_FLOAT, (const void *) data, nmiss) )
    {



    int vlistID = streamInqVlist(streamID);
    size_t elementCount = gridInqSize(vlistInqVarGrid(vlistID, varID));
    elementCount *= (size_t) zaxisInqSize(vlistInqVarZaxis(vlistID, varID));
    double *conversionBuffer = (double *) Malloc(elementCount*sizeof(*conversionBuffer));
    for ( size_t i = elementCount; i--; ) conversionBuffer[i] = (double) data[i];
    myCdiStreamWriteVar_(streamID, varID, MEMTYPE_DOUBLE, (const void *) conversionBuffer, nmiss);
    Free(conversionBuffer);
    }
}

static
int cdiStreamWriteVarSlice(int streamID, int varID, int levelID, int memtype, const void *data, size_t nmiss)
{


int status = 0;

if ( CDI_Debug ) Message("streamID = %d varID = %d", streamID, varID);

check_parg(data);

stream_t *streamptr = stream_to_pointer(streamID);
if (subtypeInqActiveIndex(streamptr->vars[varID].subtypeID) != 0)
    Error("Writing of non-trivial subtypes not yet implemented!");


if ( streamptr->curTsID == CDI_UNDEFID ) streamDefTimestep(streamID, 0);

int filetype = streamptr->filetype;

switch (filetype)
    {
#if  defined  (HAVE_LIBGRIB)
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
    {
        grb_write_var_slice(streamptr, varID, levelID, memtype, data, nmiss);
        break;
    }
#endif
#ifdef HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        srvWriteVarSliceDP(streamptr, varID, levelID, (double *)data);
        break;
    }
#endif
#ifdef HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        extWriteVarSliceDP(streamptr, varID, levelID, (double *)data);
        break;
    }
#endif
#ifdef HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        iegWriteVarSliceDP(streamptr, varID, levelID, (double *)data);
        break;
    }
#endif
#ifdef HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    cdf_write_var_slice(streamptr, varID, levelID, memtype, data, nmiss);
    break;
#endif
    default:
    {
        Error("%s support not compiled in!", strfiletype(filetype));
        break;
    }
    }

return status;
}


void streamWriteVarSlice(int streamID, int varID, int levelID, const double *data, size_t nmiss)
{
cdiStreamWriteVarSlice(streamID, varID, levelID, MEMTYPE_DOUBLE, (const void *) data, nmiss);
}


void streamWriteVarSliceF(int streamID, int varID, int levelID, const float *data, size_t nmiss)
{
if ( cdiStreamWriteVarSlice(streamID, varID, levelID, MEMTYPE_FLOAT, (const void *) data, nmiss) )
    {


    size_t elementCount = gridInqSize(vlistInqVarGrid(streamInqVlist(streamID), varID));
    double *conversionBuffer = (double *) Malloc(elementCount*sizeof(*conversionBuffer));
    for ( size_t i = elementCount; i--; ) conversionBuffer[i] = (double) data[i];
    streamWriteVarSlice(streamID, varID, levelID, conversionBuffer, nmiss);
    Free(conversionBuffer);
    }
}


void streamWriteVarChunk(int streamID, int varID,
                        const int rect[][2], const double *data, size_t nmiss)
{
void (*myCdiStreamWriteVarChunk_)(int streamID, int varID, int memtype,
                                    const int rect[][2], const void *data, size_t nmiss)
    = (void (*)(int, int, int, const int [][2], const void *, size_t))
    namespaceSwitchGet(NSSWITCH_STREAM_WRITE_VAR_CHUNK_).func;
myCdiStreamWriteVarChunk_(streamID, varID, MEMTYPE_DOUBLE, rect, data, nmiss);
}


void cdiStreamWriteVarChunk_(int streamID, int varID, int memtype,
                            const int rect[][2], const void *data, size_t nmiss)
{
if ( CDI_Debug ) Message("streamID = %d varID = %d", streamID, varID);

stream_t *streamptr = stream_to_pointer(streamID);



int filetype = streamptr->filetype;

switch (filetype)
    {
#if defined (HAVE_LIBGRIB)
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
#endif
#if defined (HAVE_LIBSERVICE)
    case CDI_FILETYPE_SRV:
#endif
#if defined (HAVE_LIBEXTRA)
    case CDI_FILETYPE_EXT:
#endif
#if defined (HAVE_LIBIEG)
    case CDI_FILETYPE_IEG:
#endif
#if  defined (HAVE_LIBGRIB) || defined (HAVE_LIBSERVICE)      \
|| defined (HAVE_LIBEXTRA) || defined (HAVE_LIBIEG)
    xabort("streamWriteVarChunk not implemented for filetype %s!",
            strfiletype(filetype));
    break;
#endif
#ifdef HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    cdf_write_var_chunk(streamptr, varID, memtype, rect, data, nmiss);
    break;
#endif
    default:
    Error("%s support not compiled in!", strfiletype(filetype));
    break;
    }
}

static
int stream_write_record(int streamID, int memtype, const void *data, size_t nmiss)
{


int status = 0;

check_parg(data);

stream_t *streamptr = stream_to_pointer(streamID);

switch (streamptr->filetype)
    {
#if  defined  (HAVE_LIBGRIB)
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
    grb_write_record(streamptr, memtype, data, nmiss);
    break;
#endif
#ifdef HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    if ( memtype == MEMTYPE_FLOAT ) return 1;
    srvWriteRecord(streamptr, (const double *)data);
    break;
#endif
#ifdef HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    if ( memtype == MEMTYPE_FLOAT ) return 1;
    extWriteRecord(streamptr, (const double *)data);
    break;
#endif
#ifdef HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    if ( memtype == MEMTYPE_FLOAT ) return 1;
    iegWriteRecord(streamptr, (const double *)data);
    break;
#endif
#ifdef HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    {
        cdf_write_record(streamptr, memtype, data, nmiss);
        break;
    }
#endif
    default:
    {
        Error("%s support not compiled in!", strfiletype(streamptr->filetype));
        break;
    }
    }

return status;
}


void streamWriteRecord(int streamID, const double *data, size_t nmiss)
{
stream_write_record(streamID, MEMTYPE_DOUBLE, (const void *) data, nmiss);
}


void streamWriteRecordF(int streamID, const float *data, size_t nmiss)
{
if ( stream_write_record(streamID, MEMTYPE_FLOAT, (const void *) data, nmiss) )
    {


    stream_t *streamptr = stream_to_pointer(streamID);
    int varID = streamptr->record->varID;
    size_t elementCount = gridInqSize(vlistInqVarGrid(streamInqVlist(streamID), varID));
    double *conversionBuffer = (double *) Malloc(elementCount*sizeof(*conversionBuffer));
    for ( size_t i = elementCount; i--; ) conversionBuffer[i] = (double) data[i];
    streamWriteRecord(streamID, conversionBuffer, nmiss);
    Free(conversionBuffer);
    }
}

#ifdef HAVE_CONFIG_H
#endif




static
int cdiStreamReadVar(int streamID, int varID, int memtype, void *data, size_t *nmiss)
{


int status = 0;

if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamID, varID);

check_parg(data);
check_parg(nmiss);

stream_t *streamptr = stream_to_pointer(streamID);
int filetype = streamptr->filetype;

*nmiss = 0;

switch (filetype)
    {
#if  defined  (HAVE_LIBGRIB)
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
    {
        grb_read_var(streamptr, varID, memtype, data, nmiss);
        break;
    }
#endif
#ifdef HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        srvReadVarDP(streamptr, varID, (double *)data, nmiss);
        break;
    }
#endif
#ifdef HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        extReadVarDP(streamptr, varID, (double *)data, nmiss);
        break;
    }
#endif
#ifdef HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        iegReadVarDP(streamptr, varID, (double *)data, nmiss);
        break;
    }
#endif
#ifdef HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    {
        cdf_read_var(streamptr, varID, memtype, data, nmiss);
        break;
    }
#endif
    default:
    {
        Error("%s support not compiled in!", strfiletype(filetype));
        break;
    }
    }

return status;
}


void streamReadVar(int streamID, int varID, double *data, size_t *nmiss)
{
cdiStreamReadVar(streamID, varID, MEMTYPE_DOUBLE, data, nmiss);
}


void streamReadVarF(int streamID, int varID, float *data, size_t *nmiss)
{
if ( cdiStreamReadVar(streamID, varID, MEMTYPE_FLOAT, data, nmiss) )
    {


    size_t elementCount = gridInqSize(vlistInqVarGrid(streamInqVlist(streamID), varID));
    elementCount *= (size_t) zaxisInqSize(vlistInqVarZaxis(streamInqVlist(streamID), varID));
    double *conversionBuffer = (double *) Malloc(elementCount*sizeof(*conversionBuffer));
    streamReadVar(streamID, varID, conversionBuffer, nmiss);
    for ( size_t i = elementCount; i--; ) data[i] = (float) conversionBuffer[i];
    Free(conversionBuffer);
    }
}


static
int cdiStreamReadVarSlice(int streamID, int varID, int levelID, int memtype, void *data, size_t *nmiss)
{


int status = 0;

if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamID, varID);

check_parg(data);
check_parg(nmiss);

stream_t *streamptr = stream_to_pointer(streamID);
int filetype = streamptr->filetype;

*nmiss = 0;

switch (filetype)
    {
#if  defined  (HAVE_LIBGRIB)
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
    {
        grb_read_var_slice(streamptr, varID, levelID, memtype, data, nmiss);
        break;
    }
#endif
#ifdef HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        srvReadVarSliceDP(streamptr, varID, levelID, (double *)data, nmiss);
        break;
    }
#endif
#ifdef HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        extReadVarSliceDP(streamptr, varID, levelID, (double *)data, nmiss);
        break;
    }
#endif
#ifdef HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    {
        if ( memtype == MEMTYPE_FLOAT ) return 1;
        iegReadVarSliceDP(streamptr, varID, levelID, (double *)data, nmiss);
        break;
    }
#endif
#ifdef HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    {
        cdf_read_var_slice(streamptr, varID, levelID, memtype, data, nmiss);
        break;
    }
#endif
    default:
    {
        Error("%s support not compiled in!", strfiletype(filetype));
        status = 2;
        break;
    }
    }

return status;
}


void streamReadVarSlice(int streamID, int varID, int levelID, double *data, size_t *nmiss)
{
if ( cdiStreamReadVarSlice(streamID, varID, levelID, MEMTYPE_DOUBLE, data, nmiss) )
    {
    Warning("Unexpected error returned from cdiStreamReadVarSlice()!");
    size_t elementCount = gridInqSize(vlistInqVarGrid(streamInqVlist(streamID), varID));
    memset(data, 0, elementCount * sizeof(*data));
    }
}


void streamReadVarSliceF(int streamID, int varID, int levelID, float *data, size_t *nmiss)
{
if ( cdiStreamReadVarSlice(streamID, varID, levelID, MEMTYPE_FLOAT, data, nmiss) )
    {


    size_t elementCount = gridInqSize(vlistInqVarGrid(streamInqVlist(streamID), varID));
    double *conversionBuffer = (double *) Malloc(elementCount*sizeof(*conversionBuffer));
    streamReadVarSlice(streamID, varID, levelID, conversionBuffer, nmiss);
    for ( size_t i = elementCount; i--; ) data[i] = (float) conversionBuffer[i];
    Free(conversionBuffer);
    }
}

static
int stream_read_record(int streamID, int memtype, void *data, size_t *nmiss)
{


int status = 0;

check_parg(data);
check_parg(nmiss);

stream_t *streamptr = stream_to_pointer(streamID);

*nmiss = 0;

switch (streamptr->filetype)
    {
#if  defined  (HAVE_LIBGRIB)
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
    grb_read_record(streamptr, memtype, data, nmiss);
    break;
#endif
#ifdef HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    if ( memtype == MEMTYPE_FLOAT ) return 1;
    srvReadRecord(streamptr, (double *)data, nmiss);
    break;
#endif
#ifdef HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    if ( memtype == MEMTYPE_FLOAT ) return 1;
    extReadRecord(streamptr, (double *)data, nmiss);
    break;
#endif
#ifdef HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    if ( memtype == MEMTYPE_FLOAT ) return 1;
    iegReadRecord(streamptr, (double *)data, nmiss);
    break;
#endif
#ifdef HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    cdf_read_record(streamptr, memtype, data, nmiss);
    break;
#endif
    default:
    {
        Error("%s support not compiled in!", strfiletype(streamptr->filetype));
        break;
    }
    }

return status;
}


void streamReadRecord(int streamID, double *data, size_t *nmiss)
{
stream_read_record(streamID, MEMTYPE_DOUBLE, (void *) data, nmiss);
}


void streamReadRecordF(int streamID, float *data, size_t *nmiss)
{
if ( stream_read_record(streamID, MEMTYPE_FLOAT, (void *) data, nmiss) )
    {


    stream_t *streamptr = stream_to_pointer(streamID);
    int tsID   = streamptr->curTsID;
    int vrecID = streamptr->tsteps[tsID].curRecID;
    int recID  = streamptr->tsteps[tsID].recIDs[vrecID];
    int varID  = streamptr->tsteps[tsID].records[recID].varID;
    size_t elementCount = gridInqSize(vlistInqVarGrid(streamInqVlist(streamID), varID));
    double *conversionBuffer = (double *) Malloc(elementCount*sizeof(*conversionBuffer));
    streamReadRecord(streamID, conversionBuffer, nmiss);
    for ( size_t i = elementCount; i--; ) data[i] = (float) conversionBuffer[i];
    Free(conversionBuffer);
    }
}
#ifndef  VARSCAN_H
#define  VARSCAN_H

#ifndef  GRID_H
#endif


void varAddRecord(int recID, int param, int gridID, int zaxistype, int lbounds,
                int level1, int level2, int level_sf, int level_unit, int prec,
                int *pvarID, int *plevelID, int tsteptype, int numavg, int ltype1, int ltype2,
                const char *name, const char *stdname, const char *longname, const char *units,
                const var_tile_t *tiles, int *tile_index);

void varDefVCT(size_t vctsize, double *vctptr);
void varDefZAxisReference(int nlev, int nvgrid, unsigned char uuid[CDI_UUID_SIZE]);

int  varDefZaxis(int vlistID, int zaxistype, int nlevels, const double *levels, const char **cvals, size_t clength, bool lbounds,
                const double *levels1, const double *levels2, int vctsize, const double *vct, char *name,
                const char *longname, const char *units, int prec, int mode, int ltype);

void varDefMissval(int varID, double missval);
void varDefCompType(int varID, int comptype);
void varDefCompLevel(int varID, int complevel);
void varDefInst(int varID, int instID);
int  varInqInst(int varID);
void varDefModel(int varID, int modelID);
int  varInqModel(int varID);
void varDefTable(int varID, int tableID);
int  varInqTable(int varID);

void varDefKeyInt(int varID, int key, int value);
void varDefKeyBytes(int varID, int key, const unsigned char *bytes, int length);

void varDefOptGribInt(int varID, int tile_index, long lval, const char *keyword);
void varDefOptGribDbl(int varID, int tile_index, double dval, const char *keyword);
int varOptGribNentries(int varID);

bool zaxisCompare(int zaxisID, int zaxistype, int nlevels, bool lbounds, const double *levels, const char *longname, const char *units, int ltype);

#endif

#ifdef HAVE_CONFIG_H
#endif

#ifdef HAVE_LIBNETCDF

#include <ctype.h>
#include <limits.h>



#define  X_AXIS  1
#define  Y_AXIS  2
#define  Z_AXIS  3
#define  T_AXIS  4

#define  POSITIVE_UP    1
#define  POSITIVE_DOWN  2

typedef struct {
int     dimid;
int     ncvarid;
int     dimtype;
size_t  len;
char    name[CDI_MAX_NAME];
}
ncdim_t;

#define  MAX_COORDVARS  4
#define  MAX_AUXVARS    4

typedef struct {
int      ncid;
int      isvar;
bool     ignore;
bool     isx;
bool     isy;
bool     isc;
bool     islon;
bool     islat;
bool     islev;
bool     istime;
bool     warn;
bool     calendar;
bool     climatology;
bool     lformulaterms;
int      param;
int      code;
int      tabnum;
int      bounds;
int      gridID;
int      zaxisID;
int      timetype;
int      gridtype;
int      zaxistype;
int      xdim;
int      ydim;
int      zdim;
int      xvarid;
int      yvarid;
int      zvarid;
int      cvarids[MAX_COORDVARS];
int      tvarid;
int      psvarid;
int      p0varid;
int      ncoordvars;
int      coordvarids[MAX_COORDVARS];
int      nauxvars;
int      auxvarids[MAX_AUXVARS];
int      cellarea;
int      tableID;
int      truncation;
int      position;
bool     defmissval;
bool     deffillval;
int      xtype;
int      gmapid;
int      positive;
int      ndims;
int      dimids[8];
int      dimtype[8];
size_t   chunks[8];
int      chunked;
int      chunktype;
int      natts;
int      deflate;
bool     lunsigned;
bool     lvalidrange;
int     *atts;
size_t   vctsize;
double  *vct;
double   missval;
double   fillval;
double   addoffset;
double   scalefactor;
double   validrange[2];
char     name[CDI_MAX_NAME];
char     longname[CDI_MAX_NAME];
char     stdname[CDI_MAX_NAME];
char     units[CDI_MAX_NAME];
char     extra[CDI_MAX_NAME];
int      typeOfEnsembleForecast;
int      numberOfForecastsInEnsemble;
int      perturbationNumber;
}
ncvar_t;


static
void scanTimeString(const char *ptu, int64_t *rdate, int *rtime)
{
int year = 1, month = 1, day = 1;
int hour = 0, minute = 0, second = 0;
int v1 = 1, v2 = 1, v3 = 1;

*rdate = 0;
*rtime = 0;

if ( *ptu )
    {
    v1 = atoi(ptu);
    if ( v1 < 0 ) ptu++;
    while ( isdigit((int) *ptu) ) ptu++;
    if ( *ptu )
        {
        v2 = atoi(++ptu);
        while ( isdigit((int) *ptu) ) ptu++;
        if ( *ptu )
            {
            v3 = atoi(++ptu);
            while ( isdigit((int) *ptu) ) ptu++;
            }
        }
    }

if ( v3 > 999 && v1 < 32 )
    { year = v3; month = v2; day = v1; }
else
    { year = v1; month = v2; day = v3; }

while ( isspace((int) *ptu) ) ptu++;

if ( *ptu )
    {
    while ( ! isdigit((int) *ptu) ) ptu++;

    hour = atoi(ptu);
    while ( isdigit((int) *ptu) ) ptu++;
    if ( *ptu == ':' )
        {
        ptu++;
        minute = atoi(ptu);
        while ( isdigit((int) *ptu) ) ptu++;
        if ( *ptu == ':' )
            {
            ptu++;
            second = atoi(ptu);
            }
        }
    }

*rdate = cdiEncodeDate(year, month, day);
*rtime = cdiEncodeTime(hour, minute, second);
}

static
int scanTimeUnit(const char *unitstr)
{
size_t len = strlen(unitstr);
int timeunit = get_timeunit(len, unitstr);
if ( timeunit == -1 )
    Message("Unsupported TIMEUNIT: %s!", unitstr);

return timeunit;
}

static
void setForecastTime(const char *timestr, taxis_t *taxis)
{
size_t len = strlen(timestr);
if ( len != 0 )
    scanTimeString(timestr, &taxis->fdate, &taxis->ftime);
else
    taxis->fdate = taxis->ftime = 0;
}

static
int setBaseTime(const char *timeunits, taxis_t *taxis)
{
int taxistype = TAXIS_ABSOLUTE;
int64_t rdate = -1;
int rtime = -1;

size_t len = strlen(timeunits);
while ( isspace(*timeunits) && len ) { timeunits++; len--; }

char *restrict tu = (char *)Malloc((len+1) * sizeof(char));

for ( size_t i = 0; i < len; i++ ) tu[i] = (char)tolower((int)timeunits[i]);
tu[len] = 0;

int timeunit = get_timeunit(len, tu);
if ( timeunit == -1 )
    {
    Message("Unsupported TIMEUNIT: %s!", timeunits);
    return 1;
    }

size_t pos = 0;
while ( pos < len && !isspace(tu[pos]) ) ++pos;
if ( tu[pos] )
    {
    while ( isspace(tu[pos]) ) ++pos;

    if ( str_is_equal(tu+pos, "since") )
        taxistype = TAXIS_RELATIVE;

    while ( pos < len && !isspace(tu[pos]) ) ++pos;
    if ( tu[pos] )
        {
        while ( isspace(tu[pos]) ) ++pos;

        if ( taxistype == TAXIS_ABSOLUTE )
            {
            if ( timeunit == TUNIT_DAY )
                {
                if ( !str_is_equal(tu+pos, "%y%m%d.%f") )
                    {
                    Message("Unsupported format %s for TIMEUNIT day!", tu+pos);
                    timeunit = -1;
                    }
                }
            else if ( timeunit == TUNIT_MONTH )
                {
                if ( !str_is_equal(tu+pos, "%y%m.%f") )
                    {
                    Message("Unsupported format %s for TIMEUNIT month!", tu+pos);
                    timeunit = -1;
                    }
                }
            }
        else if ( taxistype == TAXIS_RELATIVE )
            {
            scanTimeString(tu+pos, &rdate, &rtime);

            taxis->rdate = rdate;
            taxis->rtime = rtime;

            if ( CDI_Debug )
                Message("rdate = %lld  rtime = %d", rdate, rtime);
            }
        }
    }

taxis->type = taxistype;
taxis->unit = timeunit;

Free(tu);

if ( CDI_Debug )
    Message("taxistype = %d  unit = %d", taxistype, timeunit);

return 0;
}

static
bool xtypeIsText(int xtype)
{
bool isText = ( xtype == NC_CHAR )
#ifdef HAVE_NETCDF4
    || ( xtype == NC_STRING )
#endif
    ;
return isText;
}

static
bool xtypeIsFloat(nc_type xtype)
{
bool isFloat = xtype == NC_FLOAT || xtype == NC_DOUBLE;

return isFloat;
}

static
bool xtypeIsInt(nc_type xtype)
{
bool isInt = xtype == NC_SHORT || xtype == NC_INT
            || xtype == NC_BYTE
#ifdef HAVE_NETCDF4
            || xtype == NC_USHORT || xtype == NC_UINT
            || xtype == NC_UBYTE
#endif
            ;

return isInt;
}

static
int cdfInqDatatype(int xtype, bool lunsigned)
{
int datatype = -1;

#ifdef HAVE_NETCDF4
if ( xtype == NC_BYTE && lunsigned ) xtype = NC_UBYTE;
#endif

if      ( xtype == NC_BYTE   )  datatype = CDI_DATATYPE_INT8;
else if ( xtype == NC_CHAR   )  datatype = CDI_DATATYPE_UINT8;
else if ( xtype == NC_SHORT  )  datatype = CDI_DATATYPE_INT16;
else if ( xtype == NC_INT    )  datatype = CDI_DATATYPE_INT32;
else if ( xtype == NC_FLOAT  )  datatype = CDI_DATATYPE_FLT32;
else if ( xtype == NC_DOUBLE )  datatype = CDI_DATATYPE_FLT64;
#ifdef HAVE_NETCDF4
else if ( xtype == NC_UBYTE  )  datatype = CDI_DATATYPE_UINT8;
else if ( xtype == NC_LONG   )  datatype = CDI_DATATYPE_INT32;
else if ( xtype == NC_USHORT )  datatype = CDI_DATATYPE_UINT16;
else if ( xtype == NC_UINT   )  datatype = CDI_DATATYPE_UINT32;
else if ( xtype == NC_INT64  )  datatype = CDI_DATATYPE_FLT64;
else if ( xtype == NC_UINT64 )  datatype = CDI_DATATYPE_FLT64;
#endif

return datatype;
}

static
void cdfGetAttInt(int fileID, int ncvarid, const char *attname, size_t attlen, int *attint)
{
*attint = 0;

nc_type atttype;
size_t nc_attlen;
cdf_inq_atttype(fileID, ncvarid, attname, &atttype);
cdf_inq_attlen(fileID, ncvarid, attname, &nc_attlen);

if ( xtypeIsFloat(atttype) || xtypeIsInt(atttype) )
    {
    bool lalloc = nc_attlen > attlen;
    int *pintatt = lalloc ? (int *)(Malloc(nc_attlen*sizeof(int))) : attint;
    cdf_get_att_int(fileID, ncvarid, attname, pintatt);
    if ( lalloc )
        {
        memcpy(attint, pintatt, attlen*sizeof(int));
        Free(pintatt);
        }
    }
}

static
void cdfGetAttDouble(int fileID, int ncvarid, char *attname, size_t attlen, double *attdouble)
{
*attdouble = 0;

nc_type atttype;
size_t nc_attlen;
cdf_inq_atttype(fileID, ncvarid, attname, &atttype);
cdf_inq_attlen(fileID, ncvarid, attname, &nc_attlen);

if ( xtypeIsFloat(atttype) || xtypeIsInt(atttype) )
    {
    bool lalloc = nc_attlen > attlen;
    double *pdoubleatt = lalloc ? (double*)Malloc(nc_attlen*sizeof(double)) : attdouble;
    cdf_get_att_double(fileID, ncvarid, attname, pdoubleatt);
    if ( lalloc )
        {
        memcpy(attdouble, pdoubleatt, attlen*sizeof(double));
        Free(pdoubleatt);
        }
    }
}

static
bool cdfCheckAttText(int fileID, int ncvarid, const char *attname)
{
bool status = false;
nc_type atttype;

int status_nc = nc_inq_atttype(fileID, ncvarid, attname, &atttype);

if ( status_nc == NC_NOERR
    && (atttype == NC_CHAR
#ifdef HAVE_NETCDF4
        || atttype == NC_STRING
#endif
        ) )
    {
    status = true;
    }

return status;
}

static
void cdfGetAttText(int fileID, int ncvarid, const char *attname, size_t attlen, char *atttext)
{
nc_type atttype;
size_t nc_attlen;

cdf_inq_atttype(fileID, ncvarid, attname, &atttype);
cdf_inq_attlen(fileID, ncvarid, attname, &nc_attlen);

if ( atttype == NC_CHAR )
    {
    char attbuf[65636];
    if ( nc_attlen < sizeof(attbuf) )
        {
        cdf_get_att_text(fileID, ncvarid, attname, attbuf);

        if ( nc_attlen > (attlen-1) ) nc_attlen = (attlen-1);

        attbuf[nc_attlen++] = 0;
        memcpy(atttext, attbuf, nc_attlen);
        }
    else
        {
        atttext[0] = 0;
        }
    }
#ifdef HAVE_NETCDF4
else if ( atttype == NC_STRING )
    {
    if ( nc_attlen == 1 )
        {
        char *attbuf = NULL;
        cdf_get_att_string(fileID, ncvarid, attname, &attbuf);

        size_t ssize = strlen(attbuf) + 1;

        if ( ssize > attlen ) ssize = attlen;
        memcpy(atttext, attbuf, ssize);
        atttext[ssize - 1] = 0;
        Free(attbuf);
        }
    else
        {
        atttext[0] = 0;
        }
    }
#endif
}


void cdf_scale_add(size_t size, double *data, double addoffset, double scalefactor)
{
bool laddoffset   = IS_NOT_EQUAL(addoffset, 0);
bool lscalefactor = IS_NOT_EQUAL(scalefactor, 1);

if ( laddoffset && lscalefactor )
    {
    for (size_t i = 0; i < size; ++i )
        data[i] = data[i] * scalefactor + addoffset;
    }
else if (lscalefactor)
    {
    for (size_t i = 0; i < size; ++i )
        data[i] *= scalefactor;
    }
else if (laddoffset)
    {
    for (size_t i = 0; i < size; ++i )
        data[i] += addoffset;
    }
}

static
void cdfCreateRecords(stream_t *streamptr, int tsID)
{
if ( tsID < 0 || (tsID >= streamptr->ntsteps && tsID > 0) ) return;

if ( streamptr->tsteps[tsID].nallrecs > 0 ) return;

int vlistID  = streamptr->vlistID;

tsteps_t* sourceTstep = streamptr->tsteps;
tsteps_t* destTstep = sourceTstep + tsID;

int nvars = vlistNvars(vlistID);
int nrecs = vlistNrecs(vlistID);

if ( nrecs <= 0 ) return;

if ( tsID == 0 )
    {
    int nvrecs = nrecs;

    streamptr->nrecs += nrecs;

    destTstep->records    = (record_t *) Malloc((size_t)nrecs*sizeof(record_t));
    destTstep->nrecs      = nrecs;
    destTstep->nallrecs   = nrecs;
    destTstep->recordSize = nrecs;
    destTstep->curRecID   = CDI_UNDEFID;
    destTstep->recIDs     = (int *) Malloc((size_t)nvrecs*sizeof (int));;
    for ( int recID = 0; recID < nvrecs; recID++ ) destTstep->recIDs[recID] = recID;

    record_t *records = destTstep->records;

    for ( int varID = 0, recID = 0; varID < nvars; varID++ )
        {
        int zaxisID = vlistInqVarZaxis(vlistID, varID);
        int nlev    = zaxisInqSize(zaxisID);
        for ( int levelID = 0; levelID < nlev; levelID++ )
            {
            recordInitEntry(&records[recID]);
            records[recID].varID   = (short)varID;
            records[recID].levelID = (short)levelID;
            recID++;
            }
        }
    }
else if ( tsID == 1 )
    {
    int nvrecs = 0;
    for ( int varID = 0; varID < nvars; varID++ )
        {
        if ( vlistInqVarTimetype(vlistID, varID) != TIME_CONSTANT )
            {
            int zaxisID = vlistInqVarZaxis(vlistID, varID);
            nvrecs += zaxisInqSize(zaxisID);
            }
        }

    streamptr->nrecs += nvrecs;

    destTstep->records    = (record_t *) Malloc((size_t)nrecs*sizeof(record_t));
    destTstep->nrecs      = nvrecs;
    destTstep->nallrecs   = nrecs;
    destTstep->recordSize = nrecs;
    destTstep->curRecID   = CDI_UNDEFID;

    memcpy(destTstep->records, sourceTstep->records, (size_t)nrecs*sizeof(record_t));

    if ( nvrecs )
        {
        destTstep->recIDs = (int *) Malloc((size_t)nvrecs * sizeof (int));
        for ( int recID = 0, vrecID = 0; recID < nrecs; recID++ )
            {
            int varID = destTstep->records[recID].varID;
            if ( vlistInqVarTimetype(vlistID, varID) != TIME_CONSTANT )
                {
                destTstep->recIDs[vrecID++] = recID;
                }
            }
        }
    }
else
    {
    if ( streamptr->tsteps[1].records == 0 ) cdfCreateRecords(streamptr, 1);

    int nvrecs = streamptr->tsteps[1].nrecs;

    streamptr->nrecs += nvrecs;

    destTstep->records    = (record_t *) Malloc((size_t)nrecs*sizeof(record_t));
    destTstep->nrecs      = nvrecs;
    destTstep->nallrecs   = nrecs;
    destTstep->recordSize = nrecs;
    destTstep->curRecID   = CDI_UNDEFID;

    memcpy(destTstep->records, sourceTstep->records, (size_t)nrecs*sizeof(record_t));

    destTstep->recIDs     = (int *) Malloc((size_t)nvrecs * sizeof(int));

    memcpy(destTstep->recIDs, streamptr->tsteps[1].recIDs, (size_t)nvrecs*sizeof(int));
    }
}

static
int cdf_time_dimid(int fileID, int ndims, int nvars, ncdim_t *ncdims)
{
char dimname[CDI_MAX_NAME];

for ( int dimid = 0; dimid < ndims; ++dimid )
    {
    dimname[0] = 0;
    cdf_inq_dimname(fileID, ncdims[dimid].dimid, dimname);
    if ( str_is_equal(dimname, "time") || str_is_equal(dimname, "Time") ) return dimid;
    }

for ( int varid = 0; varid < nvars; ++varid )
    {
    nc_type xtype;
    int nvdims, nvatts, dimids[9];
    cdf_inq_var(fileID, varid, NULL, &xtype, &nvdims, dimids, &nvatts);
    for ( int i = 0; i < nvdims; ++i )
        for ( int dimid = 0; dimid < ndims; ++dimid )
        if ( ncdims[dimid].dimid == dimids[i] )
            {
            dimids[i] = dimid;
            break;
            }

    if ( nvdims == 1 )
        {
        char sbuf[CDI_MAX_NAME];
        for ( int iatt = 0; iatt < nvatts; ++iatt )
            {
            sbuf[0] = 0;
            cdf_inq_attname(fileID, varid, iatt, sbuf);
            if ( strncmp(sbuf, "units", 5) == 0 )
                {
                cdfGetAttText(fileID, varid, "units", sizeof(sbuf), sbuf);
                str_tolower(sbuf);

                if ( is_time_units(sbuf) ) return dimids[0];
                }
            }
        }
    }

return CDI_UNDEFID;
}

static
void init_ncdims(long ndims, ncdim_t *ncdims)
{
for ( long ncdimid = 0; ncdimid < ndims; ncdimid++ )
    {
    ncdims[ncdimid].dimid        = CDI_UNDEFID;
    ncdims[ncdimid].ncvarid      = CDI_UNDEFID;
    ncdims[ncdimid].dimtype      = CDI_UNDEFID;
    ncdims[ncdimid].len          = 0;
    ncdims[ncdimid].name[0]      = 0;
    }
}

static
void init_ncvars(long nvars, ncvar_t *ncvars)
{
for ( long ncvarid = 0; ncvarid < nvars; ++ncvarid )
    {
    ncvars[ncvarid].ncid            = CDI_UNDEFID;
    ncvars[ncvarid].isvar           = CDI_UNDEFID;
    ncvars[ncvarid].ignore          = false;
    ncvars[ncvarid].isx             = false;
    ncvars[ncvarid].isy             = false;
    ncvars[ncvarid].isc             = false;
    ncvars[ncvarid].islon           = false;
    ncvars[ncvarid].islat           = false;
    ncvars[ncvarid].islev           = false;
    ncvars[ncvarid].istime          = false;
    ncvars[ncvarid].warn            = false;
    ncvars[ncvarid].calendar        = false;
    ncvars[ncvarid].climatology     = false;
    ncvars[ncvarid].lformulaterms   = false;
    ncvars[ncvarid].timetype        = TIME_CONSTANT;
    ncvars[ncvarid].param           = CDI_UNDEFID;
    ncvars[ncvarid].code            = CDI_UNDEFID;
    ncvars[ncvarid].tabnum          = 0;
    ncvars[ncvarid].bounds          = CDI_UNDEFID;
    ncvars[ncvarid].gridID          = CDI_UNDEFID;
    ncvars[ncvarid].zaxisID         = CDI_UNDEFID;
    ncvars[ncvarid].gridtype        = CDI_UNDEFID;
    ncvars[ncvarid].zaxistype       = CDI_UNDEFID;
    ncvars[ncvarid].xdim            = CDI_UNDEFID;
    ncvars[ncvarid].ydim            = CDI_UNDEFID;
    ncvars[ncvarid].zdim            = CDI_UNDEFID;
    ncvars[ncvarid].xvarid          = CDI_UNDEFID;
    ncvars[ncvarid].yvarid          = CDI_UNDEFID;
    ncvars[ncvarid].zvarid          = CDI_UNDEFID;
    ncvars[ncvarid].tvarid          = CDI_UNDEFID;
    ncvars[ncvarid].psvarid         = CDI_UNDEFID;
    ncvars[ncvarid].p0varid         = CDI_UNDEFID;
    ncvars[ncvarid].ncoordvars      = 0;
    for ( int i = 0; i < MAX_COORDVARS; ++i )
        {
        ncvars[ncvarid].coordvarids[i]  = CDI_UNDEFID;
        ncvars[ncvarid].cvarids[i]      = CDI_UNDEFID;
        }
    ncvars[ncvarid].nauxvars      = 0;
    for ( int i = 0; i < MAX_AUXVARS; ++i )
        ncvars[ncvarid].auxvarids[i]  = CDI_UNDEFID;
    ncvars[ncvarid].cellarea        = CDI_UNDEFID;
    ncvars[ncvarid].tableID         = CDI_UNDEFID;
    ncvars[ncvarid].xtype           = 0;
    ncvars[ncvarid].ndims           = 0;
    ncvars[ncvarid].gmapid          = CDI_UNDEFID;
    ncvars[ncvarid].vctsize         = 0;
    ncvars[ncvarid].vct             = NULL;
    ncvars[ncvarid].truncation      = 0;
    ncvars[ncvarid].position        = 0;
    ncvars[ncvarid].positive        = 0;
    ncvars[ncvarid].chunked         = 0;
    ncvars[ncvarid].chunktype       = CDI_UNDEFID;
    ncvars[ncvarid].defmissval      = false;
    ncvars[ncvarid].deffillval      = false;
    ncvars[ncvarid].missval         = 0;
    ncvars[ncvarid].fillval         = 0;
    ncvars[ncvarid].addoffset       = 0;
    ncvars[ncvarid].scalefactor     = 1;
    ncvars[ncvarid].natts           = 0;
    ncvars[ncvarid].atts            = NULL;
    ncvars[ncvarid].deflate         = 0;
    ncvars[ncvarid].lunsigned       = false;
    ncvars[ncvarid].lvalidrange     = false;
    ncvars[ncvarid].validrange[0]   = VALIDMISS;
    ncvars[ncvarid].validrange[1]   = VALIDMISS;
    ncvars[ncvarid].typeOfEnsembleForecast      = -1;
    ncvars[ncvarid].numberOfForecastsInEnsemble = -1;
    ncvars[ncvarid].perturbationNumber          = -1;
    memset(ncvars[ncvarid].name, 0, CDI_MAX_NAME);
    memset(ncvars[ncvarid].longname, 0, CDI_MAX_NAME);
    memset(ncvars[ncvarid].stdname, 0, CDI_MAX_NAME);
    memset(ncvars[ncvarid].units, 0, CDI_MAX_NAME);
    memset(ncvars[ncvarid].extra, 0, CDI_MAX_NAME);
    }
}

static
void cdf_set_var(ncvar_t *ncvars, int ncvarid, short isvar)
{
if ( ncvars[ncvarid].isvar != CDI_UNDEFID &&
    ncvars[ncvarid].isvar != isvar   &&
    ncvars[ncvarid].warn  == false )
    {
    if ( ! ncvars[ncvarid].ignore )
        Warning("Inconsistent variable definition for %s!", ncvars[ncvarid].name);

    ncvars[ncvarid].warn = true;
    isvar = FALSE;
    }

ncvars[ncvarid].isvar = isvar;
}

static
void cdf_set_dim(ncvar_t *ncvars, int ncvarid, int dimid, int dimtype)
{
if ( ncvars[ncvarid].dimtype[dimid] != CDI_UNDEFID &&
    ncvars[ncvarid].dimtype[dimid] != dimtype )
    {
    Warning("Inconsistent dimension definition for %s! dimid = %d;  type = %d;  newtype = %d",
            ncvars[ncvarid].name, dimid, ncvars[ncvarid].dimtype[dimid], dimtype);
    }

ncvars[ncvarid].dimtype[dimid] = dimtype;
}

static
void scan_hybrid_formulaterms(int ncid, int ncfvarid, int *avarid, int *bvarid, int *psvarid, int *p0varid)
{
*avarid  = -1;
*bvarid  = -1;
*psvarid = -1;
*p0varid = -1;

char attstring[1024];
cdfGetAttText(ncid, ncfvarid, "formula_terms", sizeof(attstring), attstring);
char *pstring = attstring;

bool lstop = false;
for ( int i = 0; i < 4; i++ )
    {
    while ( isspace((int) *pstring) ) pstring++;
    if ( *pstring == 0 ) break;
    char *tagname = pstring;
    while ( !isspace((int) *pstring) && *pstring != 0 ) pstring++;
    if ( *pstring == 0 ) lstop = true;
    *pstring++ = 0;

    while ( isspace((int) *pstring) ) pstring++;
    if ( *pstring == 0 ) break;
    char *varname = pstring;
    while ( !isspace((int) *pstring) && *pstring != 0 ) pstring++;
    if ( *pstring == 0 ) lstop = true;
    *pstring++ = 0;

    int dimvarid;
    int status_nc = nc_inq_varid(ncid, varname, &dimvarid);
    if ( status_nc == NC_NOERR )
        {
        if      ( strcmp(tagname, "ap:") == 0 ) *avarid  = dimvarid;
        else if ( strcmp(tagname, "a:")  == 0 ) *avarid  = dimvarid;
        else if ( strcmp(tagname, "b:")  == 0 ) *bvarid  = dimvarid;
        else if ( strcmp(tagname, "ps:") == 0 ) *psvarid = dimvarid;
        else if ( strcmp(tagname, "p0:") == 0 ) *p0varid = dimvarid;
        }
    else if ( strcmp(tagname, "ps:") != 0 )
        {
        Warning("%s - %s", nc_strerror(status_nc), varname);
        }

    if ( lstop ) break;
    }
}

static
void readVCT(int ncid, int ndims2, size_t dimlen, size_t dimlen2, int avarid2, int bvarid2, double *vct)
{
double *abuf = (double*) Malloc(dimlen*2*sizeof(double));
double *bbuf = (double*) Malloc(dimlen*2*sizeof(double));
cdf_get_var_double(ncid, avarid2, abuf);
cdf_get_var_double(ncid, bvarid2, bbuf);

if ( ndims2 == 2 )
    {
    for ( size_t i = 0; i < dimlen; ++i )
        {
        vct[i] = abuf[i*2];
        vct[i+dimlen+1] = bbuf[i*2];
        }
    vct[dimlen]     = abuf[dimlen*2-1];
    vct[dimlen*2+1] = bbuf[dimlen*2-1];
    }
else
    {
    for ( size_t i = 0; i < dimlen2; ++i )
        {
        vct[i] = abuf[i];
        vct[i+dimlen+1] = bbuf[i];
        }
    }

Free(abuf);
Free(bbuf);
}

static
bool isHybridSigmaPressureCoordinate(int ncid, int ncvarid, ncvar_t *ncvars, const ncdim_t *ncdims)
{
bool status = false;
ncvar_t *ncvar = &ncvars[ncvarid];

if ( strcmp(ncvar->stdname, "atmosphere_hybrid_sigma_pressure_coordinate") == 0 )
    {
    CDI_convention = CDI_CONVENTION_CF;

    status = true;
    ncvar->zaxistype = ZAXIS_HYBRID;

    int dimid = ncvar->dimids[0];
    size_t dimlen = ncdims[dimid].len;
    int avarid1 = -1, bvarid1 = -1, psvarid1 = -1, p0varid1 = -1;
    int ncfvarid = ncvarid;
    if ( ncvars[ncfvarid].lformulaterms )
        scan_hybrid_formulaterms(ncid, ncfvarid, &avarid1, &bvarid1, &psvarid1, &p0varid1);

    if ( avarid1  != -1 ) ncvars[avarid1].isvar = FALSE;
    if ( bvarid1  != -1 ) ncvars[bvarid1].isvar = FALSE;
    if ( psvarid1 != -1 ) ncvar->psvarid = psvarid1;
    if ( p0varid1 != -1 ) ncvar->p0varid = p0varid1;

    if ( ncvar->bounds != CDI_UNDEFID && ncvars[ncvar->bounds].lformulaterms )
        {
        ncfvarid = ncvar->bounds;
        int avarid2 = -1, bvarid2 = -1, psvarid2 = -1, p0varid2 = -1;
        if ( ncvars[ncfvarid].lformulaterms )
            scan_hybrid_formulaterms(ncid, ncfvarid, &avarid2, &bvarid2, &psvarid2, &p0varid2);

        if ( avarid2 != -1 && bvarid2 != -1 )
            {
            ncvars[avarid2].isvar = FALSE;
            ncvars[bvarid2].isvar = FALSE;

            int ndims2 = ncvars[avarid2].ndims;
            int dimid2 = ncvars[avarid2].dimids[0];
            size_t dimlen2 = ncdims[dimid2].len;

            if ( (ndims2 == 2 && dimid == ncvars[avarid2].dimids[0] ) ||
                (ndims2 == 1 && dimlen == dimlen2-1 ) )
                {
                double px = 1;
                if ( p0varid1 != -1 && p0varid1 == p0varid2 )
                    cdf_get_var_double(ncid, p0varid2, &px);

                size_t vctsize = (dimlen+1)*2;
                double *vct = (double *) Malloc(vctsize*sizeof(double));

                readVCT(ncid, ndims2, dimlen, dimlen2, avarid2, bvarid2, vct);

                if ( p0varid1 != -1 && IS_NOT_EQUAL(px, 1) )
                    for ( size_t i = 0; i < dimlen+1; ++i ) vct[i] *= px;

                ncvar->vct = vct;
                ncvar->vctsize = vctsize;
                }
            }
        }
    }

return status;
}

static
void cdf_set_cdi_attr(int ncid, int ncvarid, int attnum, int cdiID, int varID)
{
nc_type atttype;
size_t attlen;
char attname[CDI_MAX_NAME];

cdf_inq_attname(ncid, ncvarid, attnum, attname);
cdf_inq_attlen(ncid, ncvarid, attname, &attlen);
cdf_inq_atttype(ncid, ncvarid, attname, &atttype);
if ( xtypeIsInt(atttype) )
    {
    int attint;
    int *pattint = attlen > 1 ? (int*) malloc(attlen*sizeof(int)) : &attint;
    cdfGetAttInt(ncid, ncvarid, attname, attlen, pattint);
    int datatype = (atttype == NC_SHORT)  ? CDI_DATATYPE_INT16 :
                    (atttype == NC_BYTE)   ? CDI_DATATYPE_INT8 :
#ifdef HAVE_NETCDF4
                    (atttype == NC_UBYTE)  ? CDI_DATATYPE_UINT8 :
                    (atttype == NC_USHORT) ? CDI_DATATYPE_UINT16 :
                    (atttype == NC_UINT)   ? CDI_DATATYPE_UINT32 :
#endif
                    CDI_DATATYPE_INT32;
    cdiDefAttInt(cdiID, varID, attname, datatype, (int)attlen, pattint);
    if (attlen > 1) free(pattint);
    }
else if ( xtypeIsFloat(atttype) )
    {
    double attflt;
    double *pattflt = attlen > 1 ? (double*) malloc(attlen*sizeof(double)) : &attflt;
    cdfGetAttDouble(ncid, ncvarid, attname, attlen, pattflt);
    int datatype = (atttype == NC_FLOAT) ? CDI_DATATYPE_FLT32 : CDI_DATATYPE_FLT64;
    cdiDefAttFlt(cdiID, varID, attname, datatype, (int)attlen, pattflt);
    if (attlen > 1) free(pattflt);
    }
else if ( xtypeIsText(atttype) )
    {
    char attstring[8192];
    cdfGetAttText(ncid, ncvarid, attname, sizeof(attstring), attstring);
    cdiDefAttTxt(cdiID, varID, attname, (int)attlen, attstring);
    }
}

static
void cdf_print_vars(const ncvar_t *ncvars, int nvars, const char *oname)
{
char axis[7];
static const char iaxis[] = {'t', 'z', 'y', 'x'};

fprintf(stderr, "%s:\n", oname);

for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    int ndim = 0;
    if ( ncvars[ncvarid].isvar )
        {
        axis[ndim++] = 'v';
        axis[ndim++] = ':';
        for ( int i = 0; i < ncvars[ncvarid].ndims; i++ )
            {
            if      ( ncvars[ncvarid].dimtype[i] == T_AXIS ) axis[ndim++] = iaxis[0];
            else if ( ncvars[ncvarid].dimtype[i] == Z_AXIS ) axis[ndim++] = iaxis[1];
            else if ( ncvars[ncvarid].dimtype[i] == Y_AXIS ) axis[ndim++] = iaxis[2];
            else if ( ncvars[ncvarid].dimtype[i] == X_AXIS ) axis[ndim++] = iaxis[3];
            else                                             axis[ndim++] = '?';
            }
        }
    else
        {
        axis[ndim++] = 'c';
        axis[ndim++] = ':';
        if      ( ncvars[ncvarid].istime ) axis[ndim++] = iaxis[0];
        else if ( ncvars[ncvarid].islev  ) axis[ndim++] = iaxis[1];
        else if ( ncvars[ncvarid].islat  ) axis[ndim++] = iaxis[2];
        else if ( ncvars[ncvarid].isy    ) axis[ndim++] = iaxis[2];
        else if ( ncvars[ncvarid].islon  ) axis[ndim++] = iaxis[3];
        else if ( ncvars[ncvarid].isx    ) axis[ndim++] = iaxis[3];
        else                               axis[ndim++] = '?';
        }

    axis[ndim++] = 0;

    fprintf(stderr, "%3d %3d  %-6s %s\n", ncvarid, ndim-3, axis, ncvars[ncvarid].name);
    }
}

static
void cdf_scan_attr_axis(ncvar_t *ncvars, ncdim_t *ncdims, int ncvarid, const char *attstring, size_t attlen,
                        int nvdims, int *dimidsp, const char *name)
{
int i;
for ( i = 0; i < (int)attlen; ++i )
    {
    if ( attstring[i] != '-' && attstring[i] != 't' && attstring[i] != 'z' &&
        attstring[i] != 'y' && attstring[i] != 'x' )
        {
        Warning("Unexpected character in axis attribute for %s, ignored!", name);
        break;
        }
    }

if ( i == (int) attlen && (int) attlen == nvdims )
    {
    while ( attlen-- )
        {
        if ( (int) attstring[attlen] == 't' )
            {
            if ( attlen != 0 ) Warning("axis attribute 't' not on first position");
            cdf_set_dim(ncvars, ncvarid, (int)attlen, T_AXIS);
            }
        else if ( (int) attstring[attlen] == 'z' )
            {
            ncvars[ncvarid].zdim = dimidsp[attlen];
            cdf_set_dim(ncvars, ncvarid, (int)attlen, Z_AXIS);

            if ( ncvars[ncvarid].ndims == 1 )
                {
                cdf_set_var(ncvars, ncvarid, FALSE);
                ncdims[ncvars[ncvarid].dimids[0]].dimtype = Z_AXIS;
                }
            }
        else if ( (int) attstring[attlen] == 'y' )
            {
            ncvars[ncvarid].ydim = dimidsp[attlen];
            cdf_set_dim(ncvars, ncvarid, (int)attlen, Y_AXIS);

            if ( ncvars[ncvarid].ndims == 1 )
                {
                cdf_set_var(ncvars, ncvarid, FALSE);
                ncdims[ncvars[ncvarid].dimids[0]].dimtype = Y_AXIS;
                }
            }
        else if ( (int) attstring[attlen] == 'x' )
            {
            ncvars[ncvarid].xdim = dimidsp[attlen];
            cdf_set_dim(ncvars, ncvarid, (int)attlen, X_AXIS);

            if ( ncvars[ncvarid].ndims == 1 )
                {
                cdf_set_var(ncvars, ncvarid, FALSE);
                ncdims[ncvars[ncvarid].dimids[0]].dimtype = X_AXIS;
                }
            }
        }
    }
}

static
int cdf_get_cell_varid(char *attstring, int ncid)
{
int nc_cell_id = CDI_UNDEFID;

char *pstring = attstring;
while ( isspace((int) *pstring) ) pstring++;
char *cell_measures = pstring;
while ( isalnum((int) *pstring) ) pstring++;
*pstring++ = 0;
while ( isspace((int) *pstring) ) pstring++;
char *cell_var = pstring;
while ( ! isspace((int) *pstring) && *pstring != 0 ) pstring++;
*pstring++ = 0;

if ( str_is_equal(cell_measures, "area") )
    {
    int nc_var_id;
    int status = nc_inq_varid(ncid, cell_var, &nc_var_id);
    if ( status == NC_NOERR )
        nc_cell_id = nc_var_id;

    }

return nc_cell_id;
}

static
void cdf_scan_var_attr(int nvars, ncvar_t *ncvars, int ndims, ncdim_t *ncdims, int timedimid, int modelID, int format)
{
int ncdimid;
int nvdims, nvatts;
int iatt;
nc_type xtype, atttype;
size_t attlen;
char name[CDI_MAX_NAME];
char attname[CDI_MAX_NAME];
char attstring[8192];

int nchecked_vars = 0;
enum { max_check_vars = 9 };
char *checked_vars[max_check_vars];
for ( int i = 0; i < max_check_vars; ++i ) checked_vars[i] = NULL;

for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    int ncid    = ncvars[ncvarid].ncid;
    int *dimidsp = ncvars[ncvarid].dimids;

    cdf_inq_var(ncid, ncvarid, name, &xtype, &nvdims, dimidsp, &nvatts);
    for ( int i = 0; i < nvdims; ++i )
        for ( int dimid = 0; dimid < ndims; ++dimid )
        if ( ncdims[dimid].dimid == dimidsp[i] )
            {
            dimidsp[i] = dimid;
            break;
            }
    strcpy(ncvars[ncvarid].name, name);

    for ( ncdimid = 0; ncdimid < nvdims; ncdimid++ )
        ncvars[ncvarid].dimtype[ncdimid] = -1;

    ncvars[ncvarid].xtype = xtype;
    ncvars[ncvarid].ndims = nvdims;

#ifdef HAVE_NETCDF4
    if ( format == NC_FORMAT_NETCDF4_CLASSIC || format == NC_FORMAT_NETCDF4 )
        {
        int shuffle, deflate, deflate_level;
        size_t *chunks = malloc(nvdims * sizeof(size_t));
        //size_t chunks[nvdims];
        int storage_in;
        nc_inq_var_deflate(ncid, ncvarid, &shuffle, &deflate, &deflate_level);
        if ( deflate > 0 ) ncvars[ncvarid].deflate = 1;

        if ( nc_inq_var_chunking(ncid, ncvarid, &storage_in, chunks) == NC_NOERR )
            {
            if ( storage_in == NC_CHUNKED )
                {
                ncvars[ncvarid].chunked = 1;
                for ( int i = 0; i < nvdims; ++i ) ncvars[ncvarid].chunks[i] = chunks[i];
                if ( CDI_Debug )
                    {
                    fprintf(stderr, "%s: chunking %d %d %d  chunks ", name, storage_in, NC_CONTIGUOUS, NC_CHUNKED);
                    for ( int i = 0; i < nvdims; ++i ) fprintf(stderr, "%zu ", chunks[i]);
                    fprintf(stderr, "\n");
                    }
                {
                    char *buf = ncvars[ncvarid].extra;
                    size_t pos = strlen(buf);
                    static const char prefix[] = "chunks=";
                    memcpy(buf + pos, prefix, sizeof (prefix));
                    pos += sizeof (prefix) - 1;
                    for ( int i = nvdims-1; i >= 0; --i )
                    {
                        pos += (size_t)(sprintf(buf + pos, "%zu%s", chunks[i], i > 0 ? "x" : ""));
                    }
                    buf[pos] = ' '; buf[pos + 1] = 0;
                }
                }
            }
        }
#endif

    if ( nvdims > 0 )
        {
        if ( timedimid == dimidsp[0] )
            {
            ncvars[ncvarid].timetype = TIME_VARYING;
            cdf_set_dim(ncvars, ncvarid, 0, T_AXIS);
            }
        else
            {
            for ( ncdimid = 1; ncdimid < nvdims; ncdimid++ )
                {
                if ( timedimid == dimidsp[ncdimid] )
                    {
                    Warning("Time must be the first dimension! Unsupported array structure, skipped variable %s!", ncvars[ncvarid].name);
                    ncvars[ncvarid].isvar = FALSE;
                    }
                }
            }
        }

    for ( iatt = 0; iatt < nvatts; iatt++ )
        {
        int nc_cell_id = CDI_UNDEFID;

        cdf_inq_attname(ncid, ncvarid, iatt, attname);
        cdf_inq_atttype(ncid, ncvarid, attname, &atttype);
        cdf_inq_attlen(ncid, ncvarid, attname, &attlen);

        size_t attstringsize = sizeof(attstring);
        bool isText = xtypeIsText(atttype);
        bool isNumber = xtypeIsFloat(atttype) || xtypeIsInt(atttype);
        if ( isText )
            {
            cdfGetAttText(ncid, ncvarid, attname, sizeof(attstring), attstring);
            attstringsize = strlen(attstring) + 1;
            if ( attstringsize > CDI_MAX_NAME ) attstringsize = CDI_MAX_NAME;
            }

        if ( isText && strcmp(attname, "long_name") == 0 )
            {
            memcpy(ncvars[ncvarid].longname, attstring, attstringsize);
            }
        else if ( isText && strcmp(attname, "standard_name") == 0 )
            {
            memcpy(ncvars[ncvarid].stdname, attstring, attstringsize);
            }
        else if ( isText && strcmp(attname, "units") == 0 )
            {
            memcpy(ncvars[ncvarid].units, attstring, attstringsize);
            }
        else if ( strcmp(attname, "calendar") == 0 )
            {
            ncvars[ncvarid].calendar = true;
            }
        else if ( isText && strcmp(attname, "param") == 0 )
            {
            int pnum = 0, pcat = 255, pdis = 255;
            sscanf(attstring, "%d.%d.%d", &pnum, &pcat, &pdis);
            ncvars[ncvarid].param = cdiEncodeParam(pnum, pcat, pdis);
            cdf_set_var(ncvars, ncvarid, TRUE);
            }
        else if ( isNumber && strcmp(attname, "code") == 0 )
            {
            cdfGetAttInt(ncid, ncvarid, attname, 1, &ncvars[ncvarid].code);
            cdf_set_var(ncvars, ncvarid, TRUE);
            }
        else if ( isNumber && strcmp(attname, "table") == 0 )
            {
            int tablenum;
            cdfGetAttInt(ncid, ncvarid, attname, 1, &tablenum);
            if ( tablenum > 0 )
                {
                ncvars[ncvarid].tabnum = tablenum;
                ncvars[ncvarid].tableID = tableInq(modelID, tablenum, NULL);
                if ( ncvars[ncvarid].tableID == CDI_UNDEFID )
                    ncvars[ncvarid].tableID = tableDef(modelID, tablenum, NULL);
                }
            cdf_set_var(ncvars, ncvarid, TRUE);
            }
        else if ( isText && strcmp(attname, "trunc_type") == 0 )
            {
            if ( str_is_equal(attstring, "Triangular") )
                ncvars[ncvarid].gridtype = GRID_SPECTRAL;
            }
        else if ( isText && (strcmp(attname, "grid_type") == 0 || strcmp(attname, "CDI_grid_type") == 0) )
            {
            str_tolower(attstring);
            set_gridtype(attstring, &ncvars[ncvarid].gridtype);
            cdf_set_var(ncvars, ncvarid, TRUE);
            }
        else if ( isText && strcmp(attname, "level_type") == 0 )
            {
            str_tolower(attstring);
            set_zaxistype(attstring, &ncvars[ncvarid].zaxistype);
            cdf_set_var(ncvars, ncvarid, TRUE);
            }
        else if ( isNumber && strcmp(attname, "trunc_count") == 0 )
            {
            cdfGetAttInt(ncid, ncvarid, attname, 1, &ncvars[ncvarid].truncation);
            }
        else if ( isNumber && strcmp(attname, "truncation") == 0 )
            {
            cdfGetAttInt(ncid, ncvarid, attname, 1, &ncvars[ncvarid].truncation);
            }
        else if ( isNumber && strcmp(attname, "number_of_grid_in_reference") == 0 )
            {
            cdfGetAttInt(ncid, ncvarid, attname, 1, &ncvars[ncvarid].position);
            }
        else if ( isNumber && strcmp(attname, "add_offset") == 0 )
            {
            cdfGetAttDouble(ncid, ncvarid, attname, 1, &ncvars[ncvarid].addoffset);


            }
        else if ( isNumber && strcmp(attname, "scale_factor") == 0 )
            {
            cdfGetAttDouble(ncid, ncvarid, attname, 1, &ncvars[ncvarid].scalefactor);


            }
        else if ( isText && strcmp(attname, "climatology") == 0 )
            {
            int ncboundsid;
            int status = nc_inq_varid(ncid, attstring, &ncboundsid);
            if ( status == NC_NOERR )
                {
                ncvars[ncvarid].climatology = true;
                ncvars[ncvarid].bounds = ncboundsid;
                cdf_set_var(ncvars, ncvars[ncvarid].bounds, FALSE);
                cdf_set_var(ncvars, ncvarid, FALSE);
                }
            else
                Warning("%s - %s", nc_strerror(status), attstring);
            }
        else if ( isText && strcmp(attname, "bounds") == 0 )
            {
            int ncboundsid;
            int status = nc_inq_varid(ncid, attstring, &ncboundsid);
            if ( status == NC_NOERR )
                {
                ncvars[ncvarid].bounds = ncboundsid;
                cdf_set_var(ncvars, ncvars[ncvarid].bounds, FALSE);
                cdf_set_var(ncvars, ncvarid, FALSE);
                }
            else
                Warning("%s - %s", nc_strerror(status), attstring);
            }
        else if ( isText &&  strcmp(attname, "formula_terms") == 0 )
            {
            ncvars[ncvarid].lformulaterms = true;
            }
        else if ( isText && strcmp(attname, "cell_measures") == 0 && (nc_cell_id=cdf_get_cell_varid(attstring, ncid)) != CDI_UNDEFID )
            {
            ncvars[ncvarid].cellarea = nc_cell_id;
            ncvars[nc_cell_id].isvar = FALSE;
            cdf_set_var(ncvars, ncvarid, TRUE);
            }

        else if ( isText && (strcmp(attname, "associate")  == 0 || strcmp(attname, "coordinates") == 0) )
            {
            bool lstop = false;
            char *pstring = attstring;

            for ( int i = 0; i < MAX_COORDVARS; i++ )
                {
                while ( isspace((int) *pstring) ) pstring++;
                if ( *pstring == 0 ) break;
                char *varname = pstring;
                while ( !isspace((int) *pstring) && *pstring != 0 ) pstring++;
                if ( *pstring == 0 ) lstop = true;
                if ( *(pstring-1) == ',' ) *(pstring-1) = 0;
                *pstring++ = 0;

                int dimvarid;
                int status = nc_inq_varid(ncid, varname, &dimvarid);
                if ( status == NC_NOERR )
                    {
                    cdf_set_var(ncvars, dimvarid, FALSE);
                    if ( !cdiIgnoreAttCoordinates )
                        {
                        ncvars[ncvarid].coordvarids[i] = dimvarid;
                        ncvars[ncvarid].ncoordvars++;
                        }
                    }
                else
                    {
                    if ( !cdiIgnoreAttCoordinates ) ncvars[ncvarid].ncoordvars++;

                    int k;
                    for ( k = 0; k < nchecked_vars; ++k )
                        if ( strcmp(checked_vars[k], varname) == 0 ) break;

                    if ( k == nchecked_vars )
                        {
                        if ( nchecked_vars < max_check_vars ) checked_vars[nchecked_vars++] = strdup(varname);
                        Warning("%s - >%s<", nc_strerror(status), varname);
                        }
                    }

                if ( lstop ) break;
                }

            cdf_set_var(ncvars, ncvarid, TRUE);
            }
        else if ( isText && strcmp(attname, "auxiliary_variable") == 0 )
            {
            bool lstop = false;
            char *pstring = attstring;

            for ( int i = 0; i < MAX_AUXVARS; i++ )
                {
                while ( isspace((int) *pstring) ) pstring++;
                if ( *pstring == 0 ) break;
                char *varname = pstring;
                while ( !isspace((int) *pstring) && *pstring != 0 ) pstring++;
                if ( *pstring == 0 ) lstop = true;
                *pstring++ = 0;

                int dimvarid;
                int status = nc_inq_varid(ncid, varname, &dimvarid);
                if ( status == NC_NOERR )
                    {
                    cdf_set_var(ncvars, dimvarid, FALSE);

                        {
                        ncvars[ncvarid].auxvarids[i] = dimvarid;
                        ncvars[ncvarid].nauxvars++;
                        }
                    }
                else
                    Warning("%s - %s", nc_strerror(status), varname);

                if ( lstop ) break;
                }

            cdf_set_var(ncvars, ncvarid, TRUE);
            }
        else if ( isText && strcmp(attname, "grid_mapping") == 0 )
            {
            int nc_gmap_id;
            int status = nc_inq_varid(ncid, attstring, &nc_gmap_id);
            if ( status == NC_NOERR )
                {
                ncvars[ncvarid].gmapid = nc_gmap_id;
                cdf_set_var(ncvars, ncvars[ncvarid].gmapid, FALSE);
                }
            else
                Warning("%s - %s", nc_strerror(status), attstring);

            cdf_set_var(ncvars, ncvarid, TRUE);
            }
        else if ( isText && strcmp(attname, "positive") == 0 )
            {
            str_tolower(attstring);

            if      ( str_is_equal(attstring, "down") ) ncvars[ncvarid].positive = POSITIVE_DOWN;
            else if ( str_is_equal(attstring, "up")   ) ncvars[ncvarid].positive = POSITIVE_UP;

            if ( ncvars[ncvarid].ndims == 1 )
                {
                cdf_set_var(ncvars, ncvarid, FALSE);
                cdf_set_dim(ncvars, ncvarid, 0, Z_AXIS);
                ncdims[ncvars[ncvarid].dimids[0]].dimtype = Z_AXIS;
                }
            if ( ncvars[ncvarid].ndims == 0 )
                {
                cdf_set_var(ncvars, ncvarid, FALSE);
                ncvars[ncvarid].islev = true;
                }
            }
        else if ( isNumber && strcmp(attname, "_FillValue") == 0 )
            {
            cdfGetAttDouble(ncid, ncvarid, attname, 1, &ncvars[ncvarid].fillval);
            ncvars[ncvarid].deffillval = true;

            }
        else if ( isNumber && strcmp(attname, "missing_value") == 0 )
            {
            cdfGetAttDouble(ncid, ncvarid, attname, 1, &ncvars[ncvarid].missval);
            ncvars[ncvarid].defmissval = true;

            }
        else if ( isNumber && strcmp(attname, "valid_range") == 0 && attlen == 2 )
            {
            if ( ncvars[ncvarid].lvalidrange == false )
                {
                bool lignore = xtypeIsFloat(atttype) != xtypeIsFloat(xtype);
                if ( !cdiIgnoreValidRange && lignore == false )
                    {
                    cdfGetAttDouble(ncid, ncvarid, attname, 2, ncvars[ncvarid].validrange);
                    ncvars[ncvarid].lvalidrange = true;
                    if ( ((int)ncvars[ncvarid].validrange[0]) == 0 && ((int)ncvars[ncvarid].validrange[1]) == 255 )
                        ncvars[ncvarid].lunsigned = true;

                    }
                else if ( lignore )
                    {
                    Warning("Inconsistent data type for attribute %s:valid_range, ignored!", name);
                    }
                }
            }
        else if ( isNumber && strcmp(attname, "valid_min") == 0 && attlen == 1 )
            {
            bool lignore = xtypeIsFloat(atttype) != xtypeIsFloat(xtype);
            if ( !cdiIgnoreValidRange && lignore == false )
                {
                cdfGetAttDouble(ncid, ncvarid, attname, 1, &(ncvars[ncvarid].validrange)[0]);
                ncvars[ncvarid].lvalidrange = true;
                }
            else if ( lignore )
                {
                Warning("Inconsistent data type for attribute %s:valid_min, ignored!", name);
                }
            }
        else if ( isNumber && strcmp(attname, "valid_max") == 0 && attlen == 1 )
            {
            bool lignore = xtypeIsFloat(atttype) != xtypeIsFloat(xtype);
            if ( !cdiIgnoreValidRange && lignore == false )
                {
                cdfGetAttDouble(ncid, ncvarid, attname, 1, &(ncvars[ncvarid].validrange)[1]);
                ncvars[ncvarid].lvalidrange = true;
                }
            else if ( lignore )
                {
                Warning("Inconsistent data type for attribute %s:valid_max, ignored!", name);
                }
            }
        else if ( isText && strcmp(attname, "_Unsigned") == 0 )
            {
            str_tolower(attstring);

            if ( str_is_equal(attstring, "true") )
                {
                ncvars[ncvarid].lunsigned = true;

                }

            }
        else if ( isText && strcmp(attname, "cdi") == 0 )
            {
            str_tolower(attstring);

            if ( str_is_equal(attstring, "ignore") )
                {
                ncvars[ncvarid].ignore = true;
                cdf_set_var(ncvars, ncvarid, FALSE);
                }
            }
        else if ( isText && strcmp(attname, "axis") == 0 )
            {
            attlen = strlen(attstring);

            if ( (int) attlen > nvdims && nvdims > 0 && attlen > 1 )
                {
                    Warning("Unexpected axis attribute length for %s, ignored!", name);
                }
            else if ( nvdims == 0 && attlen == 1 )
                {
                if ( attstring[0] == 'z' || attstring[0] == 'Z' )
                    {
                    cdf_set_var(ncvars, ncvarid, FALSE);
                    ncvars[ncvarid].islev = true;
                    }
                }
            else
                {
                str_tolower(attstring);
                cdf_scan_attr_axis(ncvars, ncdims, ncvarid, attstring, attlen, nvdims, dimidsp, name);
                }
            }
        else if ( isNumber &&
                    (strcmp(attname, "realization") == 0       ||
                    strcmp(attname, "ensemble_members") == 0  ||
                    strcmp(attname, "forecast_init_type") == 0) )
            {
            int temp;
            cdfGetAttInt(ncid, ncvarid, attname, 1, &temp);

            if      ( strcmp(attname, "realization") == 0 )  ncvars[ncvarid].perturbationNumber = temp;
            else if ( strcmp(attname, "ensemble_members") == 0 ) ncvars[ncvarid].numberOfForecastsInEnsemble = temp;
            else if ( strcmp(attname, "forecast_init_type") == 0 ) ncvars[ncvarid].typeOfEnsembleForecast = temp;

            cdf_set_var(ncvars, ncvarid, TRUE);
            }
        else
            {
            if ( ncvars[ncvarid].natts == 0 )
                ncvars[ncvarid].atts = (int*) Malloc((size_t)nvatts*sizeof(int));

            ncvars[ncvarid].atts[ncvars[ncvarid].natts++] = iatt;

            }
        }
    }

for ( int i = 0; i < max_check_vars; ++i ) if ( checked_vars[i] ) Free(checked_vars[i]);
}

static
void cdf_set_dimtype(int nvars, ncvar_t *ncvars, ncdim_t *ncdims)
{
for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    if ( ncvars[ncvarid].isvar == TRUE )
        {
        int ndims = ncvars[ncvarid].ndims;
        for ( int i = 0; i < ndims; i++ )
            {
            int ncdimid = ncvars[ncvarid].dimids[i];
            int dimtype = ncdims[ncdimid].dimtype;
            if ( dimtype >= X_AXIS && dimtype <= T_AXIS )
                cdf_set_dim(ncvars, ncvarid, i, dimtype);
            }

        if ( CDI_Debug )
            {
            Message("var %d %s", ncvarid, ncvars[ncvarid].name);
            for ( int i = 0; i < ndims; i++ )
                printf("  dim%d type=%d  ", i, ncvars[ncvarid].dimtype[i]);
            printf("\n");
            }
        }
    }

for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    if ( ncvars[ncvarid].isvar == TRUE )
        {
        bool lxdim = false, lydim = false, lzdim = false ;
        int lcdim = 0;
        int ndims = ncvars[ncvarid].ndims;
        for ( int i = 0; i < ndims; i++ )
            {
            int dimtype = ncvars[ncvarid].dimtype[i];
            lxdim = lxdim | (dimtype == X_AXIS);
            lydim = lydim | (dimtype == Y_AXIS);
            lzdim = lzdim | (dimtype == Z_AXIS);
            if ( ncvars[ncvarid].cvarids[i] != CDI_UNDEFID ) lcdim++;

            }

        int allcdims = lcdim;

        if ( !lxdim && ncvars[ncvarid].xvarid != CDI_UNDEFID )
            {
            if ( ncvars[ncvars[ncvarid].xvarid].ndims == 0 ) lxdim = true;
            }

        if ( !lydim && ncvars[ncvarid].yvarid != CDI_UNDEFID )
            {
            if ( ncvars[ncvars[ncvarid].yvarid].ndims == 0 ) lydim = true;
            }

        if ( lxdim && (lydim || ncvars[ncvarid].gridtype == GRID_UNSTRUCTURED) )
            for ( int i = ndims-1; i >= 0; i-- )
            {
                if ( ncvars[ncvarid].dimtype[i] == -1 )
                {
                    if ( !lzdim )
                    {
                        if ( lcdim )
                        {
                            int cdimvar = ncvars[ncvarid].cvarids[allcdims-lcdim];
                            ncvars[ncvarid].zvarid = cdimvar;
                            lcdim--;
                            ncvars[cdimvar].zaxistype = ZAXIS_CHAR;
                        }
                        cdf_set_dim(ncvars, ncvarid, i, Z_AXIS);
                        lzdim = true;
                        int ncdimid = ncvars[ncvarid].dimids[i];
                        if ( ncdims[ncdimid].dimtype == CDI_UNDEFID )
                        ncdims[ncdimid].dimtype = Z_AXIS;
                    }
                }
            }
        }
    }

for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    int ndims = ncvars[ncvarid].ndims;
    for ( int i = 0; i < ndims; i++ )
        {
        if ( ncvars[ncvarid].dimtype[i] == CDI_UNDEFID )
            {
            int ncdimid = ncvars[ncvarid].dimids[i];
            if ( ncdims[ncdimid].dimtype == Z_AXIS )
                {
                ncvars[ncvarid].islev = true;
                cdf_set_dim(ncvars, ncvarid, i, Z_AXIS);
                }
            }
        }
    }

for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    if ( ncvars[ncvarid].isvar == TRUE )
        {
        bool lxdim = false, lydim = false, lzdim = false ;
        int lcdim = 0;
        int ndims = ncvars[ncvarid].ndims;
        for ( int i = 0; i < ndims; i++ )
            {
            if      ( ncvars[ncvarid].dimtype[i] == X_AXIS ) lxdim = true;
            else if ( ncvars[ncvarid].dimtype[i] == Y_AXIS ) lydim = true;
            else if ( ncvars[ncvarid].dimtype[i] == Z_AXIS ) lzdim = true;
            else if ( ncvars[ncvarid].cvarids[i] != CDI_UNDEFID ) lcdim++;

            }

        int allcdims = lcdim;

        if ( !lxdim && ncvars[ncvarid].xvarid != CDI_UNDEFID )
            {
            if ( ncvars[ncvars[ncvarid].xvarid].ndims == 0 ) lxdim = true;
            }

        if ( !lydim && ncvars[ncvarid].yvarid != CDI_UNDEFID )
            {
            if ( ncvars[ncvars[ncvarid].yvarid].ndims == 0 ) lydim = true;
            }


            for ( int i = ndims-1; i >= 0; i-- )
            {
                if ( ncvars[ncvarid].dimtype[i] == -1 )
                {
                    if ( !lxdim )
                    {
                        if ( lcdim && ncvars[ncvarid].xvarid == CDI_UNDEFID )
                        {
                            int cdimvar = ncvars[ncvarid].cvarids[allcdims-lcdim];
                            ncvars[ncvarid].xvarid = cdimvar;
                            lcdim--;
                        }
                        cdf_set_dim(ncvars, ncvarid, i, X_AXIS);
                        lxdim = true;
                    }
                    else if ( !lydim && ncvars[ncvarid].gridtype != GRID_UNSTRUCTURED )
                    {
                        if ( lcdim && ncvars[ncvarid].yvarid == CDI_UNDEFID )
                        {
                            int cdimvar = ncvars[ncvarid].cvarids[allcdims-lcdim];
                            ncvars[ncvarid].yvarid = cdimvar;
                            lcdim--;
                        }
                        cdf_set_dim(ncvars, ncvarid, i, Y_AXIS);
                        lydim = true;
                    }
                    else if ( !lzdim )
                    {
                        if ( lcdim > 0 )
                        {
                            int cdimvar = ncvars[ncvarid].cvarids[allcdims-lcdim];
                            ncvars[ncvarid].zvarid = cdimvar;
                            lcdim--;
                            ncvars[cdimvar].zaxistype = ZAXIS_CHAR;
                        }
                        cdf_set_dim(ncvars, ncvarid, i, Z_AXIS);
                        lzdim = true;
                    }
                }
            }
        }
    }
}


static
void verify_coordinate_vars_1(int ncid, int ndims, ncdim_t *ncdims, ncvar_t *ncvars, int timedimid, bool *lhybrid_cf)
{
for ( int ncdimid = 0; ncdimid < ndims; ncdimid++ )
    {
    int ncvarid = ncdims[ncdimid].ncvarid;
    if ( ncvarid != -1 )
        {
        if ( ncvars[ncvarid].dimids[0] == timedimid )
            {
            ncvars[ncvarid].istime = true;
            ncdims[ncdimid].dimtype = T_AXIS;
            continue;
            }

        if ( isHybridSigmaPressureCoordinate(ncid, ncvarid, ncvars, ncdims) )
            {
            *lhybrid_cf = true;
            continue;
            }

        if ( ncvars[ncvarid].units[0] != 0 )
            {
            if ( is_lon_axis(ncvars[ncvarid].units, ncvars[ncvarid].stdname) )
                {
                ncvars[ncvarid].islon = true;
                cdf_set_var(ncvars, ncvarid, FALSE);
                cdf_set_dim(ncvars, ncvarid, 0, X_AXIS);
                ncdims[ncdimid].dimtype = X_AXIS;
                }
            else if ( is_lat_axis(ncvars[ncvarid].units, ncvars[ncvarid].stdname) )
                {
                ncvars[ncvarid].islat = true;
                cdf_set_var(ncvars, ncvarid, FALSE);
                cdf_set_dim(ncvars, ncvarid, 0, Y_AXIS);
                ncdims[ncdimid].dimtype = Y_AXIS;
                }
            else if ( is_x_axis(ncvars[ncvarid].units, ncvars[ncvarid].stdname) )
                {
                ncvars[ncvarid].isx = true;
                cdf_set_var(ncvars, ncvarid, FALSE);
                cdf_set_dim(ncvars, ncvarid, 0, X_AXIS);
                ncdims[ncdimid].dimtype = X_AXIS;
                }
            else if ( is_y_axis(ncvars[ncvarid].units, ncvars[ncvarid].stdname) )
                {
                ncvars[ncvarid].isy = true;
                cdf_set_var(ncvars, ncvarid, FALSE);
                cdf_set_dim(ncvars, ncvarid, 0, Y_AXIS);
                ncdims[ncdimid].dimtype = Y_AXIS;
                }
            else if ( is_pressure_units(ncvars[ncvarid].units) )
                {
                ncvars[ncvarid].zaxistype = ZAXIS_PRESSURE;
                }
            else if ( strcmp(ncvars[ncvarid].units, "level") == 0 || strcmp(ncvars[ncvarid].units, "1") == 0 )
                {
                if      ( strcmp(ncvars[ncvarid].longname, "hybrid level at layer midpoints") == 0 )
                    ncvars[ncvarid].zaxistype = ZAXIS_HYBRID;
                else if ( strncmp(ncvars[ncvarid].longname, "hybrid level at midpoints", 25) == 0 )
                    ncvars[ncvarid].zaxistype = ZAXIS_HYBRID;
                else if ( strcmp(ncvars[ncvarid].longname, "hybrid level at layer interfaces") == 0 )
                    ncvars[ncvarid].zaxistype = ZAXIS_HYBRID_HALF;
                else if ( strncmp(ncvars[ncvarid].longname, "hybrid level at interfaces", 26) == 0 )
                    ncvars[ncvarid].zaxistype = ZAXIS_HYBRID_HALF;
                else if ( strcmp(ncvars[ncvarid].units, "level") == 0 )
                    ncvars[ncvarid].zaxistype = ZAXIS_GENERIC;
                }
            else if ( is_DBL_axis(ncvars[ncvarid].longname) )
                {
                ncvars[ncvarid].zaxistype = ZAXIS_DEPTH_BELOW_LAND;
                }
            else if ( is_height_units(ncvars[ncvarid].units) )
                {
                if ( is_depth_axis(ncvars[ncvarid].stdname, ncvars[ncvarid].longname) )
                    ncvars[ncvarid].zaxistype = ZAXIS_DEPTH_BELOW_SEA;
                else if ( is_height_axis(ncvars[ncvarid].stdname, ncvars[ncvarid].longname) )
                    ncvars[ncvarid].zaxistype = ZAXIS_HEIGHT;
                }
            }
        else
            {
            if ( (strcmp(ncvars[ncvarid].longname, "generalized_height") == 0 ||
                    strcmp(ncvars[ncvarid].longname, "generalized height") == 0) &&
                strcmp(ncvars[ncvarid].stdname, "height") == 0 )
                ncvars[ncvarid].zaxistype = ZAXIS_REFERENCE;
            else if ( str_is_equal(ncvars[ncvarid].stdname, "air_pressure") )
                ncvars[ncvarid].zaxistype = ZAXIS_PRESSURE;
            }

        if ( !ncvars[ncvarid].islon && ncvars[ncvarid].longname[0] != 0 &&
            !ncvars[ncvarid].islat && ncvars[ncvarid].longname[1] != 0 )
            {
            if ( str_is_equal(ncvars[ncvarid].longname+1, "ongitude") )
                {
                ncvars[ncvarid].islon = true;
                cdf_set_var(ncvars, ncvarid, FALSE);
                cdf_set_dim(ncvars, ncvarid, 0, X_AXIS);
                ncdims[ncdimid].dimtype = X_AXIS;
                continue;
                }
            else if ( str_is_equal(ncvars[ncvarid].longname+1, "atitude") )
                {
                ncvars[ncvarid].islat = true;
                cdf_set_var(ncvars, ncvarid, FALSE);
                cdf_set_dim(ncvars, ncvarid, 0, Y_AXIS);
                ncdims[ncdimid].dimtype = Y_AXIS;
                continue;
                }
            }

        if ( ncvars[ncvarid].zaxistype != CDI_UNDEFID )
            {
            ncvars[ncvarid].islev = true;
            cdf_set_var(ncvars, ncvarid, FALSE);
            cdf_set_dim(ncvars, ncvarid, 0, Z_AXIS);
            ncdims[ncdimid].dimtype = Z_AXIS;
            }
        }
    }
}


static
void verify_coordinate_vars_2(int nvars, ncvar_t *ncvars)
{
for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    if ( ncvars[ncvarid].isvar == 0 )
        {
        if ( ncvars[ncvarid].units[0] != 0 )
            {
            if ( is_lon_axis(ncvars[ncvarid].units, ncvars[ncvarid].stdname) )
                {
                ncvars[ncvarid].islon = true;
                continue;
                }
            else if ( is_lat_axis(ncvars[ncvarid].units, ncvars[ncvarid].stdname) )
                {
                ncvars[ncvarid].islat = true;
                continue;
                }
            else if ( is_x_axis(ncvars[ncvarid].units, ncvars[ncvarid].stdname) )
                {
                ncvars[ncvarid].isx = true;
                continue;
                }
            else if ( is_y_axis(ncvars[ncvarid].units, ncvars[ncvarid].stdname) )
                {
                ncvars[ncvarid].isy = true;
                continue;
                }
            else if ( ncvars[ncvarid].zaxistype == CDI_UNDEFID &&
                        (strcmp(ncvars[ncvarid].units, "level") == 0 || strcmp(ncvars[ncvarid].units, "1") == 0) )
                {
                if      ( strcmp(ncvars[ncvarid].longname, "hybrid level at layer midpoints") == 0 )
                    ncvars[ncvarid].zaxistype = ZAXIS_HYBRID;
                else if ( strncmp(ncvars[ncvarid].longname, "hybrid level at midpoints", 25) == 0 )
                    ncvars[ncvarid].zaxistype = ZAXIS_HYBRID;
                else if ( strcmp(ncvars[ncvarid].longname, "hybrid level at layer interfaces") == 0 )
                    ncvars[ncvarid].zaxistype = ZAXIS_HYBRID_HALF;
                else if ( strncmp(ncvars[ncvarid].longname, "hybrid level at interfaces", 26) == 0 )
                    ncvars[ncvarid].zaxistype = ZAXIS_HYBRID_HALF;
                else if ( strcmp(ncvars[ncvarid].units, "level") == 0 )
                    ncvars[ncvarid].zaxistype = ZAXIS_GENERIC;
                continue;
                }
            else if ( ncvars[ncvarid].zaxistype == CDI_UNDEFID && is_pressure_units(ncvars[ncvarid].units) )
                {
                ncvars[ncvarid].zaxistype = ZAXIS_PRESSURE;
                continue;
                }
            else if ( is_DBL_axis(ncvars[ncvarid].longname) )
                {
                ncvars[ncvarid].zaxistype = ZAXIS_DEPTH_BELOW_LAND;
                continue;
                }
            else if ( is_height_units(ncvars[ncvarid].units) )
                {
                if ( is_depth_axis(ncvars[ncvarid].stdname, ncvars[ncvarid].longname) )
                    ncvars[ncvarid].zaxistype = ZAXIS_DEPTH_BELOW_SEA;
                else if ( is_height_axis(ncvars[ncvarid].stdname, ncvars[ncvarid].longname) )
                    ncvars[ncvarid].zaxistype = ZAXIS_HEIGHT;
                continue;
                }
            }
        else if ( strcmp(ncvars[ncvarid].stdname, "region") == 0  ||
                    strcmp(ncvars[ncvarid].stdname, "area_type") == 0 ||
                    cdfInqDatatype(ncvars[ncvarid].xtype, ncvars[ncvarid].lunsigned) == CDI_DATATYPE_UINT8 )
            {
            ncvars[ncvarid].isc = true;
            }
        else if ( str_is_equal(ncvars[ncvarid].stdname, "air_pressure") )
            ncvars[ncvarid].zaxistype = ZAXIS_PRESSURE;


        if ( !ncvars[ncvarid].islon && ncvars[ncvarid].longname[0] != 0 &&
            !ncvars[ncvarid].islat && ncvars[ncvarid].longname[1] != 0 )
            {
            if ( str_is_equal(ncvars[ncvarid].longname+1, "ongitude") )
                {
                ncvars[ncvarid].islon = true;
                continue;
                }
            else if ( str_is_equal(ncvars[ncvarid].longname+1, "atitude") )
                {
                ncvars[ncvarid].islat = true;
                continue;
                }
            }
        }
    }
}

static
void grid_set_chunktype(grid_t *grid, ncvar_t *ncvar)
{
if ( ncvar->chunked )
    {
    int ndims = ncvar->ndims;

    if ( grid->type == GRID_UNSTRUCTURED )
        {
        ncvar->chunktype = ncvar->chunks[ndims-1] == grid->size
            ? CDI_CHUNK_GRID : CDI_CHUNK_AUTO;
        }
    else
        {
        if ( grid->x.size > 1 && grid->y.size > 1 && ndims > 1 &&
            grid->x.size == ncvar->chunks[ndims-1] &&
            grid->y.size == ncvar->chunks[ndims-2] )
            ncvar->chunktype = CDI_CHUNK_GRID;
        else if ( grid->x.size > 1 && grid->x.size == ncvar->chunks[ndims-1] )
            ncvar->chunktype = CDI_CHUNK_LINES;
        else
            ncvar->chunktype = CDI_CHUNK_AUTO;
        }
    }
}


static
void cdf_load_vals(size_t size, int ndims, int varid, ncvar_t *ncvar, double **gridvals, struct xyValGet *valsGet,
                int ntdims, size_t *start, size_t *count)
{
if ( CDI_netcdf_lazy_grid_load )
    {
    *valsGet = (struct xyValGet){
        .scalefactor = ncvar->scalefactor,
        .addoffset = ncvar->addoffset,
        .start = { start[0], start[1], start[2] },
        .count = { count[0], count[1], count[2] },
        .size = size,
        .datasetNCId = ncvar->ncid,
        .varNCId = varid,
        .ndims = (short)ndims,
    };
    *gridvals = cdfPendingLoad;
    }
else
    {
    *gridvals = (double*) Malloc(size*sizeof(double));
    if ( ntdims == 1 )
        cdf_get_vara_double(ncvar->ncid, varid, start, count, *gridvals);
    else
        cdf_get_var_double(ncvar->ncid, varid, *gridvals);
    cdf_scale_add(size, *gridvals, ncvar->addoffset, ncvar->scalefactor);
    }
}

static
void cdf_load_cvals(size_t size, int varid, ncvar_t *ncvar, char ***gridvals, size_t dimlength)
{
size_t startc[] = {0, 0};
size_t countc[] = {1, size/dimlength};
*gridvals = (char **) Malloc(dimlength * sizeof(char *));
for ( size_t i = 0; i < dimlength; i++ )
    {
    (*gridvals)[i] = (char*) Malloc((size/dimlength) * sizeof(char));
    cdf_get_vara_text(ncvar->ncid, varid, startc, countc, (*gridvals)[i]);
    startc[0] = i+1;
    }
}

static
void cdf_load_bounds(size_t size, ncvar_t *ncvar, double **gridbounds, struct cdfLazyGridIds *cellBoundsGet)
{
if ( CDI_netcdf_lazy_grid_load )
    {
    cellBoundsGet->datasetNCId = ncvar->ncid;
    cellBoundsGet->varNCId  = ncvar->bounds;
    *gridbounds = cdfPendingLoad;
    }
else
    {
    *gridbounds = (double*) Malloc(size*sizeof(double));
    cdf_get_var_double(ncvar->ncid, ncvar->bounds, *gridbounds);
    }
}

static
void cdf_load_cellarea(size_t size, ncvar_t *ncvar, double **gridarea, struct cdfLazyGridIds *cellAreaGet)
{
if ( CDI_netcdf_lazy_grid_load )
    {
    cellAreaGet->datasetNCId = ncvar->ncid;
    cellAreaGet->varNCId = ncvar->cellarea;
    *gridarea = cdfPendingLoad;
    }
else
    {
    *gridarea = (double*) Malloc(size*sizeof(double));
    cdf_get_var_double(ncvar->ncid, ncvar->cellarea, *gridarea);
    }
}

static
void cdf_copy_axis_attr(ncvar_t *ncvar, struct gridaxis_t *gridaxis)
{
strcpy(gridaxis->name, ncvar->name);
strcpy(gridaxis->longname, ncvar->longname);
strcpy(gridaxis->units, ncvar->units);
if ( gridaxis->cvals )
    gridaxis->stdname = ncvar->stdname;
}

static
int cdf_get_xydimid(int ndims, int *dimids, int *dimtype, int *xdimid, int *ydimid)
{
int nxdims = 0, nydims = 0;
int xdimids[2] = {-1,-1}, ydimids[2] = {-1,-1};

for ( int i = 0; i < ndims; i++ )
    {
    if ( dimtype[i] == X_AXIS && nxdims < 2 )
        {
        xdimids[nxdims] = dimids[i];
        nxdims++;
        }
    else if ( dimtype[i] == Y_AXIS && nydims < 2 )
        {
        ydimids[nydims] = dimids[i];
        nydims++;
        }
    }

if ( nxdims == 2 )
    {
    *xdimid = xdimids[1];
    *ydimid = xdimids[0];
    }
else if ( nydims == 2 )
    {
    *xdimid = ydimids[1];
    *ydimid = ydimids[0];
    }
else
    {
    *xdimid = xdimids[0];
    *ydimid = ydimids[0];
    }

return nydims;
}

static
void cdf_check_gridtype(int *gridtype, bool islon, bool islat, size_t xsize, size_t ysize, grid_t *grid)
{
if ( islat && (islon || xsize == 0) )
    {
    double yinc = 0;
    if ( islon && ysize > 1 )
        {
        yinc = fabs(grid->y.vals[0] - grid->y.vals[1]);
        for ( size_t i = 2; i < ysize; i++ )
            if ( (fabs(grid->y.vals[i-1] - grid->y.vals[i]) - yinc) > (yinc/1000) )
            {
                yinc = 0;
                break;
            }
        }
    if ( ysize < 10000 && isGaussGrid(ysize, yinc, grid->y.vals) )
        {
        *gridtype = GRID_GAUSSIAN;
        grid->np = (int)(ysize/2);
        }
    else
        *gridtype = GRID_LONLAT;
    }
else if ( islon && !islat && ysize == 0 )
    {
    *gridtype = GRID_LONLAT;
    }
else
    *gridtype = GRID_GENERIC;
}

static
bool cdf_read_xcoord(struct cdfLazyGrid *restrict lazyGrid, ncdim_t *ncdims, ncvar_t *ncvar, int xvarid, ncvar_t *axisvar,
                    size_t *xsize, size_t ysize, int ntdims, size_t *start, size_t *count, bool *islon)
{
grid_t *grid = &lazyGrid->base;
bool skipvar = true;
*islon = axisvar->islon;
int ndims = axisvar->ndims;
size_t size = 0;
int datatype = cdfInqDatatype(axisvar->xtype, axisvar->lunsigned);

if ( (ndims - ntdims) == 2 )
    {

    int dimid = axisvar->dimids[ndims-2];
    size_t dimsize1 = ncdims[dimid].len;
    dimid = axisvar->dimids[ndims-1];
    size_t dimsize2 = ncdims[dimid].len;

    if ( datatype == CDI_DATATYPE_UINT8 )
        {
        ncvar->gridtype = GRID_CHARXY;
        size = dimsize1*dimsize2;
        skipvar = dimsize1 != *xsize;
        }
    else
        {
        ncvar->gridtype = GRID_CURVILINEAR;
        size = (*xsize)*ysize;
        skipvar = dimsize1*dimsize2 != size;
        }
    }
else if ( (ndims - ntdims) == 1 )
    {
    size = *xsize;

    int dimid = axisvar->dimids[ndims-1];
    size_t dimsize = ncdims[dimid].len;
    skipvar = dimsize != size;
    }
else if ( ndims == 0 && *xsize == 0 )
    {
    size = *xsize = 1;
    skipvar = false;
    }

if ( skipvar )
    {
    Warning("Unsupported array structure, skipped variable %s!", ncvar->name);
    ncvar->isvar = -1;
    return true;
    }

if ( datatype != -1 )  grid->datatype = datatype;

if ( datatype == CDI_DATATYPE_UINT8 && !CDI_netcdf_lazy_grid_load )
    {
    cdf_load_cvals(size, xvarid, axisvar, &grid->x.cvals, *xsize);
    grid->x.clength = size / (*xsize) ;
    }
else
    cdf_load_vals(size, ndims, xvarid, axisvar, &grid->x.vals, &lazyGrid->xValsGet, ntdims, start, count);

cdf_copy_axis_attr(axisvar, &grid->x);

return false;
}

static
bool cdf_read_ycoord(struct cdfLazyGrid *restrict lazyGrid, ncdim_t *ncdims, ncvar_t *ncvar, int yvarid, ncvar_t *axisvar,
                    size_t xsize, size_t *ysize, int ntdims, size_t *start, size_t *count, bool *islat)
{
grid_t *grid = &lazyGrid->base;
bool skipvar = true;
*islat = axisvar->islat;
int ndims = axisvar->ndims;
size_t size = 0;
int datatype = cdfInqDatatype(axisvar->xtype, axisvar->lunsigned);

if ( (ndims - ntdims) == 2 )
    {

    int dimid = axisvar->dimids[ndims-2];
    size_t dimsize1 = ncdims[dimid].len;
    dimid = axisvar->dimids[ndims-1];
    size_t dimsize2 = ncdims[dimid].len;

    if ( datatype == CDI_DATATYPE_UINT8 )
        {
        ncvar->gridtype = GRID_CHARXY;
        size = dimsize1*dimsize2;
        skipvar = dimsize1 != *ysize;
        }
    else
        {
        ncvar->gridtype = GRID_CURVILINEAR;
        size = xsize*(*ysize);
        skipvar = dimsize1*dimsize2 != size;
        }
    }
else if ( (ndims - ntdims) == 1 )
    {
    if ( *ysize == 0 ) size = xsize;
    else               size = *ysize;

    int dimid = axisvar->dimids[ndims-1];
    size_t dimsize = ncdims[dimid].len;
    skipvar = dimsize != size;
    }
else if ( ndims == 0 && *ysize == 0 )
    {
    size = *ysize = 1;
    skipvar = false;
    }

if ( skipvar )
    {
    Warning("Unsupported array structure, skipped variable %s!", ncvar->name);
    ncvar->isvar = -1;
    return true;
    }

if ( datatype != -1 )  grid->datatype = datatype;

if ( datatype == CDI_DATATYPE_UINT8 && !CDI_netcdf_lazy_grid_load )
    {
    cdf_load_cvals(size, yvarid, axisvar, &grid->y.cvals, *ysize);
    grid->y.clength = size / (*ysize) ;
    }
else
    cdf_load_vals(size, ndims, yvarid, axisvar, &grid->y.vals, &lazyGrid->yValsGet, ntdims, start, count);

cdf_copy_axis_attr(axisvar, &grid->y);

return false;
}

static
bool cdf_read_coordinates(struct cdfLazyGrid *restrict lazyGrid, ncvar_t *ncvar, ncvar_t *ncvars, ncdim_t *ncdims,
                        int timedimid, int xvarid, int yvarid, size_t xsize, size_t ysize, int *vdimid)
{
grid_t *grid = &lazyGrid->base;
size_t size = 0;

grid->datatype = CDI_DATATYPE_FLT64;

if ( ncvar->gridtype == GRID_TRAJECTORY )
    {
    if ( ncvar->xvarid == CDI_UNDEFID ) Error("Longitude coordinate undefined for %s!", ncvar->name);
    if ( ncvar->yvarid == CDI_UNDEFID ) Error("Latitude coordinate undefined for %s!", ncvar->name);
    }
else
    {
    static bool ltwarn = true;
    size_t start[3], count[3];
    int ntdims = 0;

    if ( xvarid != CDI_UNDEFID && yvarid != CDI_UNDEFID )
        {
        int ndims = ncvars[xvarid].ndims;
        if ( ndims != ncvars[yvarid].ndims && !ncvars[xvarid].isc && !ncvars[yvarid].isc )
            {
            Warning("Inconsistent grid structure for variable %s!", ncvar->name);
            ncvar->xvarid = xvarid = CDI_UNDEFID;
            ncvar->yvarid = yvarid = CDI_UNDEFID;
            }
        if ( ndims > 1 )
            {
            if ( ndims <= 3 )
                {
                if ( ncvars[xvarid].dimids[0] == timedimid && ncvars[yvarid].dimids[0] == timedimid )
                    {
                    size_t ntsteps = 0;
                    cdf_inq_dimlen(ncvar->ncid, ncdims[timedimid].dimid, &ntsteps);
                    if ( ltwarn && ntsteps > 1 ) Warning("Time varying grids unsupported, using grid at time step 1!");
                    ltwarn = false;
                    ntdims = 1;
                    start[0] = start[1] = start[2] = 0;
                    count[0] = 1; count[1] = ysize; count[ndims-1] = xsize;
                    }
                }
            else
                {
                Warning("Unsupported grid structure for variable %s (grid dims > 2)!", ncvar->name);
                ncvar->xvarid = xvarid = CDI_UNDEFID;
                ncvar->yvarid = yvarid = CDI_UNDEFID;
                }
            }
        }

    if ( xvarid != CDI_UNDEFID )
        {
        if ( (ncvars[xvarid].ndims - ntdims) > 2 )
            {
            Warning("Coordinate variable %s has to many dimensions (%d), skipped!", ncvars[xvarid].name, ncvars[xvarid].ndims);

            xvarid = CDI_UNDEFID;
            }
        }

    if ( yvarid != CDI_UNDEFID )
        {
        if ( (ncvars[yvarid].ndims - ntdims) > 2 )
            {
            Warning("Coordinate variable %s has to many dimensions (%d), skipped!", ncvars[yvarid].name, ncvars[yvarid].ndims);

            yvarid = CDI_UNDEFID;
            }
        }

    bool islon = false, islat = false;

    if ( xvarid != CDI_UNDEFID )
        if ( cdf_read_xcoord(lazyGrid, ncdims, ncvar, xvarid, &ncvars[xvarid],
                            &xsize, ysize, ntdims, start, count, &islon) )
        return true;

    if ( yvarid != CDI_UNDEFID )
        if ( cdf_read_ycoord(lazyGrid, ncdims, ncvar, yvarid, &ncvars[yvarid],
                            xsize, &ysize, ntdims, start, count, &islat) )
        return true;

    if      ( ysize == 0 ) size = xsize;
    else if ( xsize == 0 ) size = ysize;
    else if ( ncvar->gridtype == GRID_UNSTRUCTURED ) size = xsize;
    else                         size = xsize*ysize;

    if ( ncvar->gridtype == CDI_UNDEFID || ncvar->gridtype == GRID_GENERIC )
        cdf_check_gridtype(&ncvar->gridtype, islon, islat, xsize, ysize, grid);
    }

int gridtype = grid->type;
if ( gridtype != GRID_PROJECTION ) gridtype = ncvar->gridtype;
else if ( gridtype == GRID_PROJECTION && ncvar->gridtype == GRID_LONLAT )
    {
    int gmapvarid = ncvar->gmapid;
    if ( gmapvarid != CDI_UNDEFID && cdfCheckAttText(ncvar->ncid, gmapvarid, "grid_mapping_name") )
        {
        char attstring[CDI_MAX_NAME];
        cdfGetAttText(ncvar->ncid, gmapvarid, "grid_mapping_name", CDI_MAX_NAME, attstring);
        if ( strcmp(attstring, "latitude_longitude") == 0 ) gridtype = ncvar->gridtype;
        }
    }

switch (gridtype)
    {
    case GRID_GENERIC:
    case GRID_LONLAT:
    case GRID_GAUSSIAN:
    case GRID_UNSTRUCTURED:
    case GRID_CURVILINEAR:
    case GRID_PROJECTION:
    {
        grid->size  = size;
        grid->x.size = xsize;
        grid->y.size = ysize;
        if ( xvarid != CDI_UNDEFID && CDI_read_cell_corners )
        {
            grid->x.flag = 1;
            int bvarid = ncvars[xvarid].bounds;
            if ( bvarid != CDI_UNDEFID )
            {
                int nbdims = ncvars[bvarid].ndims;
                if ( nbdims == 2 || nbdims == 3 )
                {
                    *vdimid = ncvars[bvarid].dimids[nbdims-1];
                    grid->nvertex = (int)ncdims[*vdimid].len;
                    cdf_load_bounds(size*(size_t)grid->nvertex, &ncvars[xvarid], &grid->x.bounds, &lazyGrid->xBoundsGet);
                }
            }
        }
        if ( yvarid != CDI_UNDEFID && CDI_read_cell_corners )
        {
            grid->y.flag = 1;
            int bvarid = ncvars[yvarid].bounds;
            if ( bvarid != CDI_UNDEFID )
            {
                int nbdims = ncvars[bvarid].ndims;
                if ( nbdims == 2 || nbdims == 3 )
                {
                    if ( *vdimid == CDI_UNDEFID )
                    {
                        *vdimid = ncvars[bvarid].dimids[nbdims-1];
                        grid->nvertex = (int)ncdims[*vdimid].len;
                    }
                    cdf_load_bounds(size*(size_t)grid->nvertex, &ncvars[yvarid], &grid->y.bounds, &lazyGrid->yBoundsGet);
                }
            }
        }

        if ( ncvar->cellarea != CDI_UNDEFID )
        cdf_load_cellarea(size, ncvar, &grid->area, &lazyGrid->cellAreaGet);

        break;
    }
    case GRID_SPECTRAL:
    {
        grid->size = size;
        grid->lcomplex = 1;
        grid->trunc = ncvar->truncation;
        break;
    }
    case GRID_FOURIER:
    {
        grid->size = size;
        grid->trunc = ncvar->truncation;
        break;
    }
    case GRID_TRAJECTORY:
    {
        grid->size = 1;
        break;
    }
    case GRID_CHARXY:
    {
        grid->size = size;
        grid->x.size = xsize;
        grid->y.size = ysize;
        break;
    }
    }


if ( grid->type != gridtype )
    {

    grid->type = gridtype;
    cdiGridTypeInit(grid, gridtype, grid->size);
    }

if ( grid->size == 0 )
    {
    int ndims = ncvar->ndims;
    int *dimtype = ncvar->dimtype;
    if ( ndims == 0 ||
        (ndims == 1 && dimtype[0] == T_AXIS) ||
        (ndims == 1 && dimtype[0] == Z_AXIS) ||
        (ndims == 2 && dimtype[0] == T_AXIS && dimtype[1] == Z_AXIS) )
        {
        grid->type  = GRID_GENERIC;
        grid->size  = 1;
        grid->x.size = 0;
        grid->y.size = 0;
        }
    else
        {
        Warning("Unsupported grid, skipped variable %s!", ncvar->name);
        ncvar->isvar = -1;
        return true;
        }
    }

return false;
}

static
bool cdf_set_unstructured_par(ncvar_t *ncvar, grid_t *grid, int *xdimid, int *ydimid, int number_of_grid_used, unsigned char *uuidOfHGrid)
{
int ndims = ncvar->ndims;
int *dimtype = ncvar->dimtype;

int zdimid = CDI_UNDEFID;
int xdimidx = CDI_UNDEFID, ydimidx = CDI_UNDEFID;

for ( int i = 0; i < ndims; i++ )
    {
    if      ( dimtype[i] == X_AXIS ) xdimidx = i;
    else if ( dimtype[i] == Y_AXIS ) ydimidx = i;
    else if ( dimtype[i] == Z_AXIS ) zdimid = ncvar->dimids[i];
    }

if ( *xdimid != CDI_UNDEFID && *ydimid != CDI_UNDEFID && zdimid == CDI_UNDEFID )
    {
    if ( grid->x.size > grid->y.size && grid->y.size < 1000 )
        {
        dimtype[ydimidx] = Z_AXIS;
        *ydimid = CDI_UNDEFID;
        grid->size  = grid->x.size;
        grid->y.size = 0;
        }
    else if ( grid->y.size > grid->x.size && grid->x.size < 1000 )
        {
        dimtype[xdimidx] = Z_AXIS;
        *xdimid = *ydimid;
        *ydimid = CDI_UNDEFID;
        grid->size  = grid->y.size;
        grid->x.size = grid->y.size;
        grid->y.size = 0;
        }
    }

if ( grid->size != grid->x.size )
    {
    Warning("Unsupported array structure, skipped variable %s!", ncvar->name);
    ncvar->isvar = -1;
    return true;
    }

if ( number_of_grid_used != CDI_UNDEFID ) grid->number = number_of_grid_used;
if ( ncvar->position > 0 ) grid->position = ncvar->position;
if ( uuidOfHGrid[0] != 0 ) memcpy(grid->uuid, uuidOfHGrid, CDI_UUID_SIZE);

return false;
}

static
void cdf_read_mapping_atts(int ncid, int gmapvarid, int projID, const char *varname)
{
if ( cdfCheckAttText(ncid, gmapvarid, "grid_mapping_name") )
    {
    char attstring[CDI_MAX_NAME];
    cdfGetAttText(ncid, gmapvarid, "grid_mapping_name", CDI_MAX_NAME, attstring);
    cdiGridDefKeyStr(projID, CDI_KEY_MAPNAME, (int)(strlen(attstring)+1), attstring);
    }
else
    {
    Warning("Text attribute %s:grid_mapping_name missing!", varname);
    }

int nvatts;
cdf_inq_varnatts(ncid, gmapvarid, &nvatts);
for ( int attnum = 0; attnum < nvatts; ++attnum )
    cdf_set_cdi_attr(ncid, gmapvarid, attnum, projID, CDI_GLOBAL);
}

static
void cdf_set_grid_to_similar_vars(ncvar_t *ncvar1, ncvar_t *ncvar2, int gridtype, int xdimid, int ydimid)
{
if ( ncvar2->isvar == TRUE && ncvar2->gridID == CDI_UNDEFID )
    {
    int xdimid2 = CDI_UNDEFID, ydimid2 = CDI_UNDEFID, zdimid2 = CDI_UNDEFID;
    int xdimidx = CDI_UNDEFID, ydimidx = CDI_UNDEFID;
    int ndims2 = ncvar2->ndims;

    int *dimtype2 = ncvar2->dimtype;
    int *dimids2 = ncvar2->dimids;
    for ( int i = 0; i < ndims2; i++ )
        {
        if      ( dimtype2[i] == X_AXIS ) { xdimid2 = dimids2[i]; xdimidx = i; }
        else if ( dimtype2[i] == Y_AXIS ) { ydimid2 = dimids2[i]; ydimidx = i; }
        else if ( dimtype2[i] == Z_AXIS ) { zdimid2 = dimids2[i]; }
        }

    if ( ncvar2->gridtype == CDI_UNDEFID && gridtype == GRID_UNSTRUCTURED )
        {
        if ( xdimid == xdimid2 && ydimid2 != CDI_UNDEFID && zdimid2 == CDI_UNDEFID )
            {
            ncvar2->dimtype[ydimidx] = Z_AXIS;
            ydimid2 = CDI_UNDEFID;
            }

        if ( xdimid == ydimid2 && xdimid2 != CDI_UNDEFID && zdimid2 == CDI_UNDEFID )
            {
            ncvar2->dimtype[xdimidx] = Z_AXIS;
            xdimid2 = ydimid2;
            ydimid2 = CDI_UNDEFID;
            }
        }

    if ( xdimid == xdimid2 && (ydimid == ydimid2 || (xdimid == ydimid && ydimid2 == CDI_UNDEFID)) )
        {
        bool same_grid = ncvar1->xvarid == ncvar2->xvarid
                        && ncvar1->yvarid == ncvar2->yvarid
                        && ncvar1->position == ncvar2->position;


        if ( same_grid )
            {
            if ( CDI_Debug ) Message("Same gridID %d %s", ncvar1->gridID, ncvar2->name);
            ncvar2->gridID = ncvar1->gridID;
            ncvar2->chunktype = ncvar1->chunktype;
            }
        }
    }
}

static
int cdf_define_all_grids(ncgrid_t *ncgrid, int vlistID, ncdim_t *ncdims, int nvars, ncvar_t *ncvars,
                        int timedimid, unsigned char *uuidOfHGrid, char *gridfile, int number_of_grid_used)
{
for ( int ncvarid = 0; ncvarid < nvars; ++ncvarid )
    {
    ncvar_t *ncvar = &ncvars[ncvarid];
    if ( ncvar->isvar && ncvar->gridID == CDI_UNDEFID )
        {
        int ndims = ncvar->ndims;
        int *dimtype = ncvar->dimtype;
        int vdimid = CDI_UNDEFID;
        struct addIfNewRes projAdded = { .Id = CDI_UNDEFID, .isNew = 0 },
                            gridAdded = { .Id = CDI_UNDEFID, .isNew = 0 };
        int xdimid = CDI_UNDEFID, ydimid = CDI_UNDEFID;
        int nydims = cdf_get_xydimid(ndims, ncvar->dimids, dimtype, &xdimid, &ydimid);

        int xaxisid = (xdimid != CDI_UNDEFID) ? ncdims[xdimid].ncvarid : CDI_UNDEFID;
        int yaxisid = (ydimid != CDI_UNDEFID) ? ncdims[ydimid].ncvarid : CDI_UNDEFID;
        int xvarid = (ncvar->xvarid != CDI_UNDEFID) ? ncvar->xvarid : xaxisid;
        int yvarid = (ncvar->yvarid != CDI_UNDEFID) ? ncvar->yvarid : yaxisid;

        size_t xsize = (xdimid != CDI_UNDEFID) ? ncdims[xdimid].len : 0;
        size_t ysize = (ydimid != CDI_UNDEFID) ? ncdims[ydimid].len : 0;

        if ( ydimid == CDI_UNDEFID && yvarid != CDI_UNDEFID )
            {
            if ( ncvars[yvarid].ndims == 1 )
                {
                ydimid = ncvars[yvarid].dimids[0];
                ysize  = ncdims[ydimid].len;
                }
            }

        int gmapvarid = ncvar->gmapid;
        bool lproj = gmapvarid != CDI_UNDEFID;

        if ( !lproj && xaxisid != CDI_UNDEFID && xaxisid != xvarid && yaxisid != CDI_UNDEFID && yaxisid != yvarid )
            {
            lproj = true;
            }

        bool lgrid = !(lproj && ncvar->xvarid == CDI_UNDEFID);

        bool lunstructured = xdimid != CDI_UNDEFID && xdimid == ydimid && nydims == 0;
        if ( (ncvar->gridtype == CDI_UNDEFID || ncvar->gridtype == GRID_GENERIC) && lunstructured )
            ncvar->gridtype = GRID_UNSTRUCTURED;

        struct cdfLazyGrid *restrict lazyGrid = NULL, *restrict lazyProj = NULL;

        {
            int gridtype = !lgrid ? GRID_PROJECTION : ncvar->gridtype;
            if ( CDI_netcdf_lazy_grid_load )
            {
                cdfLazyGridRenew(&lazyGrid, gridtype);
                if ( lgrid && lproj ) cdfLazyGridRenew(&lazyProj, GRID_PROJECTION);
            }
            else
            {
                cdfBaseGridRenew(&lazyGrid, gridtype);
                if ( lgrid && lproj ) cdfBaseGridRenew(&lazyProj, GRID_PROJECTION);
            }
        }
        grid_t *grid = &lazyGrid->base;
        grid_t *proj = ( lgrid && lproj ) ? &lazyProj->base : NULL;

        xaxisid = (xdimid != CDI_UNDEFID) ? ncdims[xdimid].ncvarid : CDI_UNDEFID;
        yaxisid = (ydimid != CDI_UNDEFID) ? ncdims[ydimid].ncvarid : CDI_UNDEFID;


        if ( cdf_read_coordinates(lazyGrid, ncvar, ncvars, ncdims,
                                    timedimid, xvarid, yvarid, xsize, ysize, &vdimid) )
            continue;

        if ( number_of_grid_used != CDI_UNDEFID &&
            (grid->type == CDI_UNDEFID || grid->type == GRID_GENERIC) &&
            xdimid != CDI_UNDEFID && xsize > 999 )
            grid->type = GRID_UNSTRUCTURED;

        if ( grid->type == GRID_UNSTRUCTURED )
            if ( cdf_set_unstructured_par(ncvar, grid, &xdimid, &ydimid, number_of_grid_used, uuidOfHGrid) )
            continue;

        if ( lproj && lgrid )
            {
            int dumid;
            cdf_read_coordinates(lazyProj, ncvar, ncvars, ncdims, timedimid,
                                xaxisid, yaxisid, xsize, ysize, &dumid);
            }

        if ( CDI_Debug )
            {
            Message("grid: type = %d, size = %zu, nx = %zu, ny = %zu",
                    grid->type, grid->size, grid->x.size, grid->y.size);
            if ( proj )
                Message("proj: type = %d, size = %zu, nx = %zu, ny = %zu",
                        proj->type, proj->size, proj->x.size, proj->y.size);
            }


        if ( lgrid && lproj )
            {
            projAdded = cdiVlistAddGridIfNew(vlistID, proj, 2);
            grid->proj = projAdded.Id;
            }

        gridAdded = cdiVlistAddGridIfNew(vlistID, grid, 1);
        ncvar->gridID = gridAdded.Id;

        int gridID = ncvar->gridID;

        if ( lproj && gmapvarid != CDI_UNDEFID )
            {
            int projID = lgrid ? grid->proj : gridID;
            int ncid = ncvars[gmapvarid].ncid;
            const char *gmapname = ncvars[gmapvarid].name;
            cdf_read_mapping_atts(ncid, gmapvarid, projID, gmapname);
            cdiGridDefKeyStr(projID, CDI_KEY_MAPPING, (int)(strlen(gmapname)+1), gmapname);
            gridVerifyProj(projID);
            }

        if ( grid->type == GRID_UNSTRUCTURED && gridfile[0] != 0 )
            gridDefReference(gridID, gridfile);

        if ( ncvar->chunked ) grid_set_chunktype(grid, ncvar);

        int gridindex = vlistGridIndex(vlistID, gridID);
        ncgrid[gridindex].gridID = gridID;
        ncgrid[gridindex].ncIDs[CDF_DIMID_X] = xdimid;
        ncgrid[gridindex].ncIDs[CDF_DIMID_Y] = ydimid;
        if ( grid->type == GRID_TRAJECTORY )
            {
            ncgrid[gridindex].ncIDs[CDF_VARID_X] = xvarid;
            ncgrid[gridindex].ncIDs[CDF_VARID_Y] = yvarid;
            }

        if ( xdimid == CDI_UNDEFID && ydimid == CDI_UNDEFID && grid->size == 1 )
            gridDefHasDims(gridID, FALSE);

        if ( xdimid != CDI_UNDEFID ) cdiGridDefKeyStr(gridID, CDI_KEY_XDIMNAME, (int)(strlen(ncdims[xdimid].name)+1), ncdims[xdimid].name);
        if ( ydimid != CDI_UNDEFID ) cdiGridDefKeyStr(gridID, CDI_KEY_YDIMNAME, (int)(strlen(ncdims[ydimid].name)+1), ncdims[ydimid].name);
        if ( vdimid != CDI_UNDEFID ) cdiGridDefKeyStr(gridID, CDI_KEY_VDIMNAME, (int)(strlen(ncdims[vdimid].name)+1), ncdims[vdimid].name);

        if ( CDI_Debug ) Message("gridID %d %d %s", gridID, ncvarid, ncvar->name);

        for ( int ncvarid2 = ncvarid+1; ncvarid2 < nvars; ncvarid2++ )
            cdf_set_grid_to_similar_vars(ncvar, &ncvars[ncvarid2], grid->type, xdimid, ydimid);

        if ( gridAdded.isNew ) lazyGrid = NULL;
        if ( projAdded.isNew ) lazyProj = NULL;

        if ( lazyGrid )
            {
            if ( CDI_netcdf_lazy_grid_load ) cdfLazyGridDestroy(lazyGrid);
            if ( grid ) { grid_free(grid); Free(grid); }
            }

        if ( lazyProj )
            {
            if ( CDI_netcdf_lazy_grid_load ) cdfLazyGridDestroy(lazyProj);
            if ( proj ) { grid_free(proj); Free(proj); }
            }
        }
    }

return 0;
}


static
int cdf_define_all_zaxes(stream_t *streamptr, int vlistID, ncdim_t *ncdims, int nvars, ncvar_t *ncvars,
                        size_t vctsize_echam, double *vct_echam, unsigned char *uuidOfVGrid)
{
char *pname, *plongname, *punits;
size_t vctsize = vctsize_echam;
double *vct = vct_echam;

for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    ncvar_t *ncvar = &ncvars[ncvarid];
    if ( ncvar->isvar == TRUE && ncvar->zaxisID == CDI_UNDEFID )
        {
        bool is_scalar = false;
        bool with_bounds = false;
        int zdimid = CDI_UNDEFID;
        int zvarid = CDI_UNDEFID;
        size_t zsize = 1;
        int psvarid = -1;
        int p0varid = -1;

        int positive = 0;
        int ndims = ncvar->ndims;

        if ( ncvar->zvarid != -1 && ncvars[ncvar->zvarid].ndims == 0 )
            {
            zvarid = ncvar->zvarid;
            is_scalar = true;
            }
        else
            {
            for ( int i = 0; i < ndims; i++ )
                {
                if ( ncvar->dimtype[i] == Z_AXIS )
                    zdimid = ncvar->dimids[i];
                }

            if ( zdimid != CDI_UNDEFID )
                {

                zvarid = (ncvar->zvarid != CDI_UNDEFID) ? ncvar->zvarid : ncdims[zdimid].ncvarid;
                zsize  = ncdims[zdimid].len;
                }
            }

        if ( CDI_Debug ) Message("nlevs = %zu", zsize);

        double *zvar = NULL;
        char **zcvals = NULL;
        size_t zclength = 0;

        int zaxisType = CDI_UNDEFID;
        if ( zvarid != CDI_UNDEFID ) zaxisType = ncvars[zvarid].zaxistype;
        if ( zaxisType == CDI_UNDEFID ) zaxisType = ZAXIS_GENERIC;

        int zdatatype = CDI_DATATYPE_FLT64;
        double *restrict lbounds = NULL;
        double *restrict ubounds = NULL;

        if ( zvarid != CDI_UNDEFID )
            {
            positive  = ncvars[zvarid].positive;
            pname     = ncvars[zvarid].name;
            plongname = ncvars[zvarid].longname;
            punits    = ncvars[zvarid].units;
            if ( ncvars[zvarid].xtype == NC_FLOAT ) zdatatype = CDI_DATATYPE_FLT32;


            if ( zaxisType == ZAXIS_CHAR )
                {
                if ( ncvars[zvarid].ndims == 2 )
                    {
                    zdatatype = CDI_DATATYPE_UINT8;
                    zclength = ncdims[ncvars[zvarid].dimids[1]].len;
                    cdf_load_cvals(zsize*zclength, zvarid, ncvar, &zcvals, zsize);
                    }
                }

            if ( zaxisType == ZAXIS_HYBRID && ncvars[zvarid].vct )
                {
                vct = ncvars[zvarid].vct;
                vctsize = ncvars[zvarid].vctsize;

                if ( ncvars[zvarid].psvarid != -1 ) psvarid = ncvars[zvarid].psvarid;
                if ( ncvars[zvarid].p0varid != -1 ) p0varid = ncvars[zvarid].p0varid;
                }

            if ( zaxisType != ZAXIS_CHAR )
                {
                zvar = (double*) Malloc(zsize*sizeof(double));
                cdf_get_var_double(ncvars[zvarid].ncid, zvarid, zvar);
                }

            if ( ncvars[zvarid].bounds != CDI_UNDEFID )
                {
                int nbdims = ncvars[ncvars[zvarid].bounds].ndims;
                if ( nbdims == 2 || is_scalar )
                    {
                    size_t nlevel  = is_scalar ? 1 : ncdims[ncvars[ncvars[zvarid].bounds].dimids[0]].len;
                    int nvertex = (int)ncdims[ncvars[ncvars[zvarid].bounds].dimids[1-is_scalar]].len;
                    if ( nlevel == zsize && nvertex == 2 )
                        {
                        with_bounds = true;
                        lbounds = (double *) Malloc(4 * nlevel*sizeof(double));
                        ubounds = lbounds + nlevel;
                        double *restrict zbounds = lbounds + 2 * nlevel;
                        cdf_get_var_double(ncvars[zvarid].ncid, ncvars[zvarid].bounds, zbounds);
                        for ( size_t i = 0; i < nlevel; ++i )
                            {
                            lbounds[i] = zbounds[i*2];
                            ubounds[i] = zbounds[i*2+1];
                            }
                        }
                    }
                }
            }
        else
            {
            pname     = (zdimid != CDI_UNDEFID) ? ncdims[zdimid].name : NULL;
            plongname = NULL;
            punits    = NULL;

            if ( zsize == 1 && zdimid == CDI_UNDEFID )
                {
                zaxisType = (ncvar->zaxistype != CDI_UNDEFID) ? ncvar->zaxistype : ZAXIS_SURFACE;

                    {
                    zvar = (double*) Malloc(sizeof(double));
                    zvar[0] = 0;
                    }
                }
            }

        if ( zsize > INT_MAX )
            {
            Warning("Size limit exceeded for z-axis dimension (limit=%d)!", INT_MAX);
            return CDI_EDIMSIZE;
            }

        ncvar->zaxisID = varDefZaxis(vlistID, zaxisType, (int) zsize, zvar, (const char **)zcvals, zclength, with_bounds, lbounds, ubounds,
                                    (int)vctsize, vct, pname, plongname, punits, zdatatype, 1, 0);

        int zaxisID = ncvar->zaxisID;

        if ( CDI_cmor_mode && zsize == 1 && zaxisType != ZAXIS_HYBRID ) zaxisDefScalar(zaxisID);

        if ( uuidOfVGrid[0] != 0 )
            zaxisDefUUID(zaxisID, uuidOfVGrid);

        if ( zaxisType == ZAXIS_HYBRID )
            {
            if ( psvarid != -1 )
                cdiZaxisDefKeyStr(zaxisID, CDI_KEY_PSNAME, (int)(strlen(ncvars[psvarid].name)+1), ncvars[psvarid].name);
            if ( p0varid != -1 )
                {
                double px = 1;
                cdf_get_var_double(ncvars[p0varid].ncid, p0varid, &px);
                cdiZaxisDefKeyFlt(zaxisID, CDI_KEY_P0VALUE, px);
                cdiZaxisDefKeyStr(zaxisID, CDI_KEY_P0NAME, (int)(strlen(ncvars[p0varid].name)+1), ncvars[p0varid].name);
                }
            }

        if ( positive > 0 ) zaxisDefPositive(zaxisID, positive);
        if ( is_scalar ) zaxisDefScalar(zaxisID);

        if ( zdimid != CDI_UNDEFID )
            cdiZaxisDefKeyStr(zaxisID, CDI_KEY_DIMNAME, (int)(strlen(ncdims[zdimid].name)+1), ncdims[zdimid].name);

        if ( zvar    ) Free(zvar);
        if ( zcvals  )
            {
            for ( size_t i = 0; i < zsize; i++ )
                Free(zcvals[i]);
            Free(zcvals);
            }
        if ( lbounds ) Free(lbounds);

        if ( zvarid != CDI_UNDEFID )
            {
            int ncid = ncvars[zvarid].ncid;
            int nvatts = ncvars[zvarid].natts;
            for ( int iatt = 0; iatt < nvatts; ++iatt )
                {
                int attnum = ncvars[zvarid].atts[iatt];
                cdf_set_cdi_attr(ncid, zvarid, attnum, zaxisID, CDI_GLOBAL);
                }
            }

        int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);
        streamptr->zaxisID[zaxisindex] = zdimid;

        if ( CDI_Debug )
            Message("zaxisID %d %d %s", zaxisID, ncvarid, ncvar->name);

        for ( int ncvarid2 = ncvarid+1; ncvarid2 < nvars; ncvarid2++ )
            if ( ncvars[ncvarid2].isvar == TRUE && ncvars[ncvarid2].zaxisID == CDI_UNDEFID   )
            {
                int zvarid2 = CDI_UNDEFID;
                if ( ncvars[ncvarid2].zvarid != CDI_UNDEFID && ncvars[ncvars[ncvarid2].zvarid].ndims == 0 )
                zvarid2 = ncvars[ncvarid2].zvarid;

                int zdimid2 = CDI_UNDEFID;
                ndims = ncvars[ncvarid2].ndims;
                for ( int i = 0; i < ndims; i++ )
                {
                    if ( ncvars[ncvarid2].dimtype[i] == Z_AXIS )
                    zdimid2 = ncvars[ncvarid2].dimids[i];
                }

                if ( zdimid == zdimid2  )
                {
                    if ( (zdimid != CDI_UNDEFID && ncvars[ncvarid2].zaxistype == CDI_UNDEFID) ||
                        (zdimid == CDI_UNDEFID && zvarid != CDI_UNDEFID && zvarid == zvarid2) ||
                        (zdimid == CDI_UNDEFID && zaxisType == ncvars[ncvarid2].zaxistype) ||
                        (zdimid == CDI_UNDEFID && zvarid2 == CDI_UNDEFID && ncvars[ncvarid2].zaxistype == CDI_UNDEFID) )
                    {
                        if ( CDI_Debug )
                        Message("zaxisID %d %d %s", zaxisID, ncvarid2, ncvars[ncvarid2].name);
                        ncvars[ncvarid2].zaxisID = zaxisID;
                    }
                }
            }
        }
    }

return 0;
}


struct cdf_varinfo
{
int        varid;
const char *name;
};

static
int cdf_cmp_varname(const void *s1, const void *s2)
{
const struct cdf_varinfo *x = (const struct cdf_varinfo *)s1,
                        *y = (const struct cdf_varinfo *)s2;
return strcmp(x->name, y->name);
}


static
void cdf_define_all_vars(stream_t *streamptr, int vlistID, int instID, int modelID, int *varids, int nvars, int num_ncvars, ncvar_t *ncvars)
{
if ( CDI_Debug )
    for ( int i = 0; i < nvars; i++ ) Message("varids[%d] = %d", i, varids[i]);

if ( streamptr->sortname )
    {
    struct cdf_varinfo *varInfo
        = (struct cdf_varinfo *) Malloc((size_t)nvars * sizeof(struct cdf_varinfo));

    for ( int varID = 0; varID < nvars; varID++ )
        {
        int ncvarid = varids[varID];
        varInfo[varID].varid = ncvarid;
        varInfo[varID].name = ncvars[ncvarid].name;
        }
    qsort(varInfo, (size_t)nvars, sizeof(varInfo[0]), cdf_cmp_varname);
    for ( int varID = 0; varID < nvars; varID++ )
        {
        varids[varID] = varInfo[varID].varid;
        }
    Free(varInfo);
    if ( CDI_Debug )
        for ( int i = 0; i < nvars; i++ ) Message("sorted varids[%d] = %d", i, varids[i]);
    }

for ( int varID1 = 0; varID1 < nvars; varID1++ )
    {
    int ncvarid = varids[varID1];
    int gridID  = ncvars[ncvarid].gridID;
    int zaxisID = ncvars[ncvarid].zaxisID;

    stream_new_var(streamptr, gridID, zaxisID, CDI_UNDEFID);
    int varID = vlistDefVar(vlistID, gridID, zaxisID, ncvars[ncvarid].timetype);

#ifdef HAVE_NETCDF4
    if ( ncvars[ncvarid].deflate )
        vlistDefVarCompType(vlistID, varID, CDI_COMPRESS_ZIP);

    if ( ncvars[ncvarid].chunked && ncvars[ncvarid].chunktype != CDI_UNDEFID )
        vlistDefVarChunkType(vlistID, varID, ncvars[ncvarid].chunktype);
#endif

    streamptr->vars[varID1].defmiss = false;
    streamptr->vars[varID1].ncvarid = ncvarid;

    vlistDefVarName(vlistID, varID, ncvars[ncvarid].name);
    if ( ncvars[ncvarid].param != CDI_UNDEFID ) vlistDefVarParam(vlistID, varID, ncvars[ncvarid].param);
    if ( ncvars[ncvarid].code != CDI_UNDEFID )  vlistDefVarCode(vlistID, varID, ncvars[ncvarid].code);
    if ( ncvars[ncvarid].code != CDI_UNDEFID )
        {
        int param = cdiEncodeParam(ncvars[ncvarid].code, ncvars[ncvarid].tabnum, 255);
        vlistDefVarParam(vlistID, varID, param);
        }
    if ( ncvars[ncvarid].longname[0] )  vlistDefVarLongname(vlistID, varID, ncvars[ncvarid].longname);
    if ( ncvars[ncvarid].stdname[0] )   vlistDefVarStdname(vlistID, varID, ncvars[ncvarid].stdname);
    if ( ncvars[ncvarid].units[0] )     vlistDefVarUnits(vlistID, varID, ncvars[ncvarid].units);

    if ( ncvars[ncvarid].lvalidrange )
        vlistDefVarValidrange(vlistID, varID, ncvars[ncvarid].validrange);

    if ( IS_NOT_EQUAL(ncvars[ncvarid].addoffset, 0) )
        vlistDefVarAddoffset(vlistID, varID, ncvars[ncvarid].addoffset);
    if ( IS_NOT_EQUAL(ncvars[ncvarid].scalefactor, 1) )
        vlistDefVarScalefactor(vlistID, varID, ncvars[ncvarid].scalefactor);

    vlistDefVarDatatype(vlistID, varID, cdfInqDatatype(ncvars[ncvarid].xtype, ncvars[ncvarid].lunsigned));

    vlistDefVarInstitut(vlistID, varID, instID);
    vlistDefVarModel(vlistID, varID, modelID);
    if ( ncvars[ncvarid].tableID != CDI_UNDEFID )
        vlistDefVarTable(vlistID, varID, ncvars[ncvarid].tableID);

    if ( ncvars[ncvarid].deffillval == false && ncvars[ncvarid].defmissval )
        {
        ncvars[ncvarid].deffillval = true;
        ncvars[ncvarid].fillval    = ncvars[ncvarid].missval;
        }

    if ( ncvars[ncvarid].deffillval )
        vlistDefVarMissval(vlistID, varID, ncvars[ncvarid].fillval);

    if ( CDI_Debug )
        Message("varID = %d  gridID = %d  zaxisID = %d", varID,
                vlistInqVarGrid(vlistID, varID), vlistInqVarZaxis(vlistID, varID));

    int gridindex = vlistGridIndex(vlistID, gridID);
    int xdimid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
    int ydimid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y];

    int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);
    int zdimid = streamptr->zaxisID[zaxisindex];

    int ndims = ncvars[ncvarid].ndims;
    int iodim = 0;
    int ixyz = 0;
    static const int ipow10[4] = {1, 10, 100, 1000};

    if ( ncvars[ncvarid].timetype != TIME_CONSTANT ) iodim++;

    const int *dimids = ncvars[ncvarid].dimids;

    if ( gridInqType(gridID) == GRID_UNSTRUCTURED && ndims-iodim <= 2 && ydimid == xdimid )
        {
        ixyz = (xdimid == dimids[ndims-1]) ? 321 : 213;
        }
    else
        {
        for ( int idim = iodim; idim < ndims; idim++ )
            {
            if      ( xdimid == dimids[idim] ) ixyz += 1*ipow10[ndims-idim-1];
            else if ( ydimid == dimids[idim] ) ixyz += 2*ipow10[ndims-idim-1];
            else if ( zdimid == dimids[idim] ) ixyz += 3*ipow10[ndims-idim-1];
            }
        }

    vlistDefVarXYZ(vlistID, varID, ixyz);

    if ( ncvars[ncvarid].numberOfForecastsInEnsemble != -1 )
        {
        cdiDefKeyInt(vlistID, varID, CDI_KEY_TYPEOFENSEMBLEFORECAST, ncvars[ncvarid].typeOfEnsembleForecast);
        cdiDefKeyInt(vlistID, varID, CDI_KEY_NUMBEROFFORECASTSINENSEMBLE, ncvars[ncvarid].numberOfForecastsInEnsemble);
        cdiDefKeyInt(vlistID, varID, CDI_KEY_PERTURBATIONNUMBER, ncvars[ncvarid].perturbationNumber);
        }

    if ( ncvars[ncvarid].extra[0] != 0 )
        {
        vlistDefVarExtra(vlistID, varID, ncvars[ncvarid].extra);
        }
    }

for ( int varID = 0; varID < nvars; varID++ )
    {
    int ncvarid = varids[varID];
    int ncid = ncvars[ncvarid].ncid;

    int nvatts = ncvars[ncvarid].natts;
    for ( int iatt = 0; iatt < nvatts; ++iatt )
        {
        int attnum = ncvars[ncvarid].atts[iatt];
        cdf_set_cdi_attr(ncid, ncvarid, attnum, vlistID, varID);
        }

    if ( ncvars[ncvarid].atts )
        {
        Free(ncvars[ncvarid].atts);
        ncvars[ncvarid].atts = NULL;
        }

    if ( ncvars[ncvarid].vct )
        {
        Free(ncvars[ncvarid].vct);
        ncvars[ncvarid].vct = NULL;
        }
    }


for ( int ncvarid = 0; ncvarid < num_ncvars; ncvarid++ )
    if ( ncvars[ncvarid].atts ) Free(ncvars[ncvarid].atts);

if ( varids ) Free(varids);

for ( int varID = 0; varID < nvars; varID++ )
    {
    if ( vlistInqVarCode(vlistID, varID) == -varID-1 )
        {
        char name[CDI_MAX_NAME]; name[0] = 0;
        vlistInqVarName(vlistID, varID, name);
        size_t len = strlen(name);
        if ( len > 3 && isdigit((int) name[3]) )
            {
            if ( str_is_equal(name, "var") )
                {
                vlistDefVarCode(vlistID, varID, atoi(name+3));

                }
            }
        else if ( len > 4 && isdigit((int) name[4]) )
            {
            if ( str_is_equal(name, "code") )
                {
                vlistDefVarCode(vlistID, varID, atoi(name+4));

                }
            }
        else if ( len > 5 && isdigit((int) name[5]) )
            {
            if ( str_is_equal(name, "param") )
                {
                int pnum = -1, pcat = 255, pdis = 255;
                sscanf(name+5, "%d.%d.%d", &pnum, &pcat, &pdis);
                vlistDefVarParam(vlistID, varID, cdiEncodeParam(pnum, pcat, pdis));

                }
            }
        }
    }

for ( int varID = 0; varID < nvars; varID++ )
    {
    int varInstID  = vlistInqVarInstitut(vlistID, varID);
    int varModelID = vlistInqVarModel(vlistID, varID);
    int varTableID = vlistInqVarTable(vlistID, varID);
    int code = vlistInqVarCode(vlistID, varID);
    if ( cdiDefaultTableID != CDI_UNDEFID )
        {
        char name[CDI_MAX_NAME]; name[0] = 0;
        char longname[CDI_MAX_NAME]; longname[0] = 0;
        char units[CDI_MAX_NAME]; units[0] = 0;
        tableInqEntry(cdiDefaultTableID, code, -1, name, longname, units);
        if ( name[0] )
            {
            vlistDestroyVarName(vlistID, varID);
            vlistDestroyVarLongname(vlistID, varID);
            vlistDestroyVarUnits(vlistID, varID);

            if ( varTableID != CDI_UNDEFID )
                {
                vlistDefVarName(vlistID, varID, name);
                if ( longname[0] ) vlistDefVarLongname(vlistID, varID, longname);
                if ( units[0] ) vlistDefVarUnits(vlistID, varID, units);
                }
            else
                {
                varTableID = cdiDefaultTableID;
                }
            }

        if ( cdiDefaultModelID != CDI_UNDEFID ) varModelID = cdiDefaultModelID;
        if ( cdiDefaultInstID  != CDI_UNDEFID ) varInstID  = cdiDefaultInstID;
        }
    if ( varInstID  != CDI_UNDEFID ) vlistDefVarInstitut(vlistID, varID, varInstID);
    if ( varModelID != CDI_UNDEFID ) vlistDefVarModel(vlistID, varID, varModelID);
    if ( varTableID != CDI_UNDEFID ) vlistDefVarTable(vlistID, varID, varTableID);
    }
}

static
void cdf_copy_attint(int fileID, int vlistID, nc_type xtype, size_t attlen, char *attname)
{
int attint[8];
int *pattint = (attlen > 8) ? (int*) malloc(attlen*sizeof(int)) : attint;
cdfGetAttInt(fileID, NC_GLOBAL, attname, attlen, pattint);
int datatype = (xtype == NC_SHORT) ? CDI_DATATYPE_INT16 : CDI_DATATYPE_INT32;
cdiDefAttInt(vlistID, CDI_GLOBAL, attname, datatype, (int)attlen, pattint);
if (attlen > 8) free(pattint);
}

static
void cdf_copy_attflt(int fileID, int vlistID, nc_type xtype, size_t attlen, char *attname)
{
double attflt[8];
double *pattflt = (attlen > 8) ? (double*) malloc(attlen*sizeof(int)) : attflt;
cdfGetAttDouble(fileID, NC_GLOBAL, attname, attlen, pattflt);
int datatype = (xtype == NC_FLOAT) ? CDI_DATATYPE_FLT32 : CDI_DATATYPE_FLT64;
cdiDefAttFlt(vlistID, CDI_GLOBAL, attname, datatype, (int)attlen, pattflt);
if (attlen > 8) free(pattflt);
}

static
void cdf_scan_global_attr(int fileID, int vlistID, stream_t *streamptr, int ngatts, int *instID, int *modelID, bool *ucla_les, unsigned char *uuidOfHGrid, unsigned char *uuidOfVGrid, char *gridfile, int *number_of_grid_used)
{
nc_type xtype;
size_t attlen;
char attname[CDI_MAX_NAME];
char attstring[65636];

for ( int iatt = 0; iatt < ngatts; iatt++ )
    {
    cdf_inq_attname(fileID, NC_GLOBAL, iatt, attname);
    cdf_inq_atttype(fileID, NC_GLOBAL, attname, &xtype);
    cdf_inq_attlen(fileID, NC_GLOBAL, attname, &attlen);

    if ( xtypeIsText(xtype) )
        {
        cdfGetAttText(fileID, NC_GLOBAL, attname, sizeof(attstring), attstring);

        size_t attstrlen = strlen(attstring);

        if ( attlen > 0 && attstring[0] != 0 )
            {
            if ( strcmp(attname, "history") == 0 )
                {
                streamptr->historyID = iatt;
                }
            else if ( strcmp(attname, "institution") == 0 )
                {
                *instID = institutInq(0, 0, NULL, attstring);
                if ( *instID == CDI_UNDEFID )
                    *instID = institutDef(0, 0, NULL, attstring);
                }
            else if ( strcmp(attname, "source") == 0 )
                {
                *modelID = modelInq(-1, 0, attstring);
                if ( *modelID == CDI_UNDEFID )
                    *modelID = modelDef(-1, 0, attstring);
                }
            else if ( strcmp(attname, "Source") == 0 )
                {
                if ( strncmp(attstring, "UCLA-LES", 8) == 0 )
                    *ucla_les = true;
                }

            else if ( strcmp(attname, "CDI") == 0 )
                {
                }
            else if ( strcmp(attname, "CDO") == 0 )
                {
                }

            else if ( strcmp(attname, "grid_file_uri") == 0 )
                {
                memcpy(gridfile, attstring, attstrlen+1);
                }
            else if ( strcmp(attname, "uuidOfHGrid") == 0 && attstrlen == 36 )
                {
                attstring[36] = 0;
                cdiStr2UUID(attstring, uuidOfHGrid);

                }
            else if ( strcmp(attname, "uuidOfVGrid") == 0 && attstrlen == 36 )
                {
                attstring[36] = 0;
                cdiStr2UUID(attstring, uuidOfVGrid);
                }
            else
                {
                if ( strcmp(attname, "ICON_grid_file_uri") == 0 && gridfile[0] == 0 )
                    memcpy(gridfile, attstring, attstrlen+1);

                cdiDefAttTxt(vlistID, CDI_GLOBAL, attname, (int)attstrlen, attstring);
                }
            }
        }
    else if ( xtype == NC_SHORT || xtype == NC_INT )
        {
        if ( strcmp(attname, "number_of_grid_used") == 0 )
            {
            (*number_of_grid_used) = CDI_UNDEFID;
            cdfGetAttInt(fileID, NC_GLOBAL, attname, 1, number_of_grid_used);
            }
        else
            {
            cdf_copy_attint(fileID, vlistID, xtype, attlen, attname);
            }
        }
    else if ( xtype == NC_FLOAT || xtype == NC_DOUBLE )
        {
        cdf_copy_attflt(fileID, vlistID, xtype, attlen, attname);
        }
    }
}

static
int find_leadtime(int nvars, ncvar_t *ncvars)
{
int leadtime_id = CDI_UNDEFID;

for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    if ( ncvars[ncvarid].ndims == 1 )
        if ( ncvars[ncvarid].stdname[0] && strcmp(ncvars[ncvarid].stdname, "forecast_period") == 0 )
        {
            leadtime_id = ncvarid;
            break;
        }
    }

return leadtime_id;
}

static
void find_time_vars(int nvars, ncvar_t *ncvars, ncdim_t *ncdims, int timedimid, stream_t *streamptr,
                    bool *time_has_units, bool *time_has_bounds, bool *time_climatology)
{
int ncvarid;

if ( timedimid == CDI_UNDEFID )
    {
    char timeunits[CDI_MAX_NAME];

    for ( ncvarid = 0; ncvarid < nvars; ncvarid++ )
        {
        if ( ncvars[ncvarid].ndims == 0 && strcmp(ncvars[ncvarid].name, "time") == 0 )
            {
            if ( ncvars[ncvarid].units[0] )
                {
                strcpy(timeunits, ncvars[ncvarid].units);
                str_tolower(timeunits);

                if ( is_time_units(timeunits) )
                    {
                    streamptr->basetime.ncvarid = ncvarid;
                    break;
                    }
                }
            }
        }
    }
else
    {
    bool ltimevar = false;

    if ( ncdims[timedimid].ncvarid != CDI_UNDEFID )
        {
        streamptr->basetime.ncvarid = ncdims[timedimid].ncvarid;
        ltimevar = true;
        }

    for ( ncvarid = 0; ncvarid < nvars; ncvarid++ )
        if ( ncvarid != streamptr->basetime.ncvarid &&
            ncvars[ncvarid].ndims == 1 &&
            timedimid == ncvars[ncvarid].dimids[0] &&
            !xtypeIsText(ncvars[ncvarid].xtype) &&
            is_timeaxis_units(ncvars[ncvarid].units) )
        {
            ncvars[ncvarid].isvar = FALSE;

            if ( !ltimevar )
            {
                streamptr->basetime.ncvarid = ncvarid;
                ltimevar = true;
                if ( CDI_Debug )
                fprintf(stderr, "timevar %s\n", ncvars[ncvarid].name);
            }
            else
            {
                Warning("Found more than one time variable, skipped variable %s!", ncvars[ncvarid].name);
            }
        }

    if ( ltimevar == false )
        {
        for ( ncvarid = 0; ncvarid < nvars; ncvarid++ )
            if ( ncvarid != streamptr->basetime.ncvarid &&
                ncvars[ncvarid].ndims == 2 &&
                timedimid == ncvars[ncvarid].dimids[0] &&
                xtypeIsText(ncvars[ncvarid].xtype) &&
                ncdims[ncvars[ncvarid].dimids[1]].len == 19 )
            {
                streamptr->basetime.ncvarid = ncvarid;
                streamptr->basetime.lwrf    = true;
                break;
            }
        }


    ncvarid = streamptr->basetime.ncvarid;

    if ( ncvarid == CDI_UNDEFID )
        {
        Warning("Time variable >%s< not found!", ncdims[timedimid].name);
        }
    }


ncvarid = streamptr->basetime.ncvarid;

if ( ncvarid != CDI_UNDEFID && streamptr->basetime.lwrf == false )
    {
    if ( ncvars[ncvarid].units[0] != 0 ) *time_has_units = true;

    if ( ncvars[ncvarid].bounds != CDI_UNDEFID )
        {
        int nbdims = ncvars[ncvars[ncvarid].bounds].ndims;
        if ( nbdims == 2 )
            {
            int len = (int) ncdims[ncvars[ncvars[ncvarid].bounds].dimids[nbdims-1]].len;
            if ( len == 2 && timedimid == ncvars[ncvars[ncvarid].bounds].dimids[0] )
                {
                *time_has_bounds = true;
                streamptr->basetime.ncvarboundsid = ncvars[ncvarid].bounds;
                if ( ncvars[ncvarid].climatology ) *time_climatology = true;
                }
            }
        }
    }
}

static
void read_vct_echam(int fileID, int nvars, ncvar_t *ncvars, ncdim_t *ncdims, double **vct, size_t *pvctsize)
{

int nvcth_id = CDI_UNDEFID, vcta_id = CDI_UNDEFID, vctb_id = CDI_UNDEFID;

for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    if ( ncvars[ncvarid].ndims == 1 )
        {
        size_t len = strlen(ncvars[ncvarid].name);
        if ( len == 4 && ncvars[ncvarid].name[0] == 'h' && ncvars[ncvarid].name[1] == 'y' )
            {
            if ( ncvars[ncvarid].name[2] == 'a' && ncvars[ncvarid].name[3] == 'i' )
                {
                vcta_id = ncvarid;
                nvcth_id = ncvars[ncvarid].dimids[0];
                ncvars[ncvarid].isvar = FALSE;
                }
            else if ( ncvars[ncvarid].name[2] == 'b' && ncvars[ncvarid].name[3] == 'i' )
                {
                vctb_id = ncvarid;
                nvcth_id = ncvars[ncvarid].dimids[0];
                ncvars[ncvarid].isvar = FALSE;
                }
            else if ( (ncvars[ncvarid].name[2] == 'a' || ncvars[ncvarid].name[2] == 'b') && ncvars[ncvarid].name[3] == 'm' )
                {
                ncvars[ncvarid].isvar = FALSE;
                }
            }
        }
    }


if ( nvcth_id != CDI_UNDEFID && vcta_id != CDI_UNDEFID && vctb_id != CDI_UNDEFID )
    {
    size_t vctsize = ncdims[nvcth_id].len;
    vctsize *= 2;
    *vct = (double *) Malloc(vctsize*sizeof(double));
    cdf_get_var_double(fileID, vcta_id, *vct);
    cdf_get_var_double(fileID, vctb_id, *vct+vctsize/2);
    *pvctsize = vctsize;
    }
}

static
void cdf_set_ucla_dimtype(int ndims, ncdim_t *ncdims, ncvar_t *ncvars)
{
for ( int ncdimid = 0; ncdimid < ndims; ncdimid++ )
    {
    int ncvarid = ncdims[ncdimid].ncvarid;
    if ( ncvarid != -1 )
        {
        if ( ncdims[ncdimid].dimtype == CDI_UNDEFID && ncvars[ncvarid].units[0] == 'm' )
            {
            if      ( ncvars[ncvarid].name[0] == 'x' ) ncdims[ncdimid].dimtype = X_AXIS;
            else if ( ncvars[ncvarid].name[0] == 'y' ) ncdims[ncdimid].dimtype = Y_AXIS;
            else if ( ncvars[ncvarid].name[0] == 'z' ) ncdims[ncdimid].dimtype = Z_AXIS;
            }
        }
    }
}

static
int cdf_check_vars(int nvars, ncvar_t *ncvars, size_t ntsteps, int timedimid)
{
for ( int ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    if ( timedimid != CDI_UNDEFID )
        if ( ncvars[ncvarid].isvar == -1 &&
            ncvars[ncvarid].ndims > 1   &&
            timedimid == ncvars[ncvarid].dimids[0] )
        cdf_set_var(ncvars, ncvarid, TRUE);

    if ( ncvars[ncvarid].isvar == -1 && ncvars[ncvarid].ndims == 0 )
        cdf_set_var(ncvars, ncvarid, nvars == 1 ? TRUE : FALSE);

    if ( ncvars[ncvarid].isvar == -1 && ncvars[ncvarid].ndims > 0 )
        cdf_set_var(ncvars, ncvarid, TRUE);

    if ( ncvars[ncvarid].isvar == -1 )
        {
        ncvars[ncvarid].isvar = 0;
        Warning("Variable %s has an unknown type, skipped!", ncvars[ncvarid].name);
        continue;
        }

    if ( ncvars[ncvarid].ndims > 4 )
        {
        ncvars[ncvarid].isvar = 0;
        Warning("%d dimensional variables are not supported, skipped variable %s!",
                ncvars[ncvarid].ndims, ncvars[ncvarid].name);
        continue;
        }

    if ( ncvars[ncvarid].ndims == 4 && timedimid == CDI_UNDEFID )
        {
        ncvars[ncvarid].isvar = 0;
        Warning("%d dimensional variables without time dimension are not supported, skipped variable %s!",
                ncvars[ncvarid].ndims, ncvars[ncvarid].name);
        continue;
        }

    if ( xtypeIsText(ncvars[ncvarid].xtype) )
        {
        ncvars[ncvarid].isvar = 0;
        continue;
        }

    if ( cdfInqDatatype(ncvars[ncvarid].xtype, ncvars[ncvarid].lunsigned) == -1 )
        {
        ncvars[ncvarid].isvar = 0;
        Warning("Unsupported data type, skipped variable %s!", ncvars[ncvarid].name);
        continue;
        }

    if ( timedimid != CDI_UNDEFID && ntsteps == 0 && ncvars[ncvarid].ndims > 0 )
        {
        if ( timedimid == ncvars[ncvarid].dimids[0] )
            {
            ncvars[ncvarid].isvar = 0;
            Warning("Number of time steps undefined, skipped variable %s!", ncvars[ncvarid].name);
            continue;
            }
        }
    }

return timedimid;
}


int cdfInqContents(stream_t *streamptr)
{
int ndims, nvars, ngatts, unlimdimid;
int ncvarid;
int *varids;
int nvarids;
bool time_has_units = false;
bool time_has_bounds = false;
bool time_climatology = false;
int leadtime_id = CDI_UNDEFID;
int nvars_data;
int instID  = CDI_UNDEFID;
int modelID = CDI_UNDEFID;
int calendar = CDI_UNDEFID;
int format = 0;
bool ucla_les = false;
char gridfile[8912];
char fcreftime[CDI_MAX_NAME];
int number_of_grid_used = CDI_UNDEFID;

unsigned char uuidOfHGrid[CDI_UUID_SIZE];
unsigned char uuidOfVGrid[CDI_UUID_SIZE];
memset(uuidOfHGrid, 0, CDI_UUID_SIZE);
memset(uuidOfVGrid, 0, CDI_UUID_SIZE);
gridfile[0] = 0;
fcreftime[0] = 0;

int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;

if ( CDI_Debug ) Message("streamID = %d, fileID = %d", streamptr->self, fileID);

#ifdef HAVE_NETCDF4
nc_inq_format(fileID, &format);
#endif

cdf_inq(fileID, &ndims , &nvars, &ngatts, &unlimdimid);

if ( CDI_Debug )
    Message("root: ndims %d, nvars %d, ngatts %d", ndims, nvars, ngatts);


ncdim_t *ncdims = ndims ? (ncdim_t *) Malloc((size_t)ndims * sizeof(ncdim_t)) : NULL;
init_ncdims(ndims, ncdims);

int ncdimid = 0;
size_t dimlen;
for ( int dimid = 0; dimid < NC_MAX_DIMS; ++dimid )
    {
    int status = nc_inq_dimlen(fileID, dimid, &dimlen);
    if ( status == NC_NOERR )
        {
        ncdims[ncdimid++].dimid = dimid;
        if ( ncdimid == ndims ) break;
        }
    }

#ifdef  HAVE_NETCDF4
if ( format == NC_FORMAT_NETCDF4 )
    {
    int ncid;
    int numgrps = 0;
    int ncids[NC_MAX_VARS];
    char gname[CDI_MAX_NAME];
    int gndims, gnvars, gngatts, gunlimdimid;
    nc_inq_grps(fileID, &numgrps, ncids);
    for ( int i = 0; i < numgrps; ++i )
        {
        ncid = ncids[i];
        nc_inq_grpname(ncid, gname);
        cdf_inq(ncid, &gndims , &gnvars, &gngatts, &gunlimdimid);

        if ( CDI_Debug )
            Message("%s: ndims %d, nvars %d, ngatts %d", gname, gndims, gnvars, gngatts);

        if ( gndims == 0 )
            {
            }
        }
    if ( numgrps ) Warning("NetCDF4 groups not supported! Found %d root group%s.", numgrps, numgrps>1?"s":"");
    }
#endif

if ( nvars == 0 )
    {
    Warning("No arrays found!");
    return CDI_EUFSTRUCT;
    }


ncvar_t *ncvars = nvars ? (ncvar_t *) Malloc((size_t)nvars * sizeof (ncvar_t)) : NULL;
init_ncvars(nvars, ncvars);

for ( ncvarid = 0; ncvarid < nvars; ++ncvarid ) ncvars[ncvarid].ncid = fileID;



cdf_scan_global_attr(fileID, vlistID, streamptr, ngatts, &instID, &modelID, &ucla_les,
                    uuidOfHGrid, uuidOfVGrid, gridfile, &number_of_grid_used);


int timedimid = (unlimdimid >= 0) ? unlimdimid : cdf_time_dimid(fileID, ndims, nvars, ncdims);

streamptr->basetime.ncdimid = timedimid;

size_t ntsteps = 0;
if ( timedimid != CDI_UNDEFID ) cdf_inq_dimlen(fileID, ncdims[timedimid].dimid, &ntsteps);
if ( ntsteps > INT_MAX )
    {
    Warning("Size limit exceeded for time dimension (limit=%d)!", INT_MAX);
    return CDI_EDIMSIZE;
    }

if ( CDI_Debug ) Message("Number of timesteps = %zu", ntsteps);
if ( CDI_Debug ) Message("Time dimid = %d", streamptr->basetime.ncdimid);


for ( ncdimid = 0; ncdimid < ndims; ncdimid++ )
    {
    cdf_inq_dimlen(fileID, ncdims[ncdimid].dimid, &ncdims[ncdimid].len);
    cdf_inq_dimname(fileID, ncdims[ncdimid].dimid, ncdims[ncdimid].name);
    if ( timedimid == ncdimid )
        ncdims[ncdimid].dimtype = T_AXIS;
    }

if ( CDI_Debug ) cdf_print_vars(ncvars, nvars, "cdf_scan_var_attr");


cdf_scan_var_attr(nvars, ncvars, ndims, ncdims, timedimid, modelID, format);


if ( CDI_Debug ) cdf_print_vars(ncvars, nvars, "find coordinate vars");


for ( ncdimid = 0; ncdimid < ndims; ncdimid++ )
    {
    for ( ncvarid = 0; ncvarid < nvars; ncvarid++ )
        {
        if ( ncvars[ncvarid].ndims == 1 )
            {
            if ( timedimid != CDI_UNDEFID && timedimid == ncvars[ncvarid].dimids[0] )
                {
                if ( ncvars[ncvarid].isvar != FALSE ) cdf_set_var(ncvars, ncvarid, TRUE);
                }
            else
                {

                }


            if ( ncdimid == ncvars[ncvarid].dimids[0] && ncdims[ncdimid].ncvarid == CDI_UNDEFID )
                if ( strcmp(ncvars[ncvarid].name, ncdims[ncdimid].name) == 0 )
                {
                    ncdims[ncdimid].ncvarid = ncvarid;
                    ncvars[ncvarid].isvar = FALSE;
                }
            }
        }
    }


find_time_vars(nvars, ncvars, ncdims, timedimid, streamptr, &time_has_units, &time_has_bounds, &time_climatology);

leadtime_id = find_leadtime(nvars, ncvars);
if ( leadtime_id != CDI_UNDEFID ) ncvars[leadtime_id].isvar = FALSE;


timedimid = cdf_check_vars(nvars, ncvars, ntsteps, timedimid);


bool lhybrid_cf = false;
verify_coordinate_vars_1(fileID, ndims, ncdims, ncvars, timedimid, &lhybrid_cf);


verify_coordinate_vars_2(nvars, ncvars);

if ( CDI_Debug ) cdf_print_vars(ncvars, nvars, "verify_coordinate_vars");

if ( ucla_les ) cdf_set_ucla_dimtype(ndims, ncdims, ncvars);




for ( ncvarid = 0; ncvarid < nvars; ncvarid++ )
    {
    ncvar_t *ncvar = &ncvars[ncvarid];
    if ( ncvar->isvar == TRUE && ncvar->ncoordvars )
        {
        int ncoordvars = ncvar->ncoordvars;
        for ( int i = 0; i < ncoordvars; i++ )
            {
            if ( ncvar->coordvarids[i] != CDI_UNDEFID )
                {
                if      ( ncvars[ncvar->coordvarids[i]].islon ||
                            ncvars[ncvar->coordvarids[i]].isx )   ncvar->xvarid = ncvar->coordvarids[i];
                else if ( ncvars[ncvar->coordvarids[i]].islat ||
                            ncvars[ncvar->coordvarids[i]].isy )   ncvar->yvarid = ncvar->coordvarids[i];
                else if ( ncvars[ncvar->coordvarids[i]].islev ) ncvar->zvarid = ncvar->coordvarids[i];
                else if ( ncvars[ncvar->coordvarids[i]].isc )   ncvar->cvarids[i] = ncvar->coordvarids[i];
                }
            }
        }
    }


cdf_set_dimtype(nvars, ncvars, ncdims);


size_t vctsize = 0;
double *vct = NULL;
if ( !lhybrid_cf ) read_vct_echam(fileID, nvars, ncvars, ncdims, &vct, &vctsize);


if ( CDI_Debug ) cdf_print_vars(ncvars, nvars, "cdf_define_all_grids");


int status;
status = cdf_define_all_grids(streamptr->ncgrid, vlistID, ncdims, nvars, ncvars, timedimid, uuidOfHGrid, gridfile, number_of_grid_used);
if ( status < 0 ) return status;


status = cdf_define_all_zaxes(streamptr, vlistID, ncdims, nvars, ncvars, vctsize, vct, uuidOfVGrid);
if ( vct ) Free(vct);
if ( status < 0 ) return status;



varids = (int *) Malloc((size_t)nvars * sizeof (int));
nvarids = 0;
for ( ncvarid = 0; ncvarid < nvars; ncvarid++ )
    if ( ncvars[ncvarid].isvar == TRUE ) varids[nvarids++] = ncvarid;

nvars_data = nvarids;

if ( CDI_Debug ) Message("time varid = %d", streamptr->basetime.ncvarid);
if ( CDI_Debug ) Message("ntsteps = %zu", ntsteps);
if ( CDI_Debug ) Message("nvars_data = %d", nvars_data);


if ( nvars_data == 0 )
    {
    streamptr->ntsteps = 0;
    return CDI_EUFSTRUCT;
    }

if ( ntsteps == 0 && streamptr->basetime.ncdimid == CDI_UNDEFID && streamptr->basetime.ncvarid != CDI_UNDEFID )
    ntsteps = 1;

streamptr->ntsteps = (long)ntsteps;


cdf_define_all_vars(streamptr, vlistID, instID, modelID, varids, nvars_data, nvars, ncvars);


cdiCreateTimesteps(streamptr);


int nctimevarid = streamptr->basetime.ncvarid;

if ( time_has_units )
    {
    taxis_t *taxis = &streamptr->tsteps[0].taxis;

    if ( setBaseTime(ncvars[nctimevarid].units, taxis) == 1 )
        {
        nctimevarid = CDI_UNDEFID;
        streamptr->basetime.ncvarid = CDI_UNDEFID;
        }

    if ( leadtime_id != CDI_UNDEFID && taxis->type == TAXIS_RELATIVE )
        {
        streamptr->basetime.leadtimeid = leadtime_id;
        taxis->type = TAXIS_FORECAST;

        int timeunit = -1;
        if ( ncvars[leadtime_id].units[0] != 0 ) timeunit = scanTimeUnit(ncvars[leadtime_id].units);
        if ( timeunit == -1 ) timeunit = taxis->unit;
        taxis->fc_unit = timeunit;

        setForecastTime(fcreftime, taxis);
        }
    }

if ( time_has_bounds )
    {
    streamptr->tsteps[0].taxis.has_bounds = true;
    if ( time_climatology ) streamptr->tsteps[0].taxis.climatology = true;
    }

if ( nctimevarid != CDI_UNDEFID )
    {
    taxis_t *taxis = &streamptr->tsteps[0].taxis;
    ptaxisDefName(taxis, ncvars[nctimevarid].name);

    if ( ncvars[nctimevarid].longname[0] )
        ptaxisDefLongname(taxis, ncvars[nctimevarid].longname);

    if ( ncvars[nctimevarid].units[0] )
        ptaxisDefUnits(taxis, ncvars[nctimevarid].units);

    int datatype = (ncvars[nctimevarid].xtype == NC_FLOAT) ? CDI_DATATYPE_FLT32 : CDI_DATATYPE_FLT64;
    ptaxisDefDatatype(taxis, datatype);
    }

if ( nctimevarid != CDI_UNDEFID )
    if ( ncvars[nctimevarid].calendar == true )
    {
        char attstring[1024];
        cdfGetAttText(fileID, nctimevarid, "calendar", sizeof(attstring), attstring);
        str_tolower(attstring);
        set_calendar(attstring, &calendar);
    }

int taxisID;
if ( streamptr->tsteps[0].taxis.type == TAXIS_FORECAST )
    {
    taxisID = taxisCreate(TAXIS_FORECAST);
    }
else if ( streamptr->tsteps[0].taxis.type == TAXIS_RELATIVE )
    {
    taxisID = taxisCreate(TAXIS_RELATIVE);
    }
else
    {
    taxisID = taxisCreate(TAXIS_ABSOLUTE);
    if ( !time_has_units )
        {
        taxisDefTunit(taxisID, TUNIT_DAY);
        streamptr->tsteps[0].taxis.unit = TUNIT_DAY;
        }
    }


if ( calendar == CDI_UNDEFID && streamptr->tsteps[0].taxis.type != TAXIS_ABSOLUTE )
    {
    calendar = CALENDAR_STANDARD;
    }

#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5)
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wstrict-overflow"
#endif
if ( calendar != CDI_UNDEFID )
    {
    taxis_t *taxis = &streamptr->tsteps[0].taxis;
    taxis->calendar = calendar;
    taxisDefCalendar(taxisID, calendar);
    }
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5)
#pragma GCC diagnostic pop
#endif

vlistDefTaxis(vlistID, taxisID);

streamptr->curTsID = 0;
streamptr->rtsteps = 1;

(void) cdfInqTimestep(streamptr, 0);

cdfCreateRecords(streamptr, 0);


if ( ncdims ) Free(ncdims);


if ( ncvars ) Free(ncvars);

return 0;
}

static
void wrf_read_timestep(int fileID, int nctimevarid, int tsID, taxis_t *taxis)
{
size_t start[2], count[2];
char stvalue[32];
start[0] = (size_t) tsID; start[1] = 0;
count[0] = 1; count[1] = 19;
stvalue[0] = 0;
cdf_get_vara_text(fileID, nctimevarid, start, count, stvalue);
stvalue[19] = 0;
{
    int year = 1, month = 1, day = 1 , hour = 0, minute = 0, second = 0;
    if ( strlen(stvalue) == 19 )
    sscanf(stvalue, "%d-%d-%d_%d:%d:%d", &year, &month, &day, &hour, &minute, &second);
    taxis->vdate = cdiEncodeDate(year, month, day);
    taxis->vtime = cdiEncodeTime(hour, minute, second);
    taxis->type = TAXIS_ABSOLUTE;
}
}

static
double get_timevalue(int fileID, int nctimevarid, int tsID, timecache_t *tcache)
{
double timevalue = 0;

if ( tcache )
    {
    if ( tcache->size == 0 || (tsID < tcache->startid || tsID > (tcache->startid+tcache->size-1)) )
        {
        int maxvals = MAX_TIMECACHE_SIZE;
        tcache->startid = (tsID/MAX_TIMECACHE_SIZE)*MAX_TIMECACHE_SIZE;
        if ( (tcache->startid + maxvals) > tcache->maxvals ) maxvals = (tcache->maxvals)%MAX_TIMECACHE_SIZE;
        tcache->size = maxvals;
        size_t index = (size_t) tcache->startid;

        for ( int ival = 0; ival < maxvals; ++ival )
            {
            cdf_get_var1_double(fileID, nctimevarid, &index, &timevalue);
            if ( timevalue >= NC_FILL_DOUBLE || timevalue < -NC_FILL_DOUBLE ) timevalue = 0;
            tcache->cache[ival] = timevalue;
            index++;
            }
        }

    timevalue = tcache->cache[tsID%MAX_TIMECACHE_SIZE];
    }
else
    {
    size_t index = (size_t) tsID;
    cdf_get_var1_double(fileID, nctimevarid, &index, &timevalue);
    if ( timevalue >= NC_FILL_DOUBLE || timevalue < -NC_FILL_DOUBLE ) timevalue = 0;
    }

return timevalue;
}


int cdfInqTimestep(stream_t * streamptr, int tsID)
{
if ( CDI_Debug ) Message("streamID = %d  tsID = %d", streamptr->self, tsID);

if ( tsID < 0 ) Error("unexpected tsID = %d", tsID);

if ( tsID < streamptr->ntsteps && streamptr->ntsteps > 0 )
    {
    cdfCreateRecords(streamptr, tsID);

    taxis_t *taxis = &streamptr->tsteps[tsID].taxis;
    if ( tsID > 0 )
        ptaxisCopy(taxis, &streamptr->tsteps[0].taxis);

    double timevalue = tsID;

    int nctimevarid = streamptr->basetime.ncvarid;
    if ( nctimevarid != CDI_UNDEFID )
        {
        int fileID = streamptr->fileID;
        size_t index = (size_t)tsID;

        if ( streamptr->basetime.lwrf )
            {
            wrf_read_timestep(fileID, nctimevarid, tsID, taxis);
            }
        else
            {
#ifdef USE_TIMECACHE
            if ( streamptr->basetime.timevar_cache == NULL )
                {
                streamptr->basetime.timevar_cache = (timecache_t *) Malloc(MAX_TIMECACHE_SIZE*sizeof(timecache_t));
                streamptr->basetime.timevar_cache->size = 0;
                streamptr->basetime.timevar_cache->maxvals = streamptr->ntsteps;
                }
#endif
            timevalue = get_timevalue(fileID, nctimevarid, tsID, streamptr->basetime.timevar_cache);
            cdiDecodeTimeval(timevalue, taxis, &taxis->vdate, &taxis->vtime);
            }

        int nctimeboundsid = streamptr->basetime.ncvarboundsid;
        if ( nctimeboundsid != CDI_UNDEFID )
            {
            size_t start[2], count[2];
            start[0] = index; count[0] = 1; start[1] = 0; count[1] = 1;
            cdf_get_vara_double(fileID, nctimeboundsid, start, count, &timevalue);
            if ( timevalue >= NC_FILL_DOUBLE || timevalue < -NC_FILL_DOUBLE ) timevalue = 0;

            cdiDecodeTimeval(timevalue, taxis, &taxis->vdate_lb, &taxis->vtime_lb);

            start[0] = index; count[0] = 1; start[1] = 1; count[1] = 1;
            cdf_get_vara_double(fileID, nctimeboundsid, start, count, &timevalue);
            if ( timevalue >= NC_FILL_DOUBLE || timevalue < -NC_FILL_DOUBLE ) timevalue = 0;

            cdiDecodeTimeval(timevalue, taxis, &taxis->vdate_ub, &taxis->vtime_ub);
            }

        int leadtimeid = streamptr->basetime.leadtimeid;
        if ( leadtimeid != CDI_UNDEFID )
            {
            timevalue = get_timevalue(fileID, leadtimeid, tsID, NULL);
            cdiSetForecastPeriod(timevalue, taxis);
            }
        }
    }

streamptr->curTsID = tsID;
long nrecs = streamptr->tsteps[tsID].nrecs;

return (int) nrecs;
}


int cdfInqHistorySize(stream_t *streamptr)
{
size_t size = 0;
int ncid = streamptr->fileID;
if ( streamptr->historyID != CDI_UNDEFID )
    cdf_inq_attlen(ncid, NC_GLOBAL, "history", &size);

return (int) size;
}


void cdfInqHistoryString(stream_t *streamptr, char *history)
{
int ncid = streamptr->fileID;
if ( streamptr->historyID != CDI_UNDEFID )
    {
    nc_type atttype;
    cdf_inq_atttype(ncid, NC_GLOBAL, "history", &atttype);

    if ( atttype == NC_CHAR )
        {
        cdf_get_att_text(ncid, NC_GLOBAL, "history", history);
        }
#ifdef HAVE_NETCDF4
    else if ( atttype == NC_STRING )
        {

        Warning("History attribute with type NC_STRING unsupported!");
        }
#endif
    }
}

#endif

#ifndef  ZAXIS_H
#define  ZAXIS_H


typedef struct {
double value;
bool defined;
}
zkey_double_t;

typedef struct {
char     dimname[CDI_MAX_NAME];
char     vdimname[CDI_MAX_NAME];
char     name[CDI_MAX_NAME];
char     longname[CDI_MAX_NAME];
char     stdname[CDI_MAX_NAME];
char     units[CDI_MAX_NAME];
char     psname[CDI_MAX_NAME];
char     p0name[CDI_MAX_NAME];
zkey_double_t p0value;
double  *vals;
char   **cvals;
int      clength;
double  *lbounds;
double  *ubounds;
double  *weights;
int      self;
int      datatype;
int      scalar;
int      type;
int      ltype;
int      ltype2;
int      size;
int      direction;
int      vctsize;
unsigned positive;
double  *vct;
int      number;
int      nhlev;
unsigned char uuid[CDI_UUID_SIZE];
cdi_atts_t atts;
}
zaxis_t;


void zaxisGetTypeDescription(int zaxisType, int* outPositive, const char** outName, const char** outLongName, const char** outStdName, const char** outUnit);

unsigned cdiZaxisCount(void);

zaxis_t *zaxis_to_pointer(int zaxisID);

void cdiZaxisGetIndexList(unsigned numIDs, int *IDs);

void
zaxisUnpack(char * unpackBuffer, int unpackBufferSize,
            int * unpackBufferPos, int originNamespace, void *context,
            int force_id);

void zaxisDefLtype2(int zaxisID, int ltype2);

const resOps *getZaxisOps(void);

const char *zaxisInqNamePtr(int zaxisID);

const double *zaxisInqLevelsPtr(int zaxisID);
char **zaxisInqCValsPtr(int zaxisID);

void zaxisResize(int zaxisID, int size);

#endif


#ifdef HAVE_CONFIG_H
#endif

#ifdef HAVE_LIBNETCDF



#define  POSITIVE_UP    1
#define  POSITIVE_DOWN  2


static const char bndsName[] = "bnds";


void cdfCopyRecord(stream_t *streamptr2, stream_t *streamptr1)
{
int vlistID1 = streamptr1->vlistID;
int tsID     = streamptr1->curTsID;
int vrecID   = streamptr1->tsteps[tsID].curRecID;
int recID    = streamptr1->tsteps[tsID].recIDs[vrecID];
int ivarID   = streamptr1->tsteps[tsID].records[recID].varID;
int gridID   = vlistInqVarGrid(vlistID1, ivarID);
size_t datasize = gridInqSize(gridID);
int datatype = vlistInqVarDatatype(vlistID1, ivarID);
int memtype  = datatype != CDI_DATATYPE_FLT32 ? MEMTYPE_DOUBLE : MEMTYPE_FLOAT;

void *data = Malloc(datasize * (memtype == MEMTYPE_DOUBLE ? sizeof(double) : sizeof(float)));

size_t nmiss;
cdf_read_record(streamptr1, memtype, data, &nmiss);
cdf_write_record(streamptr2, memtype, data, nmiss);

Free(data);
}


void cdfDefRecord(stream_t *streamptr)
{
(void)streamptr;
}

static
void cdfDefTimeValue(stream_t *streamptr, int tsID)
{
int fileID = streamptr->fileID;

if ( CDI_Debug )
    Message("streamID = %d, fileID = %d", streamptr->self, fileID);

taxis_t *taxis = &streamptr->tsteps[tsID].taxis;

if ( streamptr->ncmode == 1 )
    {
    cdf_enddef(fileID);
    streamptr->ncmode = 2;
    }

double timevalue = cdiEncodeTimeval(taxis->vdate, taxis->vtime, &streamptr->tsteps[0].taxis);
if ( CDI_Debug ) Message("tsID = %d  timevalue = %f", tsID, timevalue);

int ncvarid = streamptr->basetime.ncvarid;
size_t index = (size_t)tsID;
cdf_put_var1_double(fileID, ncvarid, &index, &timevalue);

if ( taxis->has_bounds )
    {
    ncvarid = streamptr->basetime.ncvarboundsid;
    if ( ncvarid == CDI_UNDEFID ) Error("Call to taxisWithBounds() missing!");

    timevalue = cdiEncodeTimeval(taxis->vdate_lb, taxis->vtime_lb, &streamptr->tsteps[0].taxis);
    size_t start[2], count[2];
    start[0] = (size_t)tsID; count[0] = 1; start[1] = 0; count[1] = 1;
    cdf_put_vara_double(fileID, ncvarid, start, count, &timevalue);

    timevalue = cdiEncodeTimeval(taxis->vdate_ub, taxis->vtime_ub, &streamptr->tsteps[0].taxis);
    start[0] = (size_t)tsID; count[0] = 1; start[1] = 1; count[1] = 1;
    cdf_put_vara_double(fileID, ncvarid, start, count, &timevalue);
    }

ncvarid = streamptr->basetime.leadtimeid;
if ( taxis->type == TAXIS_FORECAST && ncvarid != CDI_UNDEFID )
    {
    timevalue = taxis->fc_period;
    cdf_put_var1_double(fileID, ncvarid, &index, &timevalue);
    }
}

void cdfDefTimestep(stream_t *streamptr, int tsID)
{
cdfDefTimeValue(streamptr, tsID);
}

static
void cdfDefComplex(stream_t *streamptr, int gridID, int gridindex)
{
int dimID;
ncgrid_t *ncgrid = streamptr->ncgrid;

for ( int index = 0; index < gridindex; ++index )
    {
    if ( ncgrid[index].ncIDs[CDF_DIMID_X] != CDI_UNDEFID )
        {
        int gridID0 = ncgrid[index].gridID;
        int gridtype0 = gridInqType(gridID0);
        if ( gridtype0 == GRID_SPECTRAL || gridtype0 == GRID_FOURIER )
            {
            dimID = ncgrid[index].ncIDs[CDF_DIMID_X];
            goto dimIDEstablished;
            }
        }
    }

{
    static const char axisname[] = "nc2";
    size_t dimlen = 2;
    int fileID  = streamptr->fileID;

    if ( streamptr->ncmode == 2 ) cdf_redef(fileID);
    cdf_def_dim(fileID, axisname, dimlen, &dimID);
    cdf_enddef(fileID);
    streamptr->ncmode = 2;
}
dimIDEstablished:
ncgrid[gridindex].gridID = gridID;
ncgrid[gridindex].ncIDs[CDF_DIMID_X] = dimID;
}

struct idSearch
{
int numNonMatching, foundID;
size_t foundIdx;
};

static inline struct idSearch
cdfSearchIDBySize(size_t startIdx, size_t numIDs, const ncgrid_t ncgrid[ ],
                int ncIDType, int searchType, int searchSize,
                int (*typeInq)(int id), size_t (*sizeInq)(int id))
{
int numNonMatching = 0,
    foundID = CDI_UNDEFID;
size_t foundIdx = SIZE_MAX;
for ( size_t index = startIdx; index < numIDs; index++ )
    {
    if ( ncgrid[index].ncIDs[ncIDType] != CDI_UNDEFID )
        {
        int id0 = ncgrid[index].gridID,
            id0Type = typeInq(id0);
        if ( id0Type == searchType )
            {
            int size0 = sizeInq(id0);
            if ( searchSize == size0 )
                {
                foundID = ncgrid[index].ncIDs[ncIDType];
                foundIdx = index;
                break;
                }
            numNonMatching++;
            }
        }
    }
return (struct idSearch){ .numNonMatching = numNonMatching,
    .foundID = foundID, .foundIdx = foundIdx };
}

static size_t
cdfGridInqHalfSize(int gridID)
{
return gridInqSize(gridID)/2;
}


static void
cdfDefSPorFC(stream_t *streamptr, int gridID, int gridindex,
            char *restrict axisname, int gridRefType)
{
ncgrid_t *ncgrid = streamptr->ncgrid;

size_t dimlen = gridInqSize(gridID)/2;

int iz;
int dimID;
{
    struct idSearch search
    = cdfSearchIDBySize(0, (size_t)gridindex, ncgrid, CDF_DIMID_Y,
                        gridRefType, (int)dimlen,
                        gridInqType, cdfGridInqHalfSize);
    dimID = search.foundID;
    iz = search.numNonMatching;
}

if ( dimID == CDI_UNDEFID )
    {
    int fileID  = streamptr->fileID;
    if ( iz == 0 ) axisname[3] = '\0';
    else           sprintf(&axisname[3], "%1d", iz+1);

    if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

    cdf_def_dim(fileID, axisname, dimlen, &dimID);

    cdf_enddef(fileID);
    streamptr->ncmode = 2;
    }

ncgrid[gridindex].gridID = gridID;
ncgrid[gridindex].ncIDs[CDF_DIMID_Y] = dimID;
}

static
void cdfDefSP(stream_t *streamptr, int gridID, int gridindex)
{

char axisname[5] = "nspX";
cdfDefSPorFC(streamptr, gridID, gridindex, axisname, GRID_SPECTRAL);
}


static
void cdfDefFC(stream_t *streamptr, int gridID, int gridindex)
{
char axisname[5] = "nfcX";
cdfDefSPorFC(streamptr, gridID, gridindex, axisname, GRID_FOURIER);
}

static const struct cdfDefGridAxisInqs {
size_t (*axisSize)(int gridID);
int (*axisDimname)(int cdiID, int key, int size, char *mesg);
int (*axisName)(int cdiID, int key, int size, char *mesg);
int (*axisLongname)(int cdiID, int key, int size, char *mesg);
int (*axisUnits)(int cdiID, int key, int size, char *mesg);
void (*axisStdname)(int cdiID, char *dimstdname);
double (*axisVal)(int gridID, size_t index);
const double *(*axisValsPtr)(int gridID);
const double *(*axisBoundsPtr)(int gridID);
} gridInqsX = {
.axisSize = gridInqXsize,
.axisDimname = cdiGridInqKeyStr,
.axisName = cdiGridInqKeyStr,
.axisLongname = cdiGridInqKeyStr,
.axisUnits = cdiGridInqKeyStr,
.axisStdname = gridInqXstdname,
.axisVal = gridInqXval,
.axisValsPtr = gridInqXvalsPtr,
.axisBoundsPtr = gridInqXboundsPtr,
}, gridInqsY = {
.axisSize = gridInqYsize,
.axisDimname = cdiGridInqKeyStr,
.axisName = cdiGridInqKeyStr,
.axisLongname = cdiGridInqKeyStr,
.axisUnits = cdiGridInqKeyStr,
.axisStdname = gridInqYstdname,
.axisVal = gridInqYval,
.axisValsPtr = gridInqYvalsPtr,
.axisBoundsPtr = gridInqYboundsPtr,
}, gridInqsZ = {
.axisLongname = cdiZaxisInqKeyStr,
.axisUnits = cdiZaxisInqKeyStr,
.axisStdname = zaxisInqStdname,
};

static
void cdfPutGridStdAtts(int fileID, int ncvarid, int gridID, int dimtype, const struct cdfDefGridAxisInqs *inqs)
{
size_t len;

char stdname[CDI_MAX_NAME];
inqs->axisStdname(gridID, stdname);
if ( (len = strlen(stdname)) )
    cdf_put_att_text(fileID, ncvarid, "standard_name", len, stdname);

char longname[CDI_MAX_NAME]; longname[0] = 0;
int keyname = (dimtype == 'Z') ? CDI_KEY_LONGNAME : (dimtype == 'X') ? CDI_KEY_XLONGNAME : CDI_KEY_YLONGNAME;
inqs->axisLongname(gridID, keyname, CDI_MAX_NAME, longname);
if ( longname[0] && (len = strlen(longname)) )
    cdf_put_att_text(fileID, ncvarid, "long_name", len, longname);

char units[CDI_MAX_NAME]; units[0] = 0;
keyname = (dimtype == 'Z') ? CDI_KEY_UNITS : (dimtype == 'X') ? CDI_KEY_XUNITS : CDI_KEY_YUNITS;
inqs->axisUnits(gridID, keyname, CDI_MAX_NAME, units);
if ( units[0] && (len = strlen(units)) )
    cdf_put_att_text(fileID, ncvarid, "units", len, units);
}

static void
cdfDefTrajLatLon(stream_t *streamptr, int gridID, int gridindex,
                const struct cdfDefGridAxisInqs *inqs, int dimtype)
{
nc_type xtype = (gridInqDatatype(gridID) == CDI_DATATYPE_FLT32) ? NC_FLOAT : NC_DOUBLE;
ncgrid_t *ncgrid = streamptr->ncgrid;

size_t dimlen = inqs->axisSize(gridID);
if ( dimlen != 1 )
    Error("%c size isn't 1 for %s grid!", dimtype, gridNamePtr(gridInqType(gridID)));

int ncvarid = ncgrid[gridindex].ncIDs[dimtype == 'X' ? CDF_DIMID_X : CDF_DIMID_Y];

if ( ncvarid == CDI_UNDEFID )
    {
    int dimNcID = streamptr->basetime.ncvarid;
    int fileID  = streamptr->fileID;
    if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

    char axisname[CDI_MAX_NAME]; axisname[0] = 0;
    int keyname = (dimtype == 'X') ? CDI_KEY_XNAME : CDI_KEY_YNAME;
    inqs->axisName(gridID, keyname, CDI_MAX_NAME, axisname);
    cdf_def_var(fileID, axisname, xtype, 1, &dimNcID, &ncvarid);
    cdfPutGridStdAtts(fileID, ncvarid, gridID, dimtype, inqs);
    cdf_enddef(fileID);
    streamptr->ncmode = 2;
    }

ncgrid[gridindex].gridID = gridID;

ncgrid[gridindex].ncIDs[dimtype == 'X' ? CDF_DIMID_X : CDF_DIMID_Y] = ncvarid;
}

static
void cdfDefTrajLon(stream_t *streamptr, int gridID, int gridindex)
{
cdfDefTrajLatLon(streamptr, gridID, gridindex, &gridInqsX, 'X');
}


static
void cdfDefTrajLat(stream_t *streamptr, int gridID, int gridindex)
{
cdfDefTrajLatLon(streamptr, gridID, gridindex, &gridInqsY, 'Y');
}

static
int checkDimName(int fileID, size_t dimlen, char *dimname)
{

unsigned iz = 0;
int dimid = CDI_UNDEFID;
char name[CDI_MAX_NAME];

size_t len = strlen(dimname);
memcpy(name, dimname, len + 1);

do
    {
    if ( iz ) sprintf(name + len, "_%u", iz+1);

    int dimid0, status = nc_inq_dimid(fileID, name, &dimid0);
    if ( status != NC_NOERR )
        break;
    size_t dimlen0;
    cdf_inq_dimlen(fileID, dimid0, &dimlen0);
    if ( dimlen0 == dimlen )
        {
        dimid = dimid0;
        break;
        }
    iz++;
    }
while ( iz <= 99 );


if ( iz ) sprintf(dimname + len, "_%u", iz+1);

return dimid;
}

static
void checkGridName(char *axisname, int fileID)
{
int ncdimid;
char axisname2[CDI_MAX_NAME];


unsigned iz = 0;

size_t axisnameLen = strlen(axisname);
memcpy(axisname2, axisname, axisnameLen + 1);

do
    {
    if ( iz ) sprintf(axisname2 + axisnameLen, "_%u", iz+1);

    if ( nc_inq_varid(fileID, axisname2, &ncdimid) != NC_NOERR ) break;

    ++iz;
    }
while ( iz <= 99 );

if ( iz ) sprintf(axisname + axisnameLen, "_%u", iz+1);
}

static
int checkZaxisName(char *axisname, int fileID, int vlistID, int zaxisID, int nzaxis)
{
char axisname2[CDI_MAX_NAME];


unsigned iz = 0;

size_t axisnameLen = strlen(axisname);
memcpy(axisname2, axisname, axisnameLen + 1);
do
    {
    if ( iz ) sprintf(axisname2 + axisnameLen, "_%u", iz+1);

    int ncdimid, status = nc_inq_varid(fileID, axisname2, &ncdimid);

    if ( status != NC_NOERR )
        {
        if ( iz )
            {

            for ( int index = 0; index < nzaxis; index++ )
                {
                int zaxisID0 = vlistZaxis(vlistID, index);
                if ( zaxisID != zaxisID0 )
                    {
                    const char *axisname0 = zaxisInqNamePtr(zaxisID0);
                    if ( strcmp(axisname0, axisname2) == 0 ) goto nextSuffix;
                    }
                }
            }
        break;
        }
    nextSuffix:
    ++iz;
    }
while (iz <= 99);


if ( iz ) sprintf(axisname + axisnameLen, "_%u", iz+1);

return (int)iz;
}

static void
cdfDefAxisCommon(stream_t *streamptr, int gridID, int gridindex, int ndims,
                const struct cdfDefGridAxisInqs *gridAxisInq, int dimKey, char axisLetter,
                void (*finishCyclicBounds)(double *pbounds, size_t dimlen, const double *pvals))
{
int dimID = CDI_UNDEFID;
int ncvarid = CDI_UNDEFID, ncbvarid = CDI_UNDEFID;
int nvdimID = CDI_UNDEFID;
int fileID  = streamptr->fileID;
size_t dimlen = gridAxisInq->axisSize(gridID);
nc_type xtype = (nc_type)cdfDefDatatype(gridInqDatatype(gridID), streamptr);

ncgrid_t *ncgrid = streamptr->ncgrid;

const double *pvals = gridAxisInq->axisValsPtr(gridID);
char dimname[CDI_MAX_NAME+3]; dimname[0] = 0;
if ( ndims && pvals == NULL ) cdiGridInqKeyStr(gridID, dimKey, CDI_MAX_NAME, dimname);

for ( int index = 0; index < gridindex; ++index )
    {
    int gridID0 = ncgrid[index].gridID;
    assert(gridID0 != CDI_UNDEFID);
    int gridtype0 = gridInqType(gridID0);
    if ( gridtype0 == GRID_GAUSSIAN    ||
        gridtype0 == GRID_LONLAT      ||
        gridtype0 == GRID_PROJECTION  ||
        gridtype0 == GRID_CURVILINEAR ||
        gridtype0 == GRID_GENERIC )
        {
        size_t dimlen0 = gridAxisInq->axisSize(gridID0);
        char dimname0[CDI_MAX_NAME]; dimname0[0] = 0;
        if ( dimname[0] ) cdiGridInqKeyStr(gridID0, dimKey, CDI_MAX_NAME, dimname0);
        bool lname = dimname0[0] ? strcmp(dimname, dimname0) == 0 : true;
        if ( dimlen == dimlen0 && lname )
            {
            double (*inqVal)(int gridID, size_t index) = gridAxisInq->axisVal;
            if ( IS_EQUAL(inqVal(gridID0, 0), inqVal(gridID, 0)) &&
                IS_EQUAL(inqVal(gridID0, dimlen-1), inqVal(gridID, dimlen-1)) )
                {
                dimID = ncgrid[index].ncIDs[dimKey == CDI_KEY_XDIMNAME
                                            ? CDF_DIMID_X : CDF_DIMID_Y];
                break;
                }
            }
        }
    }

if ( dimID == CDI_UNDEFID )
    {
    char axisname[CDI_MAX_NAME]; axisname[0] = 0;
    int keyname = (axisLetter == 'X') ? CDI_KEY_XNAME : CDI_KEY_YNAME;
    gridAxisInq->axisName(gridID, keyname, CDI_MAX_NAME, axisname);
    if ( axisname[0] == 0 ) Error("axis name undefined!");

    checkGridName(axisname, fileID);
    size_t axisnameLen = strlen(axisname);

    if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

    if ( ndims )
        {
        if ( dimname[0] == 0 ) strcpy(dimname, axisname);
        dimID = checkDimName(fileID, dimlen, dimname);

        if ( dimID == CDI_UNDEFID ) cdf_def_dim(fileID, dimname, dimlen, &dimID);
        }

    bool gen_bounds = false;
    bool grid_is_cyclic = gridIsCircular(gridID) > 0;
    double *pbounds = NULL;
    if ( pvals )
        {
        cdf_def_var(fileID, axisname, xtype, ndims, &dimID, &ncvarid);

        cdfPutGridStdAtts(fileID, ncvarid, gridID, axisLetter, gridAxisInq);
        {
            char axisStr[2] = { axisLetter, '\0' };
            cdf_put_att_text(fileID, ncvarid, "axis", 1, axisStr);
        }

        size_t nvertex = gridInqNvertex(gridID);
        pbounds = (double *)gridAxisInq->axisBoundsPtr(gridID);

        if ( CDI_cmor_mode && grid_is_cyclic && !pbounds )
            {
            gen_bounds = true;
            nvertex = 2;
            pbounds = (double*) Malloc(2*dimlen*sizeof(double));
            for ( size_t i = 0; i < dimlen-1; ++i )
                {
                pbounds[i*2+1]   = (pvals[i] + pvals[i+1])/2;
                pbounds[(i+1)*2] = (pvals[i] + pvals[i+1])/2;
                }
            finishCyclicBounds(pbounds, dimlen, pvals);
            }
        if ( pbounds )
            {
            if ( nc_inq_dimid(fileID, bndsName, &nvdimID) != NC_NOERR )
                cdf_def_dim(fileID, bndsName, nvertex, &nvdimID);
            }
        if ( pbounds && nvdimID != CDI_UNDEFID )
            {
            char boundsname[CDI_MAX_NAME];
            memcpy(boundsname, axisname, axisnameLen);
            boundsname[axisnameLen] = '_';
            memcpy(boundsname + axisnameLen + 1, bndsName, sizeof(bndsName));
            int dimIDs[2] = { dimID, nvdimID };
            cdf_def_var(fileID, boundsname, xtype, 2, dimIDs, &ncbvarid);
            cdf_put_att_text(fileID, ncvarid, "bounds", axisnameLen + sizeof(bndsName), boundsname);
            }
        }

    cdf_enddef(fileID);
    streamptr->ncmode = 2;

    if ( ncvarid  != CDI_UNDEFID ) cdf_put_var_double(fileID, ncvarid, pvals);
    if ( ncbvarid != CDI_UNDEFID ) cdf_put_var_double(fileID, ncbvarid, pbounds);
    if ( gen_bounds ) Free(pbounds);

    if ( ndims == 0 )
        ncgrid[gridindex].ncIDs[dimKey == CDI_KEY_XDIMNAME
                                ? CDF_VARID_X : CDF_VARID_Y] = ncvarid;
    }

ncgrid[gridindex].gridID = gridID;
ncgrid[gridindex].ncIDs[dimKey == CDI_KEY_XDIMNAME
                        ? CDF_DIMID_X : CDF_DIMID_Y] = dimID;
}

static
void finishCyclicXBounds(double *pbounds, size_t dimlen, const double *pvals)
{
pbounds[0] = (pvals[0] + pvals[dimlen-1]-360)*0.5;
pbounds[2*dimlen-1] = (pvals[dimlen-1] + pvals[0]+360)*0.5;
}

static
void cdfDefXaxis(stream_t *streamptr, int gridID, int gridindex, int ndims)
{
cdfDefAxisCommon(streamptr, gridID, gridindex, ndims, &gridInqsX,
                CDI_KEY_XDIMNAME, 'X', finishCyclicXBounds);
}

static
void finishCyclicYBounds(double *pbounds, size_t dimlen, const double *pvals)
{
pbounds[0] = copysign(90.0, pvals[0]);
pbounds[2*dimlen-1] = copysign(90.0, pvals[dimlen-1]);
}

static
void cdfDefYaxis(stream_t *streamptr, int gridID, int gridindex, int ndims)
{
cdfDefAxisCommon(streamptr, gridID, gridindex, ndims, &gridInqsY,
                CDI_KEY_YDIMNAME, 'Y', finishCyclicYBounds);
}

static
void cdfGridCompress(int fileID, int ncvarid, size_t gridsize, int filetype, int comptype)
{
#if  defined  (HAVE_NETCDF4)
if ( gridsize > 1 && comptype == CDI_COMPRESS_ZIP && (filetype == CDI_FILETYPE_NC4 || filetype == CDI_FILETYPE_NC4C) )
    {
    cdf_def_var_chunking(fileID, ncvarid, NC_CHUNKED, NULL);
    cdfDefVarDeflate(fileID, ncvarid, 1);
    }
#endif
}

static
void cdfDefGridReference(stream_t *streamptr, int gridID)
{
int fileID  = streamptr->fileID;
int number = gridInqNumber(gridID);

if ( number > 0 )
    {
    cdf_put_att_int(fileID, NC_GLOBAL, "number_of_grid_used", NC_INT, 1, &number);
    }

const char *gridfile = gridInqReferencePtr(gridID);
if ( gridfile && gridfile[0] != 0 )
    cdf_put_att_text(fileID, NC_GLOBAL, "grid_file_uri", strlen(gridfile), gridfile);
}

static
void cdfDefGridUUID(stream_t *streamptr, int gridID)
{
unsigned char uuidOfHGrid[CDI_UUID_SIZE];

gridInqUUID(gridID, uuidOfHGrid);
if ( !cdiUUIDIsNull(uuidOfHGrid) )
    {
    char uuidOfHGridStr[37];
    cdiUUID2Str(uuidOfHGrid, uuidOfHGridStr);
    if ( uuidOfHGridStr[0] != 0 && strlen(uuidOfHGridStr) == 36 )
        {
        int fileID  = streamptr->fileID;

        cdf_put_att_text(fileID, NC_GLOBAL, "uuidOfHGrid", 36, uuidOfHGridStr);

        }
    }
}

struct cdfDefIrregularGridCommonIDs
{
int xdimID, ydimID, ncxvarid, ncyvarid, ncavarid;
};

static struct cdfDefIrregularGridCommonIDs
cdfDefIrregularGridCommon(stream_t *streamptr, int gridID,
                        size_t xdimlen, size_t ydimlen,
                        int ndims, const char *xdimname_default,
                        size_t nvertex, const char *vdimname_default,
                        bool setVdimname)
{
nc_type xtype = (nc_type)cdfDefDatatype(gridInqDatatype(gridID), streamptr);
int xdimID = CDI_UNDEFID;
int ydimID = CDI_UNDEFID;
int ncxvarid = CDI_UNDEFID, ncyvarid = CDI_UNDEFID, ncavarid = CDI_UNDEFID;
int ncbxvarid = CDI_UNDEFID, ncbyvarid = CDI_UNDEFID;
int fileID  = streamptr->fileID;
if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

{
    char xdimname[CDI_MAX_NAME+3];
    xdimname[0] = 0;
    cdiGridInqKeyStr(gridID, CDI_KEY_XDIMNAME, CDI_MAX_NAME, xdimname);
    if ( xdimname[0] == 0 ) strcpy(xdimname, xdimname_default);
    xdimID = checkDimName(fileID, xdimlen, xdimname);
    if ( xdimID == CDI_UNDEFID ) cdf_def_dim(fileID, xdimname, xdimlen, &xdimID);
}

if ( ndims == 3 )
    {
    char ydimname[CDI_MAX_NAME+3];
    ydimname[0] = 0;
    cdiGridInqKeyStr(gridID, CDI_KEY_YDIMNAME, CDI_MAX_NAME, ydimname);
    if ( ydimname[0] == 0 ) { ydimname[0] = 'y'; ydimname[1] = 0; }
    ydimID = checkDimName(fileID, ydimlen, ydimname);
    if ( ydimID == CDI_UNDEFID ) cdf_def_dim(fileID, ydimname, ydimlen, &ydimID);
    }

int nvdimID = CDI_UNDEFID;
int dimIDs[3];
dimIDs[ndims-1] = CDI_UNDEFID;
if ( setVdimname )
    {
    char vdimname[CDI_MAX_NAME+3]; vdimname[0] = 0;
    cdiGridInqKeyStr(gridID, CDI_KEY_VDIMNAME, CDI_MAX_NAME, vdimname);
    if ( vdimname[0] == 0 ) strcpy(vdimname, vdimname_default);
    nvdimID = dimIDs[ndims-1] = checkDimName(fileID, nvertex, vdimname);
    if ( nvdimID == CDI_UNDEFID )
        {
        cdf_def_dim(fileID, vdimname, nvertex, dimIDs+ndims-1);
        nvdimID = dimIDs[ndims-1];
        }
    }

if ( ndims == 3 )
    {
    dimIDs[0] = ydimID;
    dimIDs[1] = xdimID;
    }
else
    {
    dimIDs[0] = xdimID;
    cdfDefGridReference(streamptr, gridID);
    cdfDefGridUUID(streamptr, gridID);
    }

const double *xvalsPtr = gridInqXvalsPtr(gridID),
    *xboundsPtr = NULL;
if ( xvalsPtr )
    {
    char xaxisname[CDI_MAX_NAME]; xaxisname[0] = 0;
    cdiGridInqKeyStr(gridID, CDI_KEY_XNAME, CDI_MAX_NAME, xaxisname);
    checkGridName(xaxisname, fileID);
    cdf_def_var(fileID, xaxisname, xtype, ndims-1, dimIDs, &ncxvarid);
    cdfGridCompress(fileID, ncxvarid, xdimlen*ydimlen, streamptr->filetype, streamptr->comptype);

    cdfPutGridStdAtts(fileID, ncxvarid, gridID, 'X', &gridInqsX);


    if ( ndims == 3 )
        cdf_put_att_text(fileID, ncxvarid, "_CoordinateAxisType", 3, "Lon");

    if ( (xboundsPtr = gridInqXboundsPtr(gridID)) && nvdimID != CDI_UNDEFID )
        {
        size_t xaxisnameLen = strlen(xaxisname);
        xaxisname[xaxisnameLen] = '_';
        memcpy(xaxisname + xaxisnameLen + 1, bndsName, sizeof (bndsName));
        cdf_def_var(fileID, xaxisname, xtype, ndims, dimIDs, &ncbxvarid);
        cdfGridCompress(fileID, ncbxvarid, xdimlen*ydimlen, streamptr->filetype, streamptr->comptype);

        cdf_put_att_text(fileID, ncxvarid, "bounds", xaxisnameLen + sizeof (bndsName), xaxisname);
        }
    }

const double *yvalsPtr = gridInqYvalsPtr(gridID),
    *yboundsPtr = NULL;
if ( yvalsPtr )
    {
    char yaxisname[CDI_MAX_NAME];
    gridInqYname(gridID, yaxisname);
    checkGridName(yaxisname, fileID);

    cdf_def_var(fileID, yaxisname, xtype, ndims - 1, dimIDs, &ncyvarid);
    cdfGridCompress(fileID, ncyvarid, xdimlen*ydimlen, streamptr->filetype, streamptr->comptype);

    cdfPutGridStdAtts(fileID, ncyvarid, gridID, 'Y', &gridInqsY);


    if ( ndims == 3 )
        cdf_put_att_text(fileID, ncyvarid, "_CoordinateAxisType", 3, "Lat");

    if ( (yboundsPtr = gridInqYboundsPtr(gridID)) && nvdimID != CDI_UNDEFID )
        {
        size_t yaxisnameLen = strlen(yaxisname);
        yaxisname[yaxisnameLen] = '_';
        memcpy(yaxisname + yaxisnameLen + 1, bndsName, sizeof (bndsName));
        cdf_def_var(fileID, yaxisname, xtype, ndims, dimIDs, &ncbyvarid);
        cdfGridCompress(fileID, ncbyvarid, xdimlen*ydimlen, streamptr->filetype, streamptr->comptype);

        cdf_put_att_text(fileID, ncyvarid, "bounds", yaxisnameLen + sizeof (bndsName), yaxisname);
        }
    }

const double *areaPtr = gridInqAreaPtr(gridID);
if ( areaPtr )
    {
    static const char yaxisname_[] = "cell_area";
    static const char units[] = "m2";
    static const char longname[] = "area of grid cell";
    static const char stdname[] = "cell_area";

    cdf_def_var(fileID, yaxisname_, xtype, ndims-1, dimIDs, &ncavarid);

    cdf_put_att_text(fileID, ncavarid, "standard_name", sizeof (stdname) - 1, stdname);
    cdf_put_att_text(fileID, ncavarid, "long_name", sizeof (longname) - 1, longname);
    cdf_put_att_text(fileID, ncavarid, "units", sizeof (units) - 1, units);
    }

cdf_enddef(fileID);
streamptr->ncmode = 2;

if ( ncxvarid  != CDI_UNDEFID ) cdf_put_var_double(fileID, ncxvarid,  xvalsPtr);
if ( ncbxvarid != CDI_UNDEFID ) cdf_put_var_double(fileID, ncbxvarid, xboundsPtr);
if ( ncyvarid  != CDI_UNDEFID ) cdf_put_var_double(fileID, ncyvarid,  yvalsPtr);
if ( ncbyvarid != CDI_UNDEFID ) cdf_put_var_double(fileID, ncbyvarid, yboundsPtr);
if ( ncavarid  != CDI_UNDEFID ) cdf_put_var_double(fileID, ncavarid,  areaPtr);

return (struct cdfDefIrregularGridCommonIDs) {
    .xdimID=xdimID, .ydimID = ydimID,
    .ncxvarid=ncxvarid, .ncyvarid=ncyvarid, .ncavarid=ncavarid
};
}

static
void cdfDefCurvilinear(stream_t *streamptr, int gridID, int gridindex)
{
ncgrid_t *ncgrid = streamptr->ncgrid;

size_t dimlen = gridInqSize(gridID);
size_t xdimlen = gridInqXsize(gridID);
size_t ydimlen = gridInqYsize(gridID);

int xdimID = CDI_UNDEFID, ydimID = CDI_UNDEFID;
int ncxvarid = CDI_UNDEFID, ncyvarid = CDI_UNDEFID, ncavarid = CDI_UNDEFID;
{
    size_t ofs = 0;
    do {
    struct idSearch search
        = cdfSearchIDBySize(ofs, (size_t)gridindex, ncgrid, CDF_DIMID_X,
                            GRID_CURVILINEAR, (int)dimlen,
                            gridInqType, gridInqSize);
    size_t index = search.foundIdx;
    if ( index != SIZE_MAX )
        {
        int gridID0 = ncgrid[index].gridID;
        if (    IS_EQUAL(gridInqXval(gridID0, 0), gridInqXval(gridID, 0))
            && IS_EQUAL(gridInqXval(gridID0, dimlen-1),
                        gridInqXval(gridID, dimlen-1))
            && IS_EQUAL(gridInqYval(gridID0, 0), gridInqYval(gridID, 0))
            && IS_EQUAL(gridInqYval(gridID0, dimlen-1),
                        gridInqYval(gridID, dimlen-1)) )
            {
            xdimID = ncgrid[index].ncIDs[CDF_DIMID_X];
            ydimID = ncgrid[index].ncIDs[CDF_DIMID_Y];
            ncxvarid = ncgrid[index].ncIDs[CDF_VARID_X];
            ncyvarid = ncgrid[index].ncIDs[CDF_VARID_Y];
            break;
            }
        ofs = search.foundIdx;
        if ( ofs < (size_t)gridindex )
            continue;
        }
    } while (false);
}

if ( xdimID == CDI_UNDEFID || ydimID == CDI_UNDEFID )
    {
    struct cdfDefIrregularGridCommonIDs createdIDs
        = cdfDefIrregularGridCommon(streamptr, gridID,
                                    xdimlen, ydimlen, 3, "x", 4, "nv4",
                                    gridInqXboundsPtr(gridID)
                                    || gridInqYboundsPtr(gridID));
    xdimID = createdIDs.xdimID;
    ydimID = createdIDs.ydimID;
    ncxvarid = createdIDs.ncxvarid;
    ncyvarid = createdIDs.ncyvarid;
    ncavarid = createdIDs.ncavarid;
    }

ncgrid[gridindex].gridID = gridID;
ncgrid[gridindex].ncIDs[CDF_DIMID_X] = xdimID;
ncgrid[gridindex].ncIDs[CDF_DIMID_Y] = ydimID;
ncgrid[gridindex].ncIDs[CDF_VARID_X] = ncxvarid;
ncgrid[gridindex].ncIDs[CDF_VARID_Y] = ncyvarid;
ncgrid[gridindex].ncIDs[CDF_VARID_A] = ncavarid;
}


static
void cdfDefUnstructured(stream_t *streamptr, int gridID, int gridindex)
{
ncgrid_t *ncgrid = streamptr->ncgrid;

size_t dimlen = gridInqSize(gridID);

int dimID = CDI_UNDEFID;
int ncxvarid = CDI_UNDEFID, ncyvarid = CDI_UNDEFID, ncavarid = CDI_UNDEFID;
{
    size_t ofs = 0;
    do {
    struct idSearch search
        = cdfSearchIDBySize(ofs, (size_t)gridindex, ncgrid, CDF_DIMID_X,
                            GRID_UNSTRUCTURED, (int)dimlen,
                            gridInqType, gridInqSize);
    size_t index = search.foundIdx;
    if ( index != SIZE_MAX )
        {
        int gridID0 = ncgrid[index].gridID;
        if ( gridInqNvertex(gridID0) == gridInqNvertex(gridID) &&
            IS_EQUAL(gridInqXval(gridID0, 0), gridInqXval(gridID, 0)) &&
            IS_EQUAL(gridInqXval(gridID0, dimlen-1),
                        gridInqXval(gridID, dimlen-1)) &&
            IS_EQUAL(gridInqYval(gridID0, 0), gridInqYval(gridID, 0)) &&
            IS_EQUAL(gridInqYval(gridID0, dimlen-1),
                        gridInqYval(gridID, dimlen-1)) )
            {
            dimID = ncgrid[index].ncIDs[CDF_DIMID_X];
            ncxvarid = ncgrid[index].ncIDs[CDF_VARID_X];
            ncyvarid = ncgrid[index].ncIDs[CDF_VARID_Y];
            ncavarid = ncgrid[index].ncIDs[CDF_VARID_A];
            break;
            }
        ofs = search.foundIdx;
        if ( ofs < (size_t)gridindex )
            continue;
        }
    } while (false);
}

if ( dimID == CDI_UNDEFID )
    {
    size_t nvertex = (size_t)gridInqNvertex(gridID);
    struct cdfDefIrregularGridCommonIDs createdIDs
        = cdfDefIrregularGridCommon(streamptr, gridID,
                                    dimlen, 1, 2, "ncells",
                                    nvertex, "vertices", nvertex > 0);
    dimID = createdIDs.xdimID;
    ncxvarid = createdIDs.ncxvarid;
    ncyvarid = createdIDs.ncyvarid;
    ncavarid = createdIDs.ncavarid;
    }

ncgrid[gridindex].gridID = gridID;
ncgrid[gridindex].ncIDs[CDF_DIMID_X] = dimID;
ncgrid[gridindex].ncIDs[CDF_VARID_X] = ncxvarid;
ncgrid[gridindex].ncIDs[CDF_VARID_Y] = ncyvarid;
ncgrid[gridindex].ncIDs[CDF_VARID_A] = ncavarid;
}

struct attTxtTab2
{
const char *attName, *attVal;
size_t valLen;
};

static
void cdf_def_vct_echam(stream_t *streamptr, int zaxisID)
{
int type = zaxisInqType(zaxisID);

if ( type == ZAXIS_HYBRID || type == ZAXIS_HYBRID_HALF )
    {
    int ilev = zaxisInqVctSize(zaxisID)/2;
    if ( ilev == 0 ) return;

    int mlev = ilev - 1;

    if ( streamptr->vct.ilev > 0 )
        {
        if ( streamptr->vct.ilev != ilev )
            Error("More than one VCT for each file unsupported!");
        return;
        }

    int fileID = streamptr->fileID;

    if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

    int ncdimid = -1, ncdimid2 = -1;
    int hyaiid, hybiid, hyamid = -1, hybmid = -1;

    cdf_def_dim(fileID, "nhyi", (size_t)ilev, &ncdimid2);
    cdf_def_var(fileID, "hyai", NC_DOUBLE, 1, &ncdimid2, &hyaiid);
    cdf_def_var(fileID, "hybi", NC_DOUBLE, 1, &ncdimid2, &hybiid);
    if ( mlev > 0 )
        {
        cdf_def_dim(fileID, "nhym", (size_t)mlev, &ncdimid);
        cdf_def_var(fileID, "hyam", NC_DOUBLE, 1, &ncdimid,  &hyamid);
        cdf_def_var(fileID, "hybm", NC_DOUBLE, 1, &ncdimid,  &hybmid);
        }

    streamptr->vct.ilev   = ilev;
    streamptr->vct.mlev   = mlev;
    streamptr->vct.mlevID = ncdimid;
    streamptr->vct.ilevID = ncdimid2;

    {
        static const char lname_n[] = "long_name",
        units_n[] = "units",
        lname_v_ai[] = "hybrid A coefficient at layer interfaces",
        units_v_ai[] = "Pa",
        lname_v_bi[] = "hybrid B coefficient at layer interfaces",
        units_v_bi[] = "1";
        static const struct attTxtTab2 tab[]
        = {
        { lname_n, lname_v_ai, sizeof (lname_v_ai) - 1 },
        { units_n, units_v_ai, sizeof (units_v_ai) - 1 },
        { lname_n, lname_v_bi, sizeof (lname_v_bi) - 1 },
        { units_n, units_v_bi, sizeof (units_v_bi) - 1 },
        };
        enum { tabLen = sizeof (tab) / sizeof (tab[0]) };
        int ids[tabLen] = { hyaiid, hyaiid, hybiid, hybiid };
        for ( size_t i = 0; i < tabLen; ++i )
        cdf_put_att_text(fileID, ids[i], tab[i].attName, tab[i].valLen, tab[i].attVal);
    }

    {
        static const char lname_n[] = "long_name",
        units_n[] = "units",
        lname_v_am[] = "hybrid A coefficient at layer midpoints",
        units_v_am[] = "Pa",
        lname_v_bm[] = "hybrid B coefficient at layer midpoints",
        units_v_bm[] = "1";
        static const struct attTxtTab2 tab[]
        = {
        { lname_n, lname_v_am, sizeof (lname_v_am) - 1 },
        { units_n, units_v_am, sizeof (units_v_am) - 1 },
        { lname_n, lname_v_bm, sizeof (lname_v_bm) - 1 },
        { units_n, units_v_bm, sizeof (units_v_bm) - 1 },
        };
        enum { tabLen = sizeof (tab) / sizeof (tab[0]) };
        int ids[tabLen] = { hyamid, hyamid, hybmid, hybmid };
        for ( size_t i = 0; i < tabLen; ++i )
        cdf_put_att_text(fileID, ids[i], tab[i].attName, tab[i].valLen, tab[i].attVal);
    }

    cdf_enddef(fileID);
    streamptr->ncmode = 2;

    const double *vctptr = zaxisInqVctPtr(zaxisID);

    cdf_put_var_double(fileID, hyaiid, vctptr);
    cdf_put_var_double(fileID, hybiid, vctptr+ilev);

    size_t start;
    size_t count = 1;
    double mval;
    for ( int i = 0; i < mlev; i++ )
        {
        start = (size_t)i;
        mval = (vctptr[i] + vctptr[i+1]) * 0.5;
        cdf_put_vara_double(fileID, hyamid, &start, &count, &mval);
        mval = (vctptr[ilev+i] + vctptr[ilev+i+1]) * 0.5;
        cdf_put_vara_double(fileID, hybmid, &start, &count, &mval);
        }
    }
}

static
void cdf_def_vct_cf(stream_t *streamptr, int zaxisID, int nclevID, int ncbndsID, int p0status, double p0value)
{
int type = zaxisInqType(zaxisID);

if ( type == ZAXIS_HYBRID || type == ZAXIS_HYBRID_HALF )
    {
    int ilev = zaxisInqVctSize(zaxisID)/2;
    if ( ilev == 0 ) return;

    int mlev = ilev - 1;
    int hyaiid = 0, hybiid = 0, hyamid, hybmid;

    if ( streamptr->vct.ilev > 0 )
        {
        if ( streamptr->vct.ilev != ilev )
            Error("more than one VCT for each file unsupported!");
        return;
        }

    int fileID = streamptr->fileID;

    if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

    int dimIDs[2];
    dimIDs[0] = nclevID;
    dimIDs[1] = ncbndsID;

    streamptr->vct.mlev   = mlev;
    streamptr->vct.ilev   = ilev;
    streamptr->vct.mlevID = nclevID;
    streamptr->vct.ilevID = nclevID;

    if ( p0status == 0 )
        cdf_def_var(fileID, "a", NC_DOUBLE, 1, dimIDs,  &hyamid);
    else
        cdf_def_var(fileID, "ap", NC_DOUBLE, 1, dimIDs,  &hyamid);
    cdf_def_var(fileID, "b",  NC_DOUBLE, 1, dimIDs,  &hybmid);

    {
        static const char lname[] = "vertical coordinate formula term: ap(k)";
        cdf_put_att_text(fileID, hyamid, "long_name", sizeof (lname) - 1, lname);
    }
    {
        static const char units[] = "Pa";
        cdf_put_att_text(fileID, hyamid, "units", sizeof (units) - 1, units);
    }
    {
        static const char lname[] = "vertical coordinate formula term: b(k)";
        cdf_put_att_text(fileID, hybmid, "long_name", sizeof (lname) - 1, lname);
    }
    {
        static const char units[] = "1";
        cdf_put_att_text(fileID, hybmid, "units", sizeof (units) - 1, units);
    }

    if ( ncbndsID != -1 )
        {
        if ( p0status == 0 )
            cdf_def_var(fileID, "a_bnds", NC_DOUBLE, 2, dimIDs, &hyaiid);
        else
            cdf_def_var(fileID, "ap_bnds", NC_DOUBLE, 2, dimIDs, &hyaiid);
        cdf_def_var(fileID, "b_bnds",  NC_DOUBLE, 2, dimIDs, &hybiid);
        {
            static const char lname[] = "vertical coordinate formula term: ap(k+1/2)";
            cdf_put_att_text(fileID, hyaiid, "long_name", sizeof (lname) - 1, lname);
        }
        {
            static const char units[] = "Pa";
            cdf_put_att_text(fileID, hyaiid, "units", sizeof (units) - 1, units);
        }
        {
            static const char lname[] = "vertical coordinate formula term: b(k+1/2)";
            cdf_put_att_text(fileID, hybiid, "long_name", sizeof (lname) - 1, lname);
        }
        {
            static const char units[] = "1";
            cdf_put_att_text(fileID, hybiid, "units", sizeof (units) - 1, units);
        }
        }

    cdf_enddef(fileID);
    streamptr->ncmode = 2;

    int vctsize = zaxisInqVctSize(zaxisID);
    double *vct = (double*) malloc(vctsize*sizeof(double));;
    zaxisInqVct(zaxisID, vct);

    if ( p0status == 0 && IS_NOT_EQUAL(p0value,0) )
        for ( int i = 0; i < vctsize/2; ++i ) vct[i] /= p0value;

    double *tarray = (double*) malloc(ilev*2*sizeof(double));

    if ( ncbndsID != -1 )
        {
        for ( int i = 0; i < mlev; ++i )
            {
            tarray[2*i  ] = vct[i];
            tarray[2*i+1] = vct[i+1];
            }
        cdf_put_var_double(fileID, hyaiid, tarray);

        for ( int i = 0; i < mlev; ++i )
            {
            tarray[2*i  ] = vct[ilev+i];
            tarray[2*i+1] = vct[ilev+i+1];
            }
        cdf_put_var_double(fileID, hybiid, tarray);
        }

    for ( int i = 0; i < mlev; ++i )
        tarray[i] = (vct[i] + vct[i+1]) * 0.5;
    cdf_put_var_double(fileID, hyamid, tarray);

    for ( int i = 0; i < mlev; ++i )
        tarray[i] = (vct[ilev+i] + vct[ilev+i+1]) * 0.5;
    cdf_put_var_double(fileID, hybmid, tarray);

    free(tarray);
    free(vct);
    }
}

struct attTxtTab { const char *txt; size_t txtLen; };

static
void cdf_def_zaxis_hybrid_echam(stream_t *streamptr, int type, int *ncvaridp, int zaxisID, int zaxisindex, int xtype, size_t dimlen, int *dimID, char *axisname)
{
int fileID = streamptr->fileID;

if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

cdf_def_dim(fileID, axisname, dimlen, dimID);
cdf_def_var(fileID, axisname, (nc_type) xtype, 1, dimID,  ncvaridp);
int ncvarid = *ncvaridp;

{
    static const char sname[] = "hybrid_sigma_pressure";
    cdf_put_att_text(fileID, ncvarid, "standard_name", sizeof (sname) - 1, sname);
}
{
    static const char *attName[] = {
    "long_name",
    "formula",
    "formula_terms"
    };
    enum { nAtt = sizeof (attName) / sizeof (attName[0]) };
    static const char lname_m[] = "hybrid level at layer midpoints",
    formula_m[] = "hyam hybm (mlev=hyam+hybm*aps)",
    fterms_m[] = "ap: hyam b: hybm ps: aps",
    lname_i[] = "hybrid level at layer interfaces",
    formula_i[] = "hyai hybi (ilev=hyai+hybi*aps)",
    fterms_i[] = "ap: hyai b: hybi ps: aps";
    static const struct attTxtTab tab[2][nAtt] = {
    {
        { lname_i, sizeof (lname_i) - 1 },
        { formula_i, sizeof (formula_i) - 1 },
        { fterms_i, sizeof (fterms_i) - 1 }
    },
    {
        { lname_m, sizeof (lname_m) - 1 },
        { formula_m, sizeof (formula_m) - 1 },
        { fterms_m, sizeof (fterms_m) - 1 }
    }
    };

    size_t tabSelect = type == ZAXIS_HYBRID;
    for (size_t i = 0; i < nAtt; ++i)
    cdf_put_att_text(fileID, ncvarid, attName[i],
                    tab[tabSelect][i].txtLen, tab[tabSelect][i].txt);
}

{
    static const char units[] = "level";
    cdf_put_att_text(fileID, ncvarid, "units", sizeof (units) - 1, units);
}
{
    static const char direction[] = "down";
    cdf_put_att_text(fileID, ncvarid, "positive", sizeof (direction) - 1, direction);
}

cdf_enddef(fileID);
streamptr->ncmode = 2;

cdf_put_var_double(fileID, ncvarid, zaxisInqLevelsPtr(zaxisID));

cdf_def_vct_echam(streamptr, zaxisID);

if ( *dimID == CDI_UNDEFID )
    streamptr->zaxisID[zaxisindex] = type == ZAXIS_HYBRID
    ? streamptr->vct.mlevID : streamptr->vct.ilevID;
}

static
void cdf_def_zaxis_hybrid_cf(stream_t *streamptr, int type, int *ncvaridp, int zaxisID, int zaxisindex, int xtype, size_t dimlen, int *dimID, char *axisname)
{
int fileID = streamptr->fileID;
if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

char psname[CDI_MAX_NAME]; psname[0] = 0;
cdiZaxisInqKeyStr(zaxisID, CDI_KEY_PSNAME, CDI_MAX_NAME, psname);
if ( psname[0] == 0 ) strcpy(psname, "ps");

char p0name[CDI_MAX_NAME]; p0name[0] = 0;
double p0value = 1;
int p0varid = CDI_UNDEFID;
int p0status = cdiZaxisInqKeyFlt(zaxisID, CDI_KEY_P0VALUE, &p0value);
if ( p0status == 0 )
    {
    cdiZaxisInqKeyStr(zaxisID, CDI_KEY_P0NAME, CDI_MAX_NAME, p0name);
    if ( p0name[0] == 0 ) strcpy(p0name, "p0");
    cdf_def_var(fileID, p0name, NC_DOUBLE, 0, 0,  &p0varid);
    static const char longname[] = "reference pressure";
    cdf_put_att_text(fileID, p0varid, "long_name", strlen(longname), longname);
    static const char units[] = "Pa";
    cdf_put_att_text(fileID, p0varid, "units", strlen(units), units);
    }

char zname[CDI_MAX_NAME]; zname[0] = 0;
char zlongname[CDI_MAX_NAME]; zlongname[0] = 0;
char zunits[CDI_MAX_NAME]; zunits[0] = 0;
cdiZaxisInqKeyStr(zaxisID, CDI_KEY_NAME, CDI_MAX_NAME, zname);

cdiZaxisInqKeyStr(zaxisID, CDI_KEY_UNITS, CDI_MAX_NAME, zunits);
if ( zname[0] ) strcpy(axisname, zname);
if ( zlongname[0] == 0 ) strcpy(zlongname, "hybrid sigma pressure coordinate");
if ( zunits[0] == 0 ) strcpy(zunits, "1");

cdf_def_dim(fileID, axisname, dimlen, dimID);
cdf_def_var(fileID, axisname, (nc_type) xtype, 1, dimID, ncvaridp);
int ncvarid = *ncvaridp;

{
    static const char sname[] = "standard_name",
    sname_v[] = "atmosphere_hybrid_sigma_pressure_coordinate",
    axis[] = "axis",
    axis_v[] = "Z",
    direction[] = "positive",
    direction_v[] = "down";
    struct attTxtTab2 tab[] = {
    { sname, sname_v, sizeof (sname_v) - 1 },
    { axis, axis_v, sizeof (axis_v) - 1 },
    { direction, direction_v, sizeof (direction_v) - 1 },
    };
    enum { nAtt = sizeof (tab) / sizeof (tab[0]) };
    for ( size_t i = 0; i < nAtt; ++i )
    cdf_put_att_text(fileID, ncvarid, tab[i].attName, tab[i].valLen, tab[i].attVal);

    cdf_put_att_text(fileID, ncvarid, "long_name", strlen(zlongname), zlongname);
    cdf_put_att_text(fileID, ncvarid, "units", strlen(zunits), zunits);
}

size_t len = 0;
char txt[CDI_MAX_NAME];
if ( p0status == 0 )
    len = (size_t)(sprintf(txt, "%s%s %s%s", "a: a b: b p0: ", p0name, "ps: ", psname));
else
    len = (size_t)(sprintf(txt, "%s%s", "ap: ap b: b ps: ", psname));
cdf_put_att_text(fileID, ncvarid, "formula_terms", len, txt);

int ncbvarid = CDI_UNDEFID;
int nvdimID = CDI_UNDEFID;

double *buffer = (double *) malloc(4*dimlen*sizeof(double));
double *levels = buffer;
double *lbounds = buffer + 2*dimlen;
double *ubounds = buffer + 3*dimlen;

if ( zaxisInqLevels(zaxisID, NULL) )
    zaxisInqLevels(zaxisID, levels);
else
    for ( size_t i = 0; i < dimlen; ++i ) levels[i] = i+1;

if ( zaxisInqLbounds(zaxisID, NULL) && zaxisInqUbounds(zaxisID, NULL) )
    {
    zaxisInqLbounds(zaxisID, lbounds);
    zaxisInqUbounds(zaxisID, ubounds);
    }
else
    {
    for ( size_t i = 0; i < dimlen; ++i ) lbounds[i] = levels[i];
    for ( size_t i = 0; i < dimlen-1; ++i ) ubounds[i] = levels[i+1];
    ubounds[dimlen-1] = levels[dimlen-1] + 1;
    }


    {
    size_t nvertex = 2;
    if ( dimlen > 1 && nc_inq_dimid(fileID, bndsName, &nvdimID) != NC_NOERR )
        cdf_def_dim(fileID, bndsName, nvertex, &nvdimID);

    if ( nvdimID != CDI_UNDEFID )
        {
        size_t axisnameLen = strlen(axisname);
        axisname[axisnameLen] = '_';
        memcpy(axisname + axisnameLen + 1, bndsName, sizeof (bndsName));
        axisnameLen += sizeof (bndsName);
        int dimIDs[2] = { *dimID, nvdimID };
        cdf_def_var(fileID, axisname, (nc_type) xtype, 2, dimIDs, &ncbvarid);
        cdf_put_att_text(fileID, ncvarid, "bounds", axisnameLen, axisname);
        {
            static const char sname[] = "standard_name",
            sname_v[] = "atmosphere_hybrid_sigma_pressure_coordinate";
            struct attTxtTab2 tab[] = {
            { sname, sname_v, sizeof (sname_v) - 1 },
            };
            enum { nAtt = sizeof (tab) / sizeof (tab[0]) };
            for ( size_t i = 0; i < nAtt; ++i )
            cdf_put_att_text(fileID, ncbvarid, tab[i].attName, tab[i].valLen, tab[i].attVal);
            cdf_put_att_text(fileID, ncbvarid, "units", strlen(zunits), zunits);
        }

        if ( p0status == 0 )
            len = (size_t)(sprintf(txt, "%s%s %s%s", "a: a_bnds b: b_bnds p0: ", p0name, "ps: ", psname));
        else
            len = (size_t)(sprintf(txt, "%s%s", "ap: ap_bnds b: b_bnds ps: ", psname));
        cdf_put_att_text(fileID, ncbvarid, "formula_terms", len, txt);
        }
    }

cdf_enddef(fileID);
streamptr->ncmode = 2;

cdf_put_var_double(fileID, ncvarid, levels);

if ( p0varid != CDI_UNDEFID ) cdf_put_var_double(fileID, p0varid, &p0value);

if ( ncbvarid != CDI_UNDEFID )
    {
    double *zbounds = buffer;
    for ( size_t i = 0; i < dimlen; ++i )
        {
        zbounds[2*i  ] = lbounds[i];
        zbounds[2*i+1] = ubounds[i];
        }
    cdf_put_var_double(fileID, ncbvarid, zbounds);
    }

cdf_def_vct_cf(streamptr, zaxisID, *dimID, nvdimID, p0status, p0value);

if ( *dimID == CDI_UNDEFID )
    streamptr->zaxisID[zaxisindex] = type == ZAXIS_HYBRID
    ? streamptr->vct.mlevID : streamptr->vct.ilevID;

free(buffer);
}

static
void cdf_def_zaxis_hybrid(stream_t *streamptr, int type, int *ncvarid, int zaxisID, int zaxisindex, int xtype, size_t dimlen, int *dimID, char *axisname)
{
void (*def_zaxis_hybrid_delegate)(stream_t *streamptr, int type, int *ncvarid, int zaxisID, int zaxisindex, int xtype, size_t dimlen, int *dimID, char *axisname)
    = ( (!CDI_cmor_mode && CDI_convention == CDI_CONVENTION_ECHAM)
        || type == ZAXIS_HYBRID_HALF )
    ? cdf_def_zaxis_hybrid_echam : cdf_def_zaxis_hybrid_cf;
def_zaxis_hybrid_delegate(streamptr, type, ncvarid, zaxisID, zaxisindex, xtype, dimlen, dimID, axisname);
}

static
void cdfDefZaxisUUID(stream_t *streamptr, int zaxisID)
{
unsigned char uuidOfVGrid[CDI_UUID_SIZE];
zaxisInqUUID(zaxisID, uuidOfVGrid);

if ( uuidOfVGrid[0] != 0 )
    {
    char uuidOfVGridStr[37];
    cdiUUID2Str(uuidOfVGrid, uuidOfVGridStr);
    if ( uuidOfVGridStr[0] != 0 && strlen(uuidOfVGridStr) == 36 )
        {
        int fileID  = streamptr->fileID;
        if ( streamptr->ncmode == 2 ) cdf_redef(fileID);
        cdf_put_att_text(fileID, NC_GLOBAL, "uuidOfVGrid", 36, uuidOfVGridStr);
        if ( streamptr->ncmode == 2 ) cdf_enddef(fileID);
        }
    }
}

static
void cdfDefZaxisChar(stream_t *streamptr, int zaxisID, char *axisname, int *dimID, size_t dimlen, int zaxisindex)
{
int fileID  = streamptr->fileID;
int ncvarID = CDI_UNDEFID;
if ( streamptr->ncmode == 2 ) cdf_redef(fileID);


char strlen[8] = "strlen\0";
size_t clen = (size_t) zaxisInqCLen(zaxisID);
if ( clen == 0 )
    Error("Maximal string length value is 0.\nA given character axis requires a dimension to save the maximal string length.");
int strlenID = CDI_UNDEFID;
strlenID = checkDimName(fileID, clen, strlen);

if ( strlenID == CDI_UNDEFID ) cdf_def_dim(fileID, strlen, clen, &strlenID);


char dimname[CDI_MAX_NAME+3]; dimname[0] = 0;
cdiZaxisInqKeyStr(zaxisID, CDI_KEY_DIMNAME, CDI_MAX_NAME, dimname);
*dimID = checkDimName(fileID, dimlen, dimname);
if ( !(dimlen > 0) )
    Error("No strings delivered for a character axis.");
if ( dimname[0] == 0 ) { memcpy(dimname, "area_type", 10); dimname[10] = 0; }

if ( *dimID == CDI_UNDEFID ) cdf_def_dim(fileID, dimname, dimlen, dimID);

int dimIDs[2];
dimIDs[0] = *dimID;
dimIDs[1] = strlenID;


char **cvals = zaxisInqCValsPtr(zaxisID);

if ( cvals )
    {

    cdf_def_var(fileID, axisname, NC_CHAR, 2, dimIDs, &ncvarID);

    cdfPutGridStdAtts(fileID, ncvarID, zaxisID, 'Z', &gridInqsZ);
    cdf_put_att_text(fileID, ncvarID, "axis", 1, "Z");
    cdfDefineAttributes(zaxisID, CDI_GLOBAL, fileID, ncvarID);

    streamptr->nczvarID[zaxisindex] = ncvarID;
    cdf_enddef(fileID);


    size_t start[2], count[2];
    start[1] = 0;
    count[0] = 1;
    count[1] = clen;
    for ( size_t i = 0; i < dimlen; i++ )
        {
        start[0] = i;
        nc_put_vara_text(fileID, ncvarID, start, count, cvals[i]);
        }
    }

streamptr->ncmode = 2;
}

static
void cdfDefZaxis(stream_t *streamptr, int zaxisID)
{

char axisname[CDI_MAX_NAME];
int dimID = CDI_UNDEFID;
int ncvarid = CDI_UNDEFID, ncbvarid = CDI_UNDEFID;
int xtype = zaxisInqDatatype(zaxisID) == CDI_DATATYPE_FLT32 ? NC_FLOAT : NC_DOUBLE;

int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;

int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);

int nzaxis = vlistNzaxis(vlistID);

size_t dimlen = (size_t)zaxisInqSize(zaxisID);
int type = zaxisInqType(zaxisID);

int ndims = 1;

if ( dimlen == 1 )
    {
    bool is_scalar = zaxisInqScalar(zaxisID) > 0;
    if ( !is_scalar && CDI_cmor_mode )
        {
        is_scalar = true;
        zaxisDefScalar(zaxisID);
        }

    if ( is_scalar ) ndims = 0;
    if ( CDI_reduce_dim ) return;

    switch (type)
        {
        case ZAXIS_SURFACE:
        case ZAXIS_CLOUD_BASE:
        case ZAXIS_CLOUD_TOP:
        case ZAXIS_ISOTHERM_ZERO:
        case ZAXIS_TOA:
        case ZAXIS_SEA_BOTTOM:
        case ZAXIS_ATMOSPHERE:
        case ZAXIS_MEANSEA:
        case ZAXIS_LAKE_BOTTOM:
        case ZAXIS_SEDIMENT_BOTTOM:
        case ZAXIS_SEDIMENT_BOTTOM_TA:
        case ZAXIS_SEDIMENT_BOTTOM_TW:
        case ZAXIS_MIX_LAYER:
        return;
        }
    }

zaxisInqName(zaxisID, axisname);

if ( dimID == CDI_UNDEFID )
    {
    checkZaxisName(axisname, fileID, vlistID, zaxisID, nzaxis);

    char dimname[CDI_MAX_NAME+3]; dimname[0] = 0;

    if ( dimname[0] == 0 ) strcpy(dimname, axisname);

    if ( type == ZAXIS_REFERENCE ) cdfDefZaxisUUID(streamptr, zaxisID);

    if ( type == ZAXIS_HYBRID || type == ZAXIS_HYBRID_HALF )
        {
        cdf_def_zaxis_hybrid(streamptr, type, &ncvarid, zaxisID, zaxisindex, xtype, dimlen, &dimID, axisname);

        int natts;
        cdiInqNatts(zaxisID, CDI_GLOBAL, &natts);
        if ( natts > 0 && streamptr->ncmode == 2 ) cdf_redef(fileID);
        cdfDefineAttributes(zaxisID, CDI_GLOBAL, fileID, ncvarid);
        if ( natts > 0 && streamptr->ncmode == 2 ) cdf_enddef(fileID);
        }
    else if ( type == ZAXIS_CHAR )
        cdfDefZaxisChar(streamptr, zaxisID, axisname, &dimID, dimlen, zaxisindex);
    else
        {
        dimID = checkDimName(fileID, dimlen, dimname);

        if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

        if ( ndims && dimID == CDI_UNDEFID ) cdf_def_dim(fileID, dimname, dimlen, &dimID);

        if ( zaxisInqLevels(zaxisID, NULL) )
            {
            cdf_def_var(fileID, axisname, (nc_type) xtype, ndims, &dimID, &ncvarid);

            cdfPutGridStdAtts(fileID, ncvarid, zaxisID, 'Z', &gridInqsZ);

            {
                int positive = zaxisInqPositive(zaxisID);
                static const char positive_up[] = "up",
                                positive_down[] = "down";
                static const struct attTxtTab tab[2] = {
                { positive_up, sizeof (positive_up) - 1 },
                { positive_down, sizeof (positive_down) - 1 },
                };
                if ( positive == POSITIVE_UP || positive == POSITIVE_DOWN )
                {
                    size_t select = positive == POSITIVE_DOWN;
                    cdf_put_att_text(fileID, ncvarid, "positive", tab[select].txtLen, tab[select].txt);
                }
            }
            cdf_put_att_text(fileID, ncvarid, "axis", 1, "Z");

            if ( zaxisInqLbounds(zaxisID, NULL) && zaxisInqUbounds(zaxisID, NULL) )
                {
                int nvdimID = CDI_UNDEFID;
                size_t nvertex = 2;
                if ( nc_inq_dimid(fileID, bndsName, &nvdimID) != NC_NOERR )
                    cdf_def_dim(fileID, bndsName, nvertex, &nvdimID);

                if ( nvdimID != CDI_UNDEFID )
                    {
                    size_t axisnameLen = strlen(axisname);
                    axisname[axisnameLen] = '_';
                    memcpy(axisname + axisnameLen + 1, bndsName, sizeof (bndsName));
                    int dimIDs[2];
                    dimIDs[0] = dimID;
                    dimIDs[ndims] = nvdimID;
                    cdf_def_var(fileID, axisname, (nc_type) xtype, ndims+1, dimIDs, &ncbvarid);
                    cdf_put_att_text(fileID, ncvarid, "bounds", strlen(axisname), axisname);
                    }
                }

            cdfDefineAttributes(zaxisID, CDI_GLOBAL, fileID, ncvarid);
            }

        cdf_enddef(fileID);
        streamptr->ncmode = 2;

        if ( zaxisInqLevels(zaxisID, NULL) )
            {
            cdf_put_var_double(fileID, ncvarid, zaxisInqLevelsPtr(zaxisID));

            if ( ncbvarid != CDI_UNDEFID )
                {
                double *buffer = (double *) malloc(4*dimlen*sizeof(double));
                double *zbounds = buffer;
                double *lbounds = buffer + 2*dimlen;
                double *ubounds = buffer + 3*dimlen;
                zaxisInqLbounds(zaxisID, lbounds);
                zaxisInqUbounds(zaxisID, ubounds);
                for ( size_t i = 0; i < dimlen; ++i )
                    {
                    zbounds[2*i  ] = lbounds[i];
                    zbounds[2*i+1] = ubounds[i];
                    }

                cdf_put_var_double(fileID, ncbvarid, zbounds);

                free(buffer);
                }

            if ( ndims == 0 ) streamptr->nczvarID[zaxisindex] = ncvarid;
            }
        }
    }

if ( dimID != CDI_UNDEFID )
    streamptr->zaxisID[zaxisindex] = dimID;
}

static
void cdf_def_mapping(stream_t *streamptr, int gridID)
{
char mapping[CDI_MAX_NAME]; mapping[0] = 0;
cdiGridInqKeyStr(gridID, CDI_KEY_MAPNAME, CDI_MAX_NAME, mapping);
if ( mapping[0] )
    {
    char gmapvarname[CDI_MAX_NAME]; gmapvarname[0] = 0;
    cdiGridInqKeyStr(gridID, CDI_KEY_MAPPING, CDI_MAX_NAME, gmapvarname);

    int fileID = streamptr->fileID;
    if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

    int ncvarid;
    int ncerrcode = nc_def_var(fileID, gmapvarname, (nc_type) NC_INT, 0, NULL, &ncvarid);
    if ( ncerrcode == NC_NOERR )
        cdfDefineAttributes(gridID, CDI_GLOBAL, fileID, ncvarid);

    cdf_enddef(fileID);

    if ( ncerrcode == NC_NOERR )
        {
        int dummy = 1;
        cdf_put_var_int(fileID, ncvarid, &dummy);
        }
    }
}

static
void cdfDefCharacter(stream_t *streamptr, int gridID, int gridindex, int xory, int strlen)
{
if ( streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X] != CDI_UNDEFID ) return;

size_t dimlen = ( xory == 0 ) ? gridInqXsize(gridID) : gridInqYsize(gridID);
int dimID, strlenID;
ncgrid_t *ncgrid = streamptr->ncgrid;



for ( int index = 0; index < gridindex; index++ )
    {
    int gridID0 = ncgrid[index].gridID;
    int gridtype0 = gridInqType(gridID0);
    if ( gridtype0 == GRID_CHARXY )
        {
        if ( gridInqXIsc(gridID0) == strlen &&
            gridInqXsize(gridID0) == dimlen )
            return;
        else if ( gridInqYIsc(gridID0) == strlen &&
            gridInqYsize(gridID0) == dimlen )
            return;
        }
    }

int fileID  = streamptr->fileID;

if ( streamptr->ncmode == 2 ) cdf_redef(fileID);



char dimname[CDI_MAX_NAME+3];
dimname[0] = 0;
if ( xory == 0 )
    cdiGridInqKeyStr(gridID, CDI_KEY_XDIMNAME, CDI_MAX_NAME, dimname);
else
    cdiGridInqKeyStr(gridID, CDI_KEY_YDIMNAME, CDI_MAX_NAME, dimname);
if ( dimname[0] == 0 ) { memcpy(dimname, "region", 7); dimname[6] = 0; }
dimID = checkDimName(fileID, dimlen, dimname);
if ( dimID == CDI_UNDEFID ) cdf_def_dim(fileID, dimname, dimlen, &dimID);



strcpy(dimname, "strlen");
strlenID = checkDimName(fileID, strlen, dimname);
if ( strlenID == CDI_UNDEFID ) cdf_def_dim(fileID, dimname, strlen, &strlenID);



int dimIDs[2];
dimIDs[0] = dimID;
dimIDs[1] = strlenID;

char axisname[CDI_MAX_NAME]; axisname[0] = 0;
char **cvals = (char **) Malloc(dimlen * sizeof(char *));
for ( size_t i = 0; i < dimlen; i++ )
    cvals[i] = (char*) Malloc(strlen * sizeof(char));
int ncaxisid;
if ( xory == 0 )
    {
    cdiGridInqKeyStr(gridID, CDI_KEY_XNAME, CDI_MAX_NAME, axisname);
    gridInqXCvals(gridID, cvals);
    }
else
    {
    cdiGridInqKeyStr(gridID, CDI_KEY_YNAME, CDI_MAX_NAME, axisname);
    gridInqXCvals(gridID, cvals);
    }
int status = nc_inq_varid(fileID, axisname, &ncaxisid);
if ( status != NC_NOERR )
    {
    cdf_def_var(fileID, axisname, NC_CHAR, 2, dimIDs, &ncaxisid);
    if ( xory == 0 )
        cdfPutGridStdAtts(fileID, ncaxisid, gridID, 'X', &gridInqsX);
    else
        cdfPutGridStdAtts(fileID, ncaxisid, gridID, 'Y', &gridInqsY);
    }
else
    return;
cdf_enddef(fileID);



size_t start[2], count[2];
start[1] = 0;
count[0] = 1;
count[1] = strlen;
for (size_t i = 0; i < dimlen; i++)
    {
    start[0] = i;
    status = nc_put_vara_text(fileID, ncaxisid, start, count, cvals[i]);
    }

ncgrid[gridindex].gridID = gridID;
if ( xory == 0 )
    {
    ncgrid[gridindex].ncIDs[CDF_DIMID_X] = dimID;
    ncgrid[gridindex].ncIDs[CDF_VARID_X] = ncaxisid;
    }
else
    {
    ncgrid[gridindex].ncIDs[CDF_DIMID_Y] = dimID;
    ncgrid[gridindex].ncIDs[CDF_VARID_Y] = ncaxisid;
    }
streamptr->ncmode = 2;
}

static
void cdfDefRgrid(stream_t *streamptr, int gridID, int gridindex)
{
ncgrid_t *ncgrid = streamptr->ncgrid;

size_t dimlen = gridInqSize(gridID);

int iz;
int dimID;
{
    struct idSearch search
    = cdfSearchIDBySize(0, (size_t)gridindex, ncgrid, CDF_DIMID_X,
                        GRID_GAUSSIAN_REDUCED, (int)dimlen,
                        gridInqType, gridInqSize);
    iz = search.numNonMatching;
    dimID = search.foundID;
}

if ( dimID == CDI_UNDEFID )
    {
    int fileID  = streamptr->fileID;
    static bool lwarn = true;
    if ( lwarn )
        {
        Warning("Creating a NetCDF file with data on a gaussian reduced grid.");
        Warning("The further processing of the resulting file is unsupported!");
        lwarn = false;
        }

    char axisname[16] = "rgridX";
    if ( iz == 0 ) axisname[5] = '\0';
    else           sprintf(&axisname[5], "%1d", iz+1);

    if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

    cdf_def_dim(fileID, axisname, dimlen, &dimID);

    cdf_enddef(fileID);
    streamptr->ncmode = 2;
    }

ncgrid[gridindex].gridID = gridID;
ncgrid[gridindex].ncIDs[CDF_DIMID_X] = dimID;
}

static
void cdfDefGdim(stream_t *streamptr, int gridID, int gridindex)
{
ncgrid_t *ncgrid = streamptr->ncgrid;
int iz = 0;
int dimID = CDI_UNDEFID;

size_t dimlen = gridInqSize(gridID);

if ( gridInqYsize(gridID) == 0 )
    {
    struct idSearch search
        = cdfSearchIDBySize(0, (size_t)gridindex, ncgrid, CDF_DIMID_X,
                            GRID_GENERIC, (int)dimlen,
                            gridInqType, gridInqSize);
    iz = search.numNonMatching;
    dimID = search.foundID;
    }

if ( gridInqXsize(gridID) == 0 )
    {
    struct idSearch search
        = cdfSearchIDBySize(0, (size_t)gridindex, ncgrid, CDF_DIMID_Y,
                            GRID_GENERIC, (int)dimlen,
                            gridInqType, gridInqSize);
    iz += search.numNonMatching;
    dimID = search.foundID;
    }

if ( dimID == CDI_UNDEFID )
    {
    int fileID  = streamptr->fileID;
    char dimname[CDI_MAX_NAME];
    strcpy(dimname, "gsize");

    dimID = checkDimName(fileID, dimlen, dimname);

    if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

    if ( dimID == CDI_UNDEFID ) cdf_def_dim(fileID, dimname, dimlen, &dimID);

    cdf_enddef(fileID);
    streamptr->ncmode = 2;
    }

ncgrid[gridindex].gridID = gridID;
ncgrid[gridindex].ncIDs[CDF_DIMID_X] = dimID;
}

static
void cdfDefGrid(stream_t *streamptr, int gridID, int gridindex)
{
if ( streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X] != CDI_UNDEFID ) return;

int gridtype = gridInqType(gridID);
size_t size = gridInqSize(gridID);

if ( CDI_Debug )
    Message("gridtype = %d  size = %zu", gridtype, size);

if ( CDI_reduce_dim && size == 1 )
    {

    streamptr->ncgrid[gridindex].gridID = gridID;
    return;
    }

if ( gridtype == GRID_GAUSSIAN    ||
    gridtype == GRID_LONLAT      ||
    gridtype == GRID_PROJECTION  ||
    gridtype == GRID_GENERIC )
    {
    if ( gridtype == GRID_GENERIC )
        {
        if ( size == 1 && gridInqXsize(gridID) == 0 && gridInqYsize(gridID) == 0 )
            {

            streamptr->ncgrid[gridindex].gridID = gridID;
            }
        else
            {
            bool lx = false, ly = false;
            if ( gridInqXsize(gridID) > 0   )
                {
                cdfDefXaxis(streamptr, gridID, gridindex, 1);
                lx = true;
                }

            if ( gridInqYsize(gridID) > 0   )
                {
                cdfDefYaxis(streamptr, gridID, gridindex, 1);
                ly = true;
                }

            if ( !lx && !ly ) cdfDefGdim(streamptr, gridID, gridindex);
            }
        }
    else
        {
        int ndims = !(gridtype == GRID_LONLAT && size == 1 && !gridInqHasDims(gridID));

        if ( gridInqXsize(gridID) > 0 ) cdfDefXaxis(streamptr, gridID, gridindex, ndims);
        if ( gridInqYsize(gridID) > 0 ) cdfDefYaxis(streamptr, gridID, gridindex, ndims);

        cdf_def_mapping(streamptr, gridID);
        }
    }
else if ( gridtype == GRID_CURVILINEAR )
    {
    cdfDefCurvilinear(streamptr, gridID, gridindex);
    }
else if ( gridtype == GRID_UNSTRUCTURED )
    {
    cdfDefUnstructured(streamptr, gridID, gridindex);
    }
else if ( gridtype == GRID_GAUSSIAN_REDUCED )
    {
    cdfDefRgrid(streamptr, gridID, gridindex);
    }
else if ( gridtype == GRID_SPECTRAL )
    {
    cdfDefComplex(streamptr, gridID, gridindex);
    cdfDefSP(streamptr, gridID, gridindex);
    }
else if ( gridtype == GRID_FOURIER )
    {
    cdfDefComplex(streamptr, gridID, gridindex);
    cdfDefFC(streamptr, gridID, gridindex);
    }
else if ( gridtype == GRID_TRAJECTORY )
    {
    cdfDefTrajLon(streamptr, gridID, gridindex);
    cdfDefTrajLat(streamptr, gridID, gridindex);
    }
else if ( gridtype == GRID_CHARXY )
    {
    int strlen = 0;
    if ( (strlen = gridInqXIsc(gridID)) )
        cdfDefCharacter(streamptr, gridID, gridindex, 0, strlen);
    else
        if ( gridInqXsize(gridID) > 0 ) cdfDefXaxis(streamptr, gridID, gridindex, 1);
    if ( (strlen = gridInqYIsc(gridID)) )
        cdfDefCharacter(streamptr, gridID, gridindex, 1, strlen);
    else
        if ( gridInqYsize(gridID) > 0 ) cdfDefYaxis(streamptr, gridID, gridindex, 1);
    }
else
    {
    Error("Unsupported grid type: %s", gridNamePtr(gridtype));
    }
}


void cdfDefHistory(stream_t *streamptr, int size, const char *history)
{
int ncid = streamptr->fileID;
cdf_put_att_text(ncid, NC_GLOBAL, "history", (size_t) size, history);
}


void cdfDefVars(stream_t *streamptr)
{
int vlistID = streamptr->vlistID;
if ( vlistID == CDI_UNDEFID )
    Error("Internal problem! vlist undefined for streamptr %p", streamptr);

if ( vlistHasTime(vlistID) ) cdfDefTime(streamptr);

int ngrids = vlistNgrids(vlistID);
if ( 2*ngrids > MAX_GRIDS_PS ) Error("Internal problem! Too many grids per stream (max=%d)\n", MAX_GRIDS_PS);
for ( int index = 0; index < 2*ngrids; ++index )
    {
    streamptr->ncgrid[index].gridID = CDI_UNDEFID;
    for (size_t i = 0; i < CDF_SIZE_ncIDs; ++i)
        streamptr->ncgrid[index].ncIDs[i] = CDI_UNDEFID;
    }

for ( int index = 0; index < ngrids; ++index )
    {
    int gridID = vlistGrid(vlistID, index);
    cdfDefGrid(streamptr, gridID, index);
    }
{
    int index = ngrids-1;
    for ( int i = 0; i < ngrids; ++i )
    {
        int gridID = vlistGrid(vlistID, i);
        int projID = gridInqProj(gridID);
        if ( projID != CDI_UNDEFID ) cdfDefGrid(streamptr, projID, ++index);
    }
}
int nzaxis = vlistNzaxis(vlistID);
for ( int index = 0; index < nzaxis; ++index )
    {
    int zaxisID = vlistZaxis(vlistID, index);
    if ( streamptr->zaxisID[index] == CDI_UNDEFID ) cdfDefZaxis(streamptr, zaxisID);
    }

if ( streamptr->ncmode != 2 )
    {
    cdf_enddef(streamptr->fileID);
    streamptr->ncmode = 2;
    }
}

#endif


#ifdef HAVE_CONFIG_H
#endif

#ifdef HAVE_LIBNETCDF

#include <stdio.h>
#include <string.h>


static
int cdfDefTimeBounds(int fileID, int nctimevarid, int nctimedimid, const char *taxis_name, taxis_t* taxis)
{
int time_bndsid = -1;
int dims[2];

dims[0] = nctimedimid;


static const char bndsName[] = "bnds";
if ( nc_inq_dimid(fileID, bndsName, &dims[1]) != NC_NOERR )
    cdf_def_dim(fileID, bndsName, 2, &dims[1]);

const char *bndsAttName, *bndsAttVal;
size_t bndsAttValLen;
char tmpstr[CDI_MAX_NAME];
if ( taxis->climatology )
    {
    static const char climatology_bndsName[] = "climatology_bnds",
        climatology_bndsAttName[] = "climatology";
    bndsAttName = climatology_bndsAttName;
    bndsAttValLen = sizeof (climatology_bndsName) - 1;
    bndsAttVal = climatology_bndsName;
    }
else
    {
    size_t taxisnameLen = strlen(taxis_name);
    memcpy(tmpstr, taxis_name, taxisnameLen);
    tmpstr[taxisnameLen] = '_';
    memcpy(tmpstr + taxisnameLen + 1, bndsName, sizeof (bndsName));
    size_t tmpstrLen = taxisnameLen + sizeof (bndsName);
    static const char generic_bndsAttName[] = "bounds";
    bndsAttName = generic_bndsAttName;
    bndsAttValLen = tmpstrLen;
    bndsAttVal = tmpstr;
    }
cdf_def_var(fileID, bndsAttVal, NC_DOUBLE, 2, dims, &time_bndsid);
cdf_put_att_text(fileID, nctimevarid, bndsAttName, bndsAttValLen, bndsAttVal);

return time_bndsid;
}

static
void cdfDefTimeUnits(char *unitstr, taxis_t *taxis0, taxis_t *taxis)
{
if ( taxis->units && taxis->units[0] )
    {
    strcpy(unitstr, taxis->units);
    }
else
    {
    unitstr[0] = 0;

    if ( taxis0->type == TAXIS_ABSOLUTE )
        {
        static const char *const unitstrfmt[3]
            = { "year as %Y.%f",
                "month as %Y%m.%f",
                "day as %Y%m%d.%f" };
        size_t fmtidx = (taxis0->unit == TUNIT_YEAR ? 0
                        : (taxis0->unit == TUNIT_MONTH ? 1
                            : 2));
        strcpy(unitstr, unitstrfmt[fmtidx]);
        }
    else
        {
        int year, month, day, hour, minute, second;
        cdiDecodeDate(taxis->rdate, &year, &month, &day);
        cdiDecodeTime(taxis->rtime, &hour, &minute, &second);

        int timeunit = taxis->unit  != -1 ? taxis->unit  : TUNIT_HOUR;
        if      ( timeunit == TUNIT_QUARTER   ) timeunit = TUNIT_MINUTE;
        else if ( timeunit == TUNIT_30MINUTES ) timeunit = TUNIT_MINUTE;
        else if (    timeunit == TUNIT_3HOURS
                    || timeunit == TUNIT_6HOURS
                    || timeunit == TUNIT_12HOURS ) timeunit = TUNIT_HOUR;

        sprintf(unitstr, "%s since %d-%d-%d %02d:%02d:%02d",
                tunitNamePtr(timeunit), year, month, day, hour, minute, second);
        }
    }
}

static
void cdfDefForecastTimeUnits(char *unitstr, int timeunit)
{
unitstr[0] = 0;

if ( timeunit == -1 ) timeunit = TUNIT_HOUR;
else if ( timeunit == TUNIT_QUARTER   ) timeunit = TUNIT_MINUTE;
else if ( timeunit == TUNIT_30MINUTES ) timeunit = TUNIT_MINUTE;
else if (    timeunit == TUNIT_3HOURS
            || timeunit == TUNIT_6HOURS
            || timeunit == TUNIT_12HOURS ) timeunit = TUNIT_HOUR;

strcpy(unitstr, tunitNamePtr(timeunit));
}

static
void cdfDefCalendar(int fileID, int ncvarid, int calendar)
{
static const struct { int calCode; const char *calStr; } calTab[] = {
    { CALENDAR_STANDARD, "standard" },
    { CALENDAR_GREGORIAN, "gregorian" },
    { CALENDAR_PROLEPTIC, "proleptic_gregorian" },
    { CALENDAR_NONE, "none" },
    { CALENDAR_360DAYS, "360_day" },
    { CALENDAR_365DAYS, "365_day" },
    { CALENDAR_366DAYS, "366_day" },
};
enum { calTabSize = sizeof calTab / sizeof calTab[0] };

for ( size_t i = 0; i < calTabSize; ++i )
    if ( calTab[i].calCode == calendar )
    {
        const char *calstr = calTab[i].calStr;
        size_t len = strlen(calstr);
        cdf_put_att_text(fileID, ncvarid, "calendar", len, calstr);
        break;
    }
}


void cdfDefTime(stream_t* streamptr)
{
int time_varid;
int time_dimid;
int time_bndsid = -1;
static const char default_name[] = "time";

if ( streamptr->basetime.ncvarid != CDI_UNDEFID ) return;

int fileID = streamptr->fileID;

if ( streamptr->ncmode == 0 ) streamptr->ncmode = 1;
if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

taxis_t *taxis = &streamptr->tsteps[0].taxis;

const char *taxis_name = (taxis->name && taxis->name[0]) ? taxis->name : default_name ;

cdf_def_dim(fileID, taxis_name, NC_UNLIMITED, &time_dimid);
streamptr->basetime.ncdimid = time_dimid;

nc_type xtype = (taxis->datatype == CDI_DATATYPE_FLT32) ? NC_FLOAT : NC_DOUBLE;

cdf_def_var(fileID, taxis_name, xtype, 1, &time_dimid, &time_varid);

streamptr->basetime.ncvarid = time_varid;

#if  defined  (HAVE_NETCDF4)
if ( streamptr->filetype == CDI_FILETYPE_NC4 || streamptr->filetype == CDI_FILETYPE_NC4C )
    {
    size_t chunk = 512;
    cdf_def_var_chunking(fileID, time_varid, NC_CHUNKED, &chunk);
    }
#endif

{
    static const char timeStr[] = "time";
    cdf_put_att_text(fileID, time_varid, "standard_name", sizeof(timeStr) - 1, timeStr);
}

if ( taxis->longname && taxis->longname[0] )
    cdf_put_att_text(fileID, time_varid, "long_name", strlen(taxis->longname), taxis->longname);

if ( taxis->has_bounds )
    {
    time_bndsid = cdfDefTimeBounds(fileID, time_varid, time_dimid, taxis_name, taxis);
    streamptr->basetime.ncvarboundsid = time_bndsid;
    }

{
    char unitstr[CDI_MAX_NAME];
    cdfDefTimeUnits(unitstr, &streamptr->tsteps[0].taxis, taxis);
    size_t len = strlen(unitstr);
    if ( len )
    {
        cdf_put_att_text(fileID, time_varid, "units", len, unitstr);

    }
}

if ( taxis->calendar != -1 )
    {
    cdfDefCalendar(fileID, time_varid, taxis->calendar);

    }

if ( taxis->type == TAXIS_FORECAST )
    {
    int leadtimeid;
    cdf_def_var(fileID, "leadtime", xtype, 1, &time_dimid, &leadtimeid);
    streamptr->basetime.leadtimeid = leadtimeid;

    {
        static const char stdname[] = "forecast_period";
        cdf_put_att_text(fileID, leadtimeid, "standard_name", sizeof(stdname) - 1, stdname);
    }

    {
        static const char lname[] = "Time elapsed since the start of the forecast";
        cdf_put_att_text(fileID, leadtimeid, "long_name", sizeof(lname) - 1, lname);
    }

    {
        char unitstr[CDI_MAX_NAME];
        cdfDefForecastTimeUnits(unitstr, taxis->fc_unit);
        size_t len = strlen(unitstr);
        if ( len )
            cdf_put_att_text(fileID, leadtimeid, "units", len, unitstr);
    }
    }

cdf_put_att_text(fileID, time_varid, "axis", 1, "T");

if ( streamptr->ncmode == 2 ) cdf_enddef(fileID);
}


#endif

#ifdef  HAVE_CONFIG_H
#endif


#ifdef HAVE_LIBNETCDF
static inline bool
filetypeIsNetCDF(int filetype)
{
return filetype == CDI_FILETYPE_NC
    ||   filetype == CDI_FILETYPE_NC2
    ||   filetype == CDI_FILETYPE_NC5
    ||   filetype == CDI_FILETYPE_NC4
    ||   filetype == CDI_FILETYPE_NC4C;
}
#endif

void streamDefHistory(int streamID, int length, const char *history)
{
#ifdef HAVE_LIBNETCDF
stream_t *streamptr = stream_to_pointer(streamID);

if ( filetypeIsNetCDF(streamptr->filetype) )
    {
    char *histstring;
    size_t len;
    if ( history )
        {
        len = strlen(history);
        if ( len )
            {

            histstring = strdupx(history);
            cdfDefHistory(streamptr, length, histstring);
            Free(histstring);
            }
        }
    }
#else
(void)streamID; (void)length; (void)history;
#endif
}


int streamInqHistorySize(int streamID)
{
int size = 0;
#ifdef HAVE_LIBNETCDF
stream_t *streamptr = stream_to_pointer(streamID);

if ( filetypeIsNetCDF(streamptr->filetype) )
    {
    size = cdfInqHistorySize(streamptr);
    }
#else
(void)streamID;
#endif
return (size);
}


void streamInqHistoryString(int streamID, char *history)
{
#ifdef HAVE_LIBNETCDF
stream_t *streamptr = stream_to_pointer(streamID);

if ( filetypeIsNetCDF(streamptr->filetype) )
    {
    cdfInqHistoryString(streamptr, history);
    }
#else
(void)streamID; (void)history;
#endif
}

#ifdef  HAVE_CONFIG_H
#endif

#include <limits.h>
#include <stdio.h>
#include <string.h>




void recordInitEntry(record_t *record)
{
record->position  = CDI_UNDEFID;
record->size      = 0;
record->gridsize  = 0;
record->param     = 0;
record->ilevel    = CDI_UNDEFID;
record->used      = false;
record->tsteptype = CDI_UNDEFID;
record->varID     = CDI_UNDEFID;
record->levelID   = CDI_UNDEFID;
memset(record->varname, 0, sizeof(record->varname));
memset(&record->tiles, 0, sizeof(record->tiles));
}


int recordNewEntry(stream_t *streamptr, int tsID)
{
size_t recordID = 0;
size_t recordSize = (size_t)streamptr->tsteps[tsID].recordSize;
record_t *records = streamptr->tsteps[tsID].records;

if ( ! recordSize )
    {
    recordSize = 1;
    records = (record_t *) Malloc(recordSize * sizeof (record_t));

    for ( size_t i = 0; i < recordSize; i++ )
        records[i].used = CDI_UNDEFID;
    }
else
    {
    while ( recordID < recordSize && records[recordID].used != CDI_UNDEFID )
        ++recordID;
    }

if ( recordID == recordSize )
    {
    if (recordSize <= INT_MAX / 2)
        recordSize *= 2;
    else if (recordSize < INT_MAX)
        recordSize = INT_MAX;
    else
        Error("Cannot handle this many records!\n");
    records = (record_t *) Realloc(records, recordSize * sizeof (record_t));

    for ( size_t i = recordID; i < recordSize; i++ )
        records[i].used = CDI_UNDEFID;
    }

recordInitEntry(&records[recordID]);

records[recordID].used = 1;

streamptr->tsteps[tsID].recordSize = (int)recordSize;
streamptr->tsteps[tsID].records    = records;

return (int)recordID;
}

static
void cdiInitRecord(stream_t *streamptr)
{
Record *record = (Record *) Malloc(sizeof(Record));
streamptr->record = record;

record->param      = 0;
record->level      = 0;
record->date       = 0;
record->time       = 0;
record->gridID     = 0;
record->buffer     = NULL;
record->buffersize = 0;
record->position   = 0;
record->varID      = 0;
record->levelID    = CDI_UNDEFID;
}


void streamInqRecord(int streamID, int *varID, int *levelID)
{
check_parg(varID);
check_parg(levelID);

stream_t *streamptr = stream_to_pointer(streamID);

cdiDefAccesstype(streamID, TYPE_REC);

if ( ! streamptr->record ) cdiInitRecord(streamptr);

int tsID   = streamptr->curTsID;
int rindex = streamptr->tsteps[tsID].curRecID + 1;

if ( rindex >= streamptr->tsteps[tsID].nrecs )
    Error("record %d not available at timestep %d", rindex+1, tsID+1);

int recID  = streamptr->tsteps[tsID].recIDs[rindex];

if ( recID == -1 || recID >= streamptr->tsteps[tsID].nallrecs )
    Error("Internal problem! tsID = %d recID = %d", tsID, recID);

*varID   = streamptr->tsteps[tsID].records[recID].varID;
int lindex = streamptr->tsteps[tsID].records[recID].levelID;

int isub = subtypeInqActiveIndex(streamptr->vars[*varID].subtypeID);
*levelID = streamptr->vars[*varID].recordTable[isub].lindex[lindex];

if ( CDI_Debug )
    Message("tsID = %d, recID = %d, varID = %d, levelID = %d", tsID, recID, *varID, *levelID);

streamptr->curTsID = tsID;
streamptr->tsteps[tsID].curRecID = rindex;
}


void streamDefRecord(int streamID, int varID, int levelID)
{
stream_t *streamptr = stream_to_pointer(streamID);

int tsID = streamptr->curTsID;

if ( tsID == CDI_UNDEFID )
    {
    tsID++;
    streamDefTimestep(streamID, tsID);
    }

if ( ! streamptr->record ) cdiInitRecord(streamptr);

int vlistID = streamptr->vlistID;
int gridID  = vlistInqVarGrid(vlistID, varID);
int zaxisID = vlistInqVarZaxis(vlistID, varID);
int param   = vlistInqVarParam(vlistID, varID);
int level   = (int)(zaxisInqLevel(zaxisID, levelID));

Record *record = streamptr->record;
record->varID    = varID;
record->levelID  = levelID;
record->param    = param;
record->level    = level;
record->date     = streamptr->tsteps[tsID].taxis.vdate;
record->time     = streamptr->tsteps[tsID].taxis.vtime;
record->gridID   = gridID;
record->prec     = vlistInqVarDatatype(vlistID, varID);

switch (streamptr->filetype)
    {
#if  defined  (HAVE_LIBGRIB)
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
    grbDefRecord(streamptr);
    break;
#endif
#ifdef HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    srvDefRecord(streamptr);
    break;
#endif
#ifdef HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    extDefRecord(streamptr);
    break;
#endif
#ifdef HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    iegDefRecord(streamptr);
    break;
#endif
#ifdef HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    if ( streamptr->accessmode == 0 ) cdfEndDef(streamptr);
    cdfDefRecord(streamptr);
    break;
#endif
    default:
    Error("%s support not compiled in!", strfiletype(streamptr->filetype));
    break;
    }
}


void streamCopyRecord(int streamID2, int streamID1)
{
stream_t *streamptr1 = stream_to_pointer(streamID1),
    *streamptr2 = stream_to_pointer(streamID2);
int filetype1 = streamptr1->filetype,
    filetype2 = streamptr2->filetype,
    filetype  = CDI_FILETYPE_UNDEF;

if ( filetype1 == filetype2 ) filetype = filetype2;
else
    {
    switch (filetype1)
        {
        case CDI_FILETYPE_NC:
        case CDI_FILETYPE_NC2:
        case CDI_FILETYPE_NC4:
        case CDI_FILETYPE_NC4C:
        case CDI_FILETYPE_NC5:
        switch (filetype2)
            {
            case CDI_FILETYPE_NC:
            case CDI_FILETYPE_NC2:
            case CDI_FILETYPE_NC4:
            case CDI_FILETYPE_NC4C:
            case CDI_FILETYPE_NC5:

            filetype = filetype2;
            break;
            }
        break;
        }
    }

if ( filetype == CDI_FILETYPE_UNDEF )
    Error("Streams have different file types (%s -> %s)!", strfiletype(filetype1), strfiletype(filetype2));

switch (filetype)
    {
#if  defined  (HAVE_LIBGRIB)
    case CDI_FILETYPE_GRB:
    case CDI_FILETYPE_GRB2:
    grbCopyRecord(streamptr2, streamptr1);
    break;
#endif
#ifdef HAVE_LIBSERVICE
    case CDI_FILETYPE_SRV:
    srvCopyRecord(streamptr2, streamptr1);
    break;
#endif
#ifdef HAVE_LIBEXTRA
    case CDI_FILETYPE_EXT:
    extCopyRecord(streamptr2, streamptr1);
    break;
#endif
#ifdef HAVE_LIBIEG
    case CDI_FILETYPE_IEG:
    iegCopyRecord(streamptr2, streamptr1);
    break;
#endif
#ifdef HAVE_LIBNETCDF
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    cdfCopyRecord(streamptr2, streamptr1);
    break;
#endif
    default:
    {
        Error("%s support not compiled in!", strfiletype(filetype));
        break;
    }
    }
}


void cdi_create_records(stream_t *streamptr, int tsID)
{
unsigned nrecords, maxrecords;

tsteps_t *sourceTstep = streamptr->tsteps;
tsteps_t *destTstep = sourceTstep + tsID;

if ( destTstep->records ) return;

int vlistID = streamptr->vlistID;

if ( tsID == 0 )
    {
    maxrecords = 0;
    int nvars = streamptr->nvars;
    for ( int varID = 0; varID < nvars; varID++)
        for (int isub=0; isub<streamptr->vars[varID].subtypeSize; isub++)
        maxrecords += (unsigned)streamptr->vars[varID].recordTable[isub].nlevs;
    }
else
    {
    maxrecords = (unsigned)sourceTstep->recordSize;
    }

if ( tsID == 0 )
    {
    nrecords = maxrecords;
    }
else if ( tsID == 1 )
    {
    nrecords = 0;
    maxrecords = (unsigned)sourceTstep->recordSize;
    for ( unsigned recID = 0; recID < maxrecords; recID++ )
        {
        int varID = sourceTstep->records[recID].varID;
        nrecords += (varID == CDI_UNDEFID
                    || vlistInqVarTimetype(vlistID, varID) != TIME_CONSTANT);

        }
    }
else
    {
    nrecords = (unsigned)streamptr->tsteps[1].nallrecs;
    }


record_t *records = NULL;
if ( maxrecords > 0 ) records = (record_t *) Malloc(maxrecords*sizeof(record_t));

destTstep->records    = records;
destTstep->recordSize = (int)maxrecords;
destTstep->nallrecs   = (int)nrecords;

if ( tsID == 0 )
    {
    for ( unsigned recID = 0; recID < maxrecords; recID++ )
        recordInitEntry(&destTstep->records[recID]);
    }
else
    {
    memcpy(destTstep->records, sourceTstep->records, (size_t)maxrecords*sizeof(record_t));

    for ( unsigned recID = 0; recID < maxrecords; recID++ )
        {
        record_t *curRecord = &sourceTstep->records[recID];
        destTstep->records[recID].used = curRecord->used;
        if ( curRecord->used != CDI_UNDEFID && curRecord->varID != -1 )
            {
            if ( vlistInqVarTimetype(vlistID, curRecord->varID) != TIME_CONSTANT )
                {
                destTstep->records[recID].position = CDI_UNDEFID;
                destTstep->records[recID].size     = 0;
                destTstep->records[recID].used     = false;
                }
            }
        }
    }
}


void streamFCopyRecord(stream_t *streamptr2, stream_t *streamptr1, const char *container_name)
{
int fileID1 = streamptr1->fileID;
int fileID2 = streamptr2->fileID;

int tsID    = streamptr1->curTsID;
int vrecID  = streamptr1->tsteps[tsID].curRecID;
int recID   = streamptr1->tsteps[tsID].recIDs[vrecID];
off_t recpos  = streamptr1->tsteps[tsID].records[recID].position;
size_t recsize = streamptr1->tsteps[tsID].records[recID].size;

if (fileSetPos(fileID1, recpos, SEEK_SET) != 0)
    Error("Cannot seek input file for %s record copy!", container_name);

char *buffer = (char *) Malloc(recsize);

if (fileRead(fileID1, buffer, recsize) != recsize)
    Error("Failed to read record from %s file for copying!", container_name);

if (fileWrite(fileID2, buffer, recsize) != recsize)
    Error("Failed to write record to %s file when copying!", container_name);

Free(buffer);
}




static void streamvar_init_recordtable(stream_t *streamptr, int varID, int isub)
{
streamptr->vars[varID].recordTable[isub].nlevs    = 0;
streamptr->vars[varID].recordTable[isub].recordID = NULL;
streamptr->vars[varID].recordTable[isub].lindex   = NULL;
}


static
void streamvar_init_entry(stream_t *streamptr, int varID)
{
streamptr->vars[varID].ncvarid      = CDI_UNDEFID;
streamptr->vars[varID].defmiss      = false;

streamptr->vars[varID].subtypeSize  = 0;
streamptr->vars[varID].recordTable  = NULL;

streamptr->vars[varID].gridID       = CDI_UNDEFID;
streamptr->vars[varID].zaxisID      = CDI_UNDEFID;
streamptr->vars[varID].tsteptype    = CDI_UNDEFID;
streamptr->vars[varID].subtypeID    = CDI_UNDEFID;
}

static
int streamvar_new_entry(stream_t *streamptr)
{
int varID = 0;
int streamvarSize = streamptr->varsAllocated;
svarinfo_t *streamvar = streamptr->vars;

if ( ! streamvarSize )
    {
    streamvarSize = 2;
    streamvar = (svarinfo_t *) Malloc((size_t)streamvarSize * sizeof(svarinfo_t));
    if ( streamvar == NULL )
        {
        Message("streamvarSize = %d", streamvarSize);
        SysError("Allocation of svarinfo_t failed");
        }

    for ( int i = 0; i < streamvarSize; i++ )
        streamvar[i].isUsed = false;
    }
else
    {
    while ( varID < streamvarSize )
        {
        if ( ! streamvar[varID].isUsed ) break;
        varID++;
        }
    }

if ( varID == streamvarSize )
    {
    streamvarSize = 2*streamvarSize;
    streamvar = (svarinfo_t *) Realloc(streamvar,
                                (size_t)streamvarSize * sizeof (svarinfo_t));
    if ( streamvar == NULL )
        {
        Message("streamvarSize = %d", streamvarSize);
        SysError("Reallocation of svarinfo_t failed");
        }
    varID = streamvarSize/2;

    for ( int i = varID; i < streamvarSize; i++ )
        streamvar[i].isUsed = false;
    }

streamptr->varsAllocated = streamvarSize;
streamptr->vars          = streamvar;

streamvar_init_entry(streamptr, varID);

streamptr->vars[varID].isUsed = true;

return varID;
}


static void
allocate_record_table_entry(stream_t *streamptr, int varID, int subID, int nlevs)
{
int *level    = (int *) Malloc((size_t)nlevs * sizeof (int));
int *lindex   = (int *) Malloc((size_t)nlevs * sizeof (int));

for ( int levID = 0; levID < nlevs; levID++ )
    {
    level[levID]    = CDI_UNDEFID;
    lindex[levID]   = levID;
    }

streamptr->vars[varID].recordTable[subID].nlevs    = nlevs;
streamptr->vars[varID].recordTable[subID].recordID = level;
streamptr->vars[varID].recordTable[subID].lindex   = lindex;
}


int stream_new_var(stream_t *streamptr, int gridID, int zaxisID, int tilesetID)
{
if ( CDI_Debug )
    Message("gridID = %d  zaxisID = %d", gridID, zaxisID);

int varID = streamvar_new_entry(streamptr);
int nlevs = zaxisInqSize(zaxisID);

streamptr->nvars++;

streamptr->vars[varID].gridID  = gridID;
streamptr->vars[varID].zaxisID = zaxisID;

int nsub = 1;
if (tilesetID != CDI_UNDEFID)
    nsub = subtypeInqSize(tilesetID);
if ( CDI_Debug )
    Message("varID %d: create %d tiles with %d level(s), zaxisID=%d", varID, nsub, nlevs,zaxisID);
streamptr->vars[varID].recordTable = (sleveltable_t *) Malloc((size_t)nsub * sizeof (sleveltable_t));
if( streamptr->vars[varID].recordTable == NULL )
    SysError("Allocation of leveltable failed!");
streamptr->vars[varID].subtypeSize = nsub;

for (int isub=0; isub<nsub; isub++) {
    streamvar_init_recordtable(streamptr, varID, isub);
    allocate_record_table_entry(streamptr, varID, isub, nlevs);
    if ( CDI_Debug )
    Message("streamptr->vars[varID].recordTable[isub].recordID[0]=%d",
            streamptr->vars[varID].recordTable[isub].recordID[0]);
}

streamptr->vars[varID].subtypeID = tilesetID;

return varID;
}

#ifndef  VLIST_VAR_H
#define  VLIST_VAR_H

#ifdef  HAVE_CONFIG_H
#endif

#ifndef  VLIST_H
#endif

int  vlistVarGetPackSize(vlist_t *p, int varID, void *context);
void vlistVarPack(vlist_t *p, int varID,
                char * buffer, int bufferSize, int * pos, void *context);
void vlistVarUnpack(int vlistID,
                    char * buf, int size, int *position, int, void *context);
int vlistVarCompare(vlist_t *a, int varIDA, vlist_t *b, int varIDB);
void vlistDefVarIOrank    ( int, int, int );
int  vlistInqVarIOrank    ( int, int );

void cdiVlistCreateVarLevInfo(vlist_t *vlistptr, int varID);

const char *vlistInqVarNamePtr(int vlistID, int varID);

#endif

#ifdef HAVE_CONFIG_H
#endif

#ifdef HAVE_LIBNETCDF



void cdfDefVarDeflate(int ncid, int ncvarid, int deflate_level)
{
#if  defined(HAVE_NETCDF4)
int retval;

int shuffle = 1;
int deflate = 1;

if ( deflate_level < 1 || deflate_level > 9 ) deflate_level = 1;

if ( (retval = nc_def_var_deflate(ncid, ncvarid, shuffle, deflate, deflate_level)) )
    {
    Error("nc_def_var_deflate failed, status = %d", retval);
    }
#else
static bool lwarn = true;
if ( lwarn )
    {
    lwarn = false;
    Warning("Deflate compression failed, NetCDF4 not available!");
    }
#endif
}

int cdfDefDatatype(int datatype, stream_t *streamptr)
{
int xtype = NC_FLOAT;

if ( streamptr->filetype == CDI_FILETYPE_NC4 )
    {
    if      ( datatype == CDI_DATATYPE_INT8   ) xtype = NC_BYTE;
    else if ( datatype == CDI_DATATYPE_INT16  ) xtype = NC_SHORT;
    else if ( datatype == CDI_DATATYPE_INT32  ) xtype = NC_INT;
#ifdef  HAVE_NETCDF4
    else if ( datatype == CDI_DATATYPE_UINT8  ) xtype = NC_UBYTE;
    else if ( datatype == CDI_DATATYPE_UINT16 ) xtype = NC_USHORT;
    else if ( datatype == CDI_DATATYPE_UINT32 ) xtype = NC_UINT;
    else if ( datatype == CDI_DATATYPE_CPX32 || datatype == CDI_DATATYPE_CPX64 )
        Error("CDI library does not support complex numbers with NetCDF4/HDF5!");
#else
    else if ( datatype == CDI_DATATYPE_UINT8  ) xtype = NC_SHORT;
    else if ( datatype == CDI_DATATYPE_UINT16 ) xtype = NC_INT;
    else if ( datatype == CDI_DATATYPE_UINT32 ) xtype = NC_INT;
    else if ( datatype == CDI_DATATYPE_CPX32 || datatype == CDI_DATATYPE_CPX64 )
        Error("CDI library does not support complex numbers with NetCDF4 classic!");
#endif
    else if ( datatype == CDI_DATATYPE_FLT64  ) xtype = NC_DOUBLE;
    else if ( datatype == CDI_DATATYPE_FLT32  ) xtype = NC_FLOAT;
    }
else
    {
    if      ( datatype == CDI_DATATYPE_INT8   ) xtype = NC_BYTE;
    else if ( datatype == CDI_DATATYPE_INT16  ) xtype = NC_SHORT;
    else if ( datatype == CDI_DATATYPE_INT32  ) xtype = NC_INT;
    else if ( datatype == CDI_DATATYPE_UINT8  ) xtype = NC_SHORT;
    else if ( datatype == CDI_DATATYPE_UINT16 ) xtype = NC_INT;
    else if ( datatype == CDI_DATATYPE_UINT32 ) xtype = NC_INT;
    else if ( datatype == CDI_DATATYPE_FLT64  ) xtype = NC_DOUBLE;
    else if ( datatype == CDI_DATATYPE_FLT32  ) xtype = NC_FLOAT;
    else if ( datatype == CDI_DATATYPE_CPX32 || datatype == CDI_DATATYPE_CPX64 )
        Error("CDI library does not support complex numbers with NetCDF classic!");
    }

return xtype;
}

static
void cdfDefVarMissval(stream_t *streamptr, int varID, int dtype, int lcheck)
{
if ( streamptr->vars[varID].defmiss == false )
    {
    int vlistID = streamptr->vlistID;
    int fileID  = streamptr->fileID;
    int ncvarid = streamptr->vars[varID].ncvarid;
    double missval = vlistInqVarMissval(vlistID, varID);

    if ( lcheck && streamptr->ncmode == 2 ) cdf_redef(fileID);

    int xtype = cdfDefDatatype(dtype, streamptr);

    if ( xtype == NC_BYTE && missval > 127 && missval < 256 ) xtype = NC_INT;

    if ( lcheck == 0 ||
        streamptr->ncmode != 2 ||
        streamptr->filetype == CDI_FILETYPE_NC ||
        streamptr->filetype == CDI_FILETYPE_NC2||
        streamptr->filetype == CDI_FILETYPE_NC5 )
        cdf_put_att_double(fileID, ncvarid, "_FillValue", (nc_type) xtype, 1, &missval);

    cdf_put_att_double(fileID, ncvarid, "missing_value", (nc_type) xtype, 1, &missval);

    if ( lcheck && streamptr->ncmode == 2 ) cdf_enddef(fileID);

    streamptr->vars[varID].defmiss = true;
    }
}

static
void cdfDefInstitut(stream_t *streamptr)
{
int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;
int instID  = vlistInqInstitut(vlistID);

if ( instID != CDI_UNDEFID )
    {
    const char *longname = institutInqLongnamePtr(instID);
    if ( longname )
        {
        size_t len = strlen(longname);
        if ( len > 0 )
            {
            if ( streamptr->ncmode == 2 ) cdf_redef(fileID);
            cdf_put_att_text(fileID, NC_GLOBAL, "institution", len, longname);
            if ( streamptr->ncmode == 2 ) cdf_enddef(fileID);
            }
        }
    }
}

static
void cdfDefSource(stream_t *streamptr)
{
int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;
int modelID = vlistInqModel(vlistID);

if ( modelID != CDI_UNDEFID )
    {
    const char *longname = modelInqNamePtr(modelID);
    if ( longname )
        {
        size_t len = strlen(longname);
        if ( len > 0 )
            {
            if ( streamptr->ncmode == 2 ) cdf_redef(fileID);
            cdf_put_att_text(fileID, NC_GLOBAL, "source", len, longname);
            if ( streamptr->ncmode == 2 ) cdf_enddef(fileID);
            }
        }
    }
}

static inline
void *resizeBuf(void **buf, size_t *bufSize, size_t reqSize)
{
if ( reqSize > *bufSize )
    {
    *buf = Realloc(*buf, reqSize);
    *bufSize = reqSize;
    }
return *buf;
}


void cdfDefineAttributes(int cdiID, int varID, int fileID, int ncvarID)
{
int atttype, attlen;
size_t len;
char attname[CDI_MAX_NAME+1];
void *attBuf = NULL;
size_t attBufSize = 0;

int natts;
cdiInqNatts(cdiID, varID, &natts);

for ( int iatt = 0; iatt < natts; ++iatt )
    {
    cdiInqAtt(cdiID, varID, iatt, attname, &atttype, &attlen);

    if ( attlen == 0 ) continue;

    if ( atttype == CDI_DATATYPE_TXT )
        {
        size_t attSize = (size_t)attlen*sizeof(char);
        char *atttxt = (char *)resizeBuf(&attBuf, &attBufSize, attSize);
        cdiInqAttTxt(cdiID, varID, attname, attlen, atttxt);
        len = (size_t)attlen;
        cdf_put_att_text(fileID, ncvarID, attname, len, atttxt);
        }
    else if ( atttype == CDI_DATATYPE_INT8  || atttype == CDI_DATATYPE_UINT8  ||
                atttype == CDI_DATATYPE_INT16 || atttype == CDI_DATATYPE_UINT16 ||
                atttype == CDI_DATATYPE_INT32 || atttype == CDI_DATATYPE_UINT32 )
        {
        size_t attSize = (size_t)attlen*sizeof(int);
        int *attint = (int *)resizeBuf(&attBuf, &attBufSize, attSize);
        cdiInqAttInt(cdiID, varID, attname, attlen, &attint[0]);
        len = (size_t)attlen;
        nc_type xtype = (atttype == CDI_DATATYPE_INT8)  ? NC_BYTE :
                        (atttype == CDI_DATATYPE_INT16) ? NC_SHORT :
#if  defined  (HAVE_NETCDF4)
                        (atttype == CDI_DATATYPE_UINT8)  ? NC_UBYTE :
                        (atttype == CDI_DATATYPE_UINT16) ? NC_USHORT :
                        (atttype == CDI_DATATYPE_UINT32) ? NC_UINT :
#endif
                        NC_INT;
        cdf_put_att_int(fileID, ncvarID, attname, xtype, len, attint);
        }
    else if ( atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64 )
        {
        size_t attSize = (size_t)attlen * sizeof(double);
        double *attflt = (double *)resizeBuf(&attBuf, &attBufSize, attSize);
        cdiInqAttFlt(cdiID, varID, attname, attlen, attflt);
        len = (size_t)attlen;
        if ( atttype == CDI_DATATYPE_FLT32 )
            {
            float attflt_sp[8];
            float *pattflt_sp = (len > 8) ? (float*) malloc(len*sizeof(float)) : attflt_sp;
            for ( size_t i = 0; i < len; ++i ) pattflt_sp[i] = (float)attflt[i];
            cdf_put_att_float(fileID, ncvarID, attname, NC_FLOAT, len, pattflt_sp);
            if (len > 8) free(pattflt_sp);
            }
        else
            cdf_put_att_double(fileID, ncvarID, attname, NC_DOUBLE, len, attflt);
        }
    }

Free(attBuf);
}

static
void cdfDefGlobalAtts(stream_t *streamptr)
{
if ( streamptr->globalatts ) return;

int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;

cdfDefSource(streamptr);
cdfDefInstitut(streamptr);

int natts;
cdiInqNatts(vlistID, CDI_GLOBAL, &natts);

if ( natts > 0 && streamptr->ncmode == 2 ) cdf_redef(fileID);

cdfDefineAttributes(vlistID, CDI_GLOBAL, fileID, NC_GLOBAL);

if ( natts > 0 && streamptr->ncmode == 2 ) cdf_enddef(fileID);

streamptr->globalatts = 1;
}

static
void cdfDefLocalAtts(stream_t *streamptr)
{
int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;

if ( streamptr->localatts ) return;
if ( vlistInqInstitut(vlistID) != CDI_UNDEFID ) return;

streamptr->localatts = 1;

if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

for ( int varID = 0; varID < streamptr->nvars; varID++ )
    {
    int instID = vlistInqVarInstitut(vlistID, varID);
    if ( instID != CDI_UNDEFID )
        {
        int ncvarid = streamptr->vars[varID].ncvarid;
        const char *name = institutInqNamePtr(instID);
        if ( name )
            {
            size_t len = strlen(name);
            cdf_put_att_text(fileID, ncvarid, "institution", len, name);
            }
        }
    }

if ( streamptr->ncmode == 2 ) cdf_enddef(fileID);
}

static
void cdf_get_gmapvarname(int gridID, char *gmapvarname)
{
int pgridID = gridID;
char mapping[CDI_MAX_NAME]; mapping[0] = 0;
cdiGridInqKeyStr(pgridID, CDI_KEY_MAPNAME, CDI_MAX_NAME, mapping);

if ( !mapping[0] )
    {
    int projID = gridInqProj(gridID);
    if ( projID != CDI_UNDEFID )
        {
        pgridID = projID;
        cdiGridInqKeyStr(pgridID, CDI_KEY_MAPNAME, CDI_MAX_NAME, mapping);
        }
    }

if ( mapping[0] )
    cdiGridInqKeyStr(pgridID, CDI_KEY_MAPPING, CDI_MAX_NAME, gmapvarname);
}

static
int nc_grid_index(stream_t *streamptr, int gridID)
{
int index = 0;
int vlistID = streamptr->vlistID;
int ngrids = vlistNgrids(vlistID);
for ( index = 0; index < ngrids; ++index )
    if ( streamptr->ncgrid[index].gridID == gridID ) break;

assert(index < ngrids);

return index;
}

static
int cdfDefVar(stream_t *streamptr, int varID)
{
int ncvarid = -1;
int xid = CDI_UNDEFID, yid = CDI_UNDEFID;
size_t xsize = 0, ysize = 0;
int dims[4];
size_t chunks[4] = {0,0,0,0};
int ndims = 0;
int tablenum;
int dimorder[3];
size_t iax = 0;
char axis[5];

int fileID  = streamptr->fileID;

if ( CDI_Debug )
    Message("streamID = %d, fileID = %d, varID = %d", streamptr->self, fileID, varID);

if ( streamptr->vars[varID].ncvarid != CDI_UNDEFID )
    return streamptr->vars[varID].ncvarid;

int vlistID  = streamptr->vlistID;
int gridID   = vlistInqVarGrid(vlistID, varID);
int zaxisID  = vlistInqVarZaxis(vlistID, varID);
int timetype = vlistInqVarTimetype(vlistID, varID);
int code     = vlistInqVarCode(vlistID, varID);
int param    = vlistInqVarParam(vlistID, varID);
int pnum, pcat, pdis;
cdiDecodeParam(param, &pnum, &pcat, &pdis);

int chunktype = vlistInqVarChunkType(vlistID, varID);

vlistInqVarDimorder(vlistID, varID, &dimorder);

size_t gridsize = gridInqSize(gridID);
bool lchunk = (gridsize >= 16);
int gridtype  = gridInqType(gridID);
int gridindex = nc_grid_index(streamptr, gridID);
if ( gridtype != GRID_TRAJECTORY )
    {
    xid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
    yid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y];
    if ( xid != CDI_UNDEFID ) cdf_inq_dimlen(fileID, xid, &xsize);
    if ( yid != CDI_UNDEFID ) cdf_inq_dimlen(fileID, yid, &ysize);
    }

int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);
int zid = streamptr->zaxisID[zaxisindex];
bool zaxis_is_scalar = false;
if ( zid == CDI_UNDEFID ) zaxis_is_scalar = zaxisInqScalar(zaxisID) > 0;

if ( dimorder[0] != 3 ) lchunk = false;

if ( ((dimorder[0]>0)+(dimorder[1]>0)+(dimorder[2]>0)) < ((xid!=CDI_UNDEFID)+(yid!=CDI_UNDEFID)+(zid!=CDI_UNDEFID)) )
    {
    printf("zid=%d  yid=%d  xid=%d\n", zid, yid, xid);
    Error("Internal problem, dimension order missing!");
    }

int tid = streamptr->basetime.ncdimid;

if ( vlistHasTime(vlistID) && timetype != TIME_CONSTANT )
    {
    if ( tid == CDI_UNDEFID ) Error("Internal problem, time undefined!");
    chunks[ndims] = 1;
    dims[ndims++] = tid;
    axis[iax++] = 'T';
    }

size_t chunk_size_max = 65536;
if ( chunktype != CDI_CHUNK_LINES && gridsize > INT32_MAX )
    {
    if ( CDI_Debug ) fprintf(stderr, "gridsize > %d, changed chunktype to CDI_CHUNK_LINES!\n", INT32_MAX);
    chunktype = CDI_CHUNK_LINES;
    }

for ( int id = 0; id < 3; ++id )
    {
    if ( dimorder[id] == 3 && zid != CDI_UNDEFID )
        {
        axis[iax++] = 'Z';
        chunks[ndims] = 1;
        dims[ndims] = zid;
        ndims++;
        }
    else if ( dimorder[id] == 2 && yid != CDI_UNDEFID )
        {
        if ( chunktype == CDI_CHUNK_AUTO )
            chunks[ndims] = (chunk_size_max > gridsize) ? ysize : chunk_size_max/xsize;
        else
            chunks[ndims] = (chunktype == CDI_CHUNK_LINES) ? 1 : ysize;
        dims[ndims] = yid;
        ndims++;
        }
    else if ( dimorder[id] == 1 && xid != CDI_UNDEFID )
        {
        if ( chunktype == CDI_CHUNK_AUTO && yid == CDI_UNDEFID )
            chunks[ndims] = (chunk_size_max > xsize) ? xsize : chunk_size_max;
        else
            chunks[ndims] = (xsize > INT32_MAX) ? INT32_MAX : xsize;
        dims[ndims] = xid;
        ndims++;
        }
    }

if ( CDI_Debug )
    fprintf(stderr, "chunktype %d  chunks %zu %zu %zu %zu\n", chunktype, chunks[0], chunks[1], chunks[2], chunks[3]);

char varname[CDI_MAX_NAME];
char name[CDI_MAX_NAME]; name[0] = 0;
char longname[CDI_MAX_NAME]; longname[0] = 0;
char stdname[CDI_MAX_NAME]; stdname[0] = 0;
char units[CDI_MAX_NAME]; units[0] = 0;
if ( vlistInqVarNamePtr(vlistID, varID) ) vlistInqVarName(vlistID, varID, name);
vlistInqVarLongname(vlistID, varID, longname);
vlistInqVarStdname(vlistID, varID, stdname);
vlistInqVarUnits(vlistID, varID, units);

int tableID  = vlistInqVarTable(vlistID, varID);
if ( !name[0] ) tableInqEntry(tableID, code, -1, name, longname, units);
if ( name[0] )
    {
    sprintf(varname, "%s", name);
    char *varname2 = varname+strlen(varname);
    unsigned iz = 0;

    do
        {
        if ( iz ) sprintf(varname2, "_%u", iz+1);

        if ( nc_inq_varid(fileID, varname, &ncvarid) != NC_NOERR ) break;

        ++iz;
        }
    while ( iz <= 99 );

    if (iz > 99) Error("Variable name %s already exists!", name);

    if ( strcmp(name, varname) != 0 )
        Warning("Changed %s entry of variable name '%s' to '%s'!", (iz==1)?"double":"multiple", name, varname);

    strcpy(name, varname);
    }
else
    {
    if ( code < 0 ) code = -code;
    if ( pnum < 0 ) pnum = -pnum;

    if ( pdis == 255 )
        sprintf(varname, "var%d", code);
    else
        sprintf(varname, "param%d.%d.%d", pnum, pcat, pdis);

    char *varname2 = varname+strlen(varname);
    unsigned iz = 0;

    do
        {
        if ( iz ) sprintf(varname2, "_%u", iz+1);

        if ( nc_inq_varid(fileID, varname, &ncvarid) != NC_NOERR ) break;

        ++iz;
        }
    while ( iz <= 99 );

    if (iz > 99) Error("Variable name %s already exists!", name);

    strcpy(name, varname);
    code = 0;
    pdis = 255;
    }



int dtype = vlistInqVarDatatype(vlistID, varID);
int xtype = cdfDefDatatype(dtype, streamptr);

cdf_def_var(fileID, name, (nc_type) xtype, ndims, dims, &ncvarid);

#if  defined  (HAVE_NETCDF4)
if ( lchunk && (streamptr->filetype == CDI_FILETYPE_NC4 || streamptr->filetype == CDI_FILETYPE_NC4C) )
    cdf_def_var_chunking(fileID, ncvarid, NC_CHUNKED, chunks);
#endif

if ( streamptr->comptype == CDI_COMPRESS_ZIP )
    {
    if ( lchunk && (streamptr->filetype == CDI_FILETYPE_NC4 || streamptr->filetype == CDI_FILETYPE_NC4C) )
        {
        cdfDefVarDeflate(fileID, ncvarid, streamptr->complevel);
        }
    else
        {
        if ( lchunk )
            {
            static bool lwarn = true;
            if ( lwarn )
                {
                lwarn = false;
                Warning("Deflate compression is only available for NetCDF4!");
                }
            }
        }
    }

if ( *stdname )  cdf_put_att_text(fileID, ncvarid, "standard_name", strlen(stdname), stdname);
if ( *longname ) cdf_put_att_text(fileID, ncvarid, "long_name", strlen(longname), longname);
if ( *units )    cdf_put_att_text(fileID, ncvarid, "units", strlen(units), units);

if ( code > 0 && pdis == 255 )
    cdf_put_att_int(fileID, ncvarid, "code", NC_INT, 1, &code);

if ( pdis != 255 )
    {
    char paramstr[32];
    cdiParamToString(param, paramstr, sizeof(paramstr));
    cdf_put_att_text(fileID, ncvarid, "param", strlen(paramstr), paramstr);
    }

if ( tableID != CDI_UNDEFID )
    {
    tablenum = tableInqNum(tableID);
    if ( tablenum > 0 )
        cdf_put_att_int(fileID, ncvarid, "table", NC_INT, 1, &tablenum);
    }

char coordinates[CDI_MAX_NAME]; coordinates[0] = 0;

if ( zaxis_is_scalar || zaxisInqType(zaxisID) == ZAXIS_CHAR )
    {
    int nczvarID = streamptr->nczvarID[zaxisindex];
    if ( nczvarID != CDI_UNDEFID )
        {
        size_t len = strlen(coordinates);
        if ( len ) coordinates[len++] = ' ';
        cdf_inq_varname(fileID, nczvarID, coordinates+len);
        }
    }

if ( gridtype != GRID_GENERIC && gridtype != GRID_LONLAT && gridtype != GRID_GAUSSIAN &&
    gridtype != GRID_PROJECTION && gridtype != GRID_CURVILINEAR && gridtype != GRID_CHARXY )
    {
    size_t len = strlen(gridNamePtr(gridtype));
    if ( len > 0 )
        cdf_put_att_text(fileID, ncvarid, "CDI_grid_type", len, gridNamePtr(gridtype));
    }

char gmapvarname[CDI_MAX_NAME]; gmapvarname[0] = 0;

cdf_get_gmapvarname(gridID, gmapvarname);

if ( gmapvarname[0] ) cdf_put_att_text(fileID, ncvarid, "grid_mapping", strlen(gmapvarname), gmapvarname);

if ( gridtype == GRID_TRAJECTORY )
    {
    cdf_put_att_text(fileID, ncvarid, "coordinates", 9, "tlon tlat" );
    }
else if ( gridtype == GRID_LONLAT && xid == CDI_UNDEFID && yid == CDI_UNDEFID && gridsize == 1 )
    {
    int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
    int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
    if ( ncyvarID != CDI_UNDEFID )
        {
        size_t len = strlen(coordinates);
        if ( len ) coordinates[len++] = ' ';
        cdf_inq_varname(fileID, ncyvarID, coordinates+len);
        }
    if ( ncxvarID != CDI_UNDEFID )
        {
        size_t len = strlen(coordinates);
        if ( len ) coordinates[len++] = ' ';
        cdf_inq_varname(fileID, ncxvarID, coordinates+len);
        }
    }
else if ( gridtype == GRID_UNSTRUCTURED || gridtype == GRID_CURVILINEAR )
    {
    char cellarea[CDI_MAX_NAME] = "area: ";
    int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
    int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
    int ncavarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_A];

    if ( cdiCoordinatesLonLat )
        {
        if ( ncxvarID != CDI_UNDEFID )
            {
            size_t len = strlen(coordinates);
            if ( len ) coordinates[len++] = ' ';
            cdf_inq_varname(fileID, ncxvarID, coordinates+len);
            }
        if ( ncyvarID != CDI_UNDEFID )
            {
            size_t len = strlen(coordinates);
            if ( len ) coordinates[len++] = ' ';
            cdf_inq_varname(fileID, ncyvarID, coordinates+len);
            }
        }
    else
        {
        if ( ncyvarID != CDI_UNDEFID )
            {
            size_t len = strlen(coordinates);
            if ( len ) coordinates[len++] = ' ';
            cdf_inq_varname(fileID, ncyvarID, coordinates+len);
            }
        if ( ncxvarID != CDI_UNDEFID )
            {
            size_t len = strlen(coordinates);
            if ( len ) coordinates[len++] = ' ';
            cdf_inq_varname(fileID, ncxvarID, coordinates+len);
            }
        }

    if ( ncavarID != CDI_UNDEFID )
        {
        size_t len = strlen(cellarea);
        cdf_inq_varname(fileID, ncavarID, cellarea+len);
        len = strlen(cellarea);
        cdf_put_att_text(fileID, ncvarid, "cell_measures", len, cellarea);
        }

    if ( gridtype == GRID_UNSTRUCTURED )
        {
        int position = gridInqPosition(gridID);
        if ( position > 0 )
            cdf_put_att_int(fileID, ncvarid, "number_of_grid_in_reference", NC_INT, 1, &position);
        }
    }
else if ( gridtype == GRID_SPECTRAL || gridtype == GRID_FOURIER )
    {
    int gridTruncation = gridInqTrunc(gridID);
    axis[iax++] = '-';
    axis[iax++] = '-';
    cdf_put_att_text(fileID, ncvarid, "axis", iax, axis);
    cdf_put_att_int(fileID, ncvarid, "truncation", NC_INT, 1, &gridTruncation);
    }
else if ( gridtype == GRID_CHARXY )
    {
    if ( gridInqXIsc(gridID) )
        {
        int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
        size_t len = strlen(coordinates);
        if ( len ) coordinates[len++] = ' ';
        cdf_inq_varname(fileID, ncxvarID, coordinates+len);
        }
    else if ( gridInqYIsc(gridID) )
        {
        int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
        size_t len = strlen(coordinates);
        if ( len ) coordinates[len++] = ' ';
        cdf_inq_varname(fileID, ncyvarID, coordinates+len);
        }
    }

size_t len = strlen(coordinates);
if ( len ) cdf_put_att_text(fileID, ncvarid, "coordinates", len, coordinates);


    {
    int astype = NC_DOUBLE;

    double addoffset   = vlistInqVarAddoffset(vlistID, varID);
    double scalefactor = vlistInqVarScalefactor(vlistID, varID);
    bool laddoffset   = IS_NOT_EQUAL(addoffset, 0);
    bool lscalefactor = IS_NOT_EQUAL(scalefactor, 1);

    if ( laddoffset || lscalefactor )
        {
        if ( IS_EQUAL(addoffset,   (double) ((float) addoffset)) &&
            IS_EQUAL(scalefactor, (double) ((float) scalefactor)) )
            {
            astype = NC_FLOAT;
            }

        if ( xtype == (int) NC_FLOAT ) astype = NC_FLOAT;

        cdf_put_att_double(fileID, ncvarid, "add_offset",   (nc_type) astype, 1, &addoffset);
        cdf_put_att_double(fileID, ncvarid, "scale_factor", (nc_type) astype, 1, &scalefactor);
        }
    }

if ( dtype == CDI_DATATYPE_UINT8 && xtype == NC_BYTE )
    {
    int validrange[2] = {0, 255};
    cdf_put_att_int(fileID, ncvarid, "valid_range", NC_SHORT, 2, validrange);
    cdf_put_att_text(fileID, ncvarid, "_Unsigned", 4, "true");
    }

streamptr->vars[varID].ncvarid = ncvarid;

if ( vlistInqVarMissvalUsed(vlistID, varID) )
    cdfDefVarMissval(streamptr, varID, vlistInqVarDatatype(vlistID, varID), 0);

if ( zid == -1 )
    {
    if ( zaxisInqType(zaxisID) == ZAXIS_CLOUD_BASE          ||
        zaxisInqType(zaxisID) == ZAXIS_CLOUD_TOP           ||
        zaxisInqType(zaxisID) == ZAXIS_ISOTHERM_ZERO       ||
        zaxisInqType(zaxisID) == ZAXIS_TOA                 ||
        zaxisInqType(zaxisID) == ZAXIS_SEA_BOTTOM          ||
        zaxisInqType(zaxisID) == ZAXIS_LAKE_BOTTOM         ||
        zaxisInqType(zaxisID) == ZAXIS_SEDIMENT_BOTTOM     ||
        zaxisInqType(zaxisID) == ZAXIS_SEDIMENT_BOTTOM_TA  ||
        zaxisInqType(zaxisID) == ZAXIS_SEDIMENT_BOTTOM_TW  ||
        zaxisInqType(zaxisID) == ZAXIS_MIX_LAYER           ||
        zaxisInqType(zaxisID) == ZAXIS_ATMOSPHERE )
        {
        zaxisInqName(zaxisID, varname);
        cdf_put_att_text(fileID, ncvarid, "level_type", strlen(varname), varname);
        }
    }

int perturbationNumber, numberOfForecastsInEnsemble, typeOfEnsembleForecast;
if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_PERTURBATIONNUMBER, &perturbationNumber) == 0 )
    cdf_put_att_int(fileID, ncvarid, "realization", NC_INT, 1, &perturbationNumber);
if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_NUMBEROFFORECASTSINENSEMBLE, &numberOfForecastsInEnsemble) == 0 )
    cdf_put_att_int(fileID, ncvarid, "ensemble_members", NC_INT, 1, &numberOfForecastsInEnsemble);
if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_TYPEOFENSEMBLEFORECAST, &typeOfEnsembleForecast) == 0 )
    cdf_put_att_int(fileID, ncvarid, "forecast_init_type", NC_INT, 1, &typeOfEnsembleForecast);


cdfDefineAttributes(vlistID, varID, fileID, ncvarid);



return ncvarid;
}


void cdfEndDef(stream_t *streamptr)
{
cdfDefGlobalAtts(streamptr);
cdfDefLocalAtts(streamptr);

if ( streamptr->accessmode == 0 )
    {
    int fileID  = streamptr->fileID;
    if ( streamptr->ncmode == 2 ) cdf_redef(fileID);

    int nvars =  streamptr->nvars;
    for ( int varID = 0; varID < nvars; varID++ )
        cdfDefVar(streamptr, varID);

    if ( streamptr->ncmode == 2 )
        {
        if ( CDI_netcdf_hdr_pad == 0UL )
            cdf_enddef(fileID);
        else
            cdf__enddef(fileID, CDI_netcdf_hdr_pad);
        }

    streamptr->accessmode = 1;
    }
}

static
void cdfWriteGridTraj(stream_t *streamptr, int gridID)
{
int gridindex = nc_grid_index(streamptr, gridID);
int lonID = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
int latID = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y];

double xlon = gridInqXval(gridID, 0);
double xlat = gridInqYval(gridID, 0);
int tsID = streamptr->curTsID;
size_t index = (size_t)tsID;

int fileID = streamptr->fileID;
cdf_put_var1_double(fileID, lonID, &index, &xlon);
cdf_put_var1_double(fileID, latID, &index, &xlat);
}

static
void cdf_write_var_data(int fileID, int vlistID, int varID, int ncvarid, int dtype, size_t nvals, size_t xsize, size_t ysize,
                        bool swapxy, size_t *start, size_t *count, int memtype, const void *data, size_t nmiss)
{
const double *pdata_dp = (const double *) data;
double *mdata_dp = NULL;
double *sdata_dp = NULL;
const float *pdata_sp = (const float *) data;
float *mdata_sp = NULL;
float *sdata_sp = NULL;


    {
    double missval      = vlistInqVarMissval(vlistID, varID);
    double addoffset    = vlistInqVarAddoffset(vlistID, varID);
    double scalefactor  = vlistInqVarScalefactor(vlistID, varID);
    bool laddoffset     = IS_NOT_EQUAL(addoffset, 0);
    bool lscalefactor   = IS_NOT_EQUAL(scalefactor, 1);

    if ( laddoffset || lscalefactor )
        {
        if ( memtype == MEMTYPE_FLOAT )
            {
            mdata_sp = (float *) Malloc(nvals*sizeof(float));
            memcpy(mdata_sp, pdata_sp, nvals*sizeof(float));
            pdata_sp = mdata_sp;

            if ( nmiss > 0 )
                {
                for ( size_t i = 0; i < nvals; i++ )
                    {
                    double temp = mdata_sp[i];
                    if ( !DBL_IS_EQUAL(temp, missval) )
                        {
                        if ( laddoffset )   temp -= addoffset;
                        if ( lscalefactor ) temp /= scalefactor;
                        mdata_sp[i] = (float)temp;
                        }
                    }
                }
            else
                {
                for ( size_t i = 0; i < nvals; i++ )
                    {
                    double temp = mdata_sp[i];
                    if ( laddoffset )   temp -= addoffset;
                    if ( lscalefactor ) temp /= scalefactor;
                    mdata_sp[i] = (float)temp;
                    }
                }
            }
        else
            {
            mdata_dp = (double *) Malloc(nvals*sizeof(double));
            memcpy(mdata_dp, pdata_dp, nvals*sizeof(double));
            pdata_dp = mdata_dp;

            if ( nmiss > 0 )
                {
                for ( size_t i = 0; i < nvals; i++ )
                    {
                    if ( !DBL_IS_EQUAL(mdata_dp[i], missval) )
                        {
                        if ( laddoffset )   mdata_dp[i] -= addoffset;
                        if ( lscalefactor ) mdata_dp[i] /= scalefactor;
                        }
                    }
                }
            else
                {
                for ( size_t i = 0; i < nvals; i++ )
                    {
                    if ( laddoffset )   mdata_dp[i] -= addoffset;
                    if ( lscalefactor ) mdata_dp[i] /= scalefactor;
                    }
                }
            }
        }

    if ( dtype == CDI_DATATYPE_UINT8 || dtype == CDI_DATATYPE_INT8 ||
        dtype == CDI_DATATYPE_INT16 || dtype == CDI_DATATYPE_INT32 )
        {
        if ( memtype == MEMTYPE_FLOAT )
            {
            if ( mdata_sp == NULL )
                {
                mdata_sp = (float *) Malloc(nvals*sizeof(float));
                memcpy(mdata_sp, pdata_sp, nvals*sizeof(float));
                pdata_sp = mdata_sp;
                }

            for ( size_t i = 0; i < nvals; i++ ) mdata_sp[i] = roundf(mdata_sp[i]);

            if ( dtype == CDI_DATATYPE_UINT8 )
                {
                nc_type xtype;
                cdf_inq_vartype(fileID, ncvarid, &xtype);
                if ( xtype == NC_BYTE )
                    {
                    for ( size_t i = 0; i < nvals; ++i )
                        if ( mdata_sp[i] > 127 ) mdata_sp[i] -= 256;
                    }
                }
            }
        else
            {
            if ( mdata_dp == NULL )
                {
                mdata_dp = (double *) Malloc(nvals*sizeof(double));
                memcpy(mdata_dp, pdata_dp, nvals*sizeof(double));
                pdata_dp = mdata_dp;
                }

            for ( size_t i = 0; i < nvals; i++ ) mdata_dp[i] = round(mdata_dp[i]);

            if ( dtype == CDI_DATATYPE_UINT8 )
                {
                nc_type xtype;
                cdf_inq_vartype(fileID, ncvarid, &xtype);
                if ( xtype == NC_BYTE )
                    {
                    for ( size_t i = 0; i < nvals; ++i )
                        if ( mdata_dp[i] > 127 ) mdata_dp[i] -= 256;
                    }
                }
            }
        }

    if ( CDF_Debug && memtype != MEMTYPE_FLOAT )
        {
        double fmin =  1.0e200;
        double fmax = -1.0e200;
        for ( size_t i = 0; i < nvals; ++i )
            {
            if ( !DBL_IS_EQUAL(pdata_dp[i], missval) )
                {
                if ( pdata_dp[i] < fmin ) fmin = pdata_dp[i];
                if ( pdata_dp[i] > fmax ) fmax = pdata_dp[i];
                }
            }
        Message("nvals = %zu, nmiss = %d, missval = %g, minval = %g, maxval = %g",
                nvals, nmiss, missval, fmin, fmax);
        }
    }

if ( swapxy )
    {
    size_t gridsize = xsize*ysize;
    if ( memtype == MEMTYPE_FLOAT )
        {
        sdata_sp = (float *) Malloc(gridsize*sizeof(float));
        for ( size_t j = 0; j < ysize; ++j )
            for ( size_t i = 0; i < xsize; ++i )
            sdata_sp[i*ysize+j] = pdata_sp[j*xsize+i];
        pdata_sp = sdata_sp;
        }
    else
        {
        sdata_dp = (double *) Malloc(gridsize*sizeof (double));
        for ( size_t j = 0; j < ysize; ++j )
            for ( size_t i = 0; i < xsize; ++i )
            sdata_dp[i*ysize+j] = pdata_dp[j*xsize+i];
        pdata_dp = sdata_dp;
        }
    }

if ( memtype == MEMTYPE_FLOAT )
    cdf_put_vara_float(fileID, ncvarid, start, count, pdata_sp);
else
    cdf_put_vara_double(fileID, ncvarid, start, count, pdata_dp);

if ( mdata_dp ) Free(mdata_dp);
if ( sdata_dp ) Free(sdata_dp);
if ( mdata_sp ) Free(mdata_sp);
if ( sdata_sp ) Free(sdata_sp);
}


void cdf_write_var(stream_t *streamptr, int varID, int memtype, const void *data, size_t nmiss)
{
if ( streamptr->accessmode == 0 ) cdfEndDef(streamptr);

size_t xsize = 0, ysize = 0;
size_t size;
size_t start[5];
size_t count[5];
bool swapxy = false;
size_t ndims = 0;

if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamptr->self, varID);

int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;

long ntsteps = streamptr->ntsteps;
if ( CDI_Debug ) Message("ntsteps = %ld", ntsteps);

int ncvarid = cdfDefVar(streamptr, varID);

int gridID   = vlistInqVarGrid(vlistID, varID);
int zaxisID  = vlistInqVarZaxis(vlistID, varID);
int timetype = vlistInqVarTimetype(vlistID, varID);

int xid = CDI_UNDEFID, yid = CDI_UNDEFID;
if ( gridInqType(gridID) == GRID_TRAJECTORY )
    {
    cdfWriteGridTraj(streamptr, gridID);
    }
else
    {
    int gridindex = nc_grid_index(streamptr, gridID);
    xid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
    yid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y];
    }

int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);
int zid = streamptr->zaxisID[zaxisindex];

if ( vlistHasTime(vlistID) && timetype != TIME_CONSTANT )
    {
    start[ndims] = (size_t)ntsteps - 1;
    count[ndims] = 1;
    ndims++;
    }

if ( zid != CDI_UNDEFID )
    {
    start[ndims] = 0;
    count[ndims] = (size_t)zaxisInqSize(zaxisID);
    ndims++;
    }

if ( yid != CDI_UNDEFID )
    {
    start[ndims] = 0;
    cdf_inq_dimlen(fileID, yid, &size);

    count[ndims] = size;
    ndims++;
    }

if ( xid != CDI_UNDEFID )
    {
    start[ndims] = 0;
    cdf_inq_dimlen(fileID, xid, &size);

    count[ndims] = size;
    ndims++;
    }

if ( CDI_Debug )
    for (size_t idim = 0; idim < ndims; idim++)
    Message("dim = %d  start = %d  count = %d", idim, start[idim], count[idim]);

if ( streamptr->ncmode == 1 )
    {
    cdf_enddef(fileID);
    streamptr->ncmode = 2;
    }

int dtype = vlistInqVarDatatype(vlistID, varID);

if ( nmiss > 0 ) cdfDefVarMissval(streamptr, varID, dtype, 1);

size_t nvals = gridInqSize(gridID) * (size_t)(zaxisInqSize(zaxisID));

cdf_write_var_data(fileID, vlistID, varID, ncvarid, dtype, nvals, xsize, ysize, swapxy, start, count, memtype, data, nmiss);
}


void cdf_write_var_chunk(stream_t *streamptr, int varID, int memtype,
                        const int rect[][2], const void *data, size_t nmiss)
{
if ( streamptr->accessmode == 0 ) cdfEndDef(streamptr);

int xid = CDI_UNDEFID, yid = CDI_UNDEFID;
size_t xsize = 0, ysize = 0;
size_t start[5];
size_t count[5];
bool swapxy = false;
size_t ndims = 0;
int streamID = streamptr->self;

if ( CDI_Debug )
    Message("streamID = %d  varID = %d", streamID, varID);

int vlistID = streamInqVlist(streamID);
int fileID  = streamInqFileID(streamID);

long ntsteps = streamptr->ntsteps;
if ( CDI_Debug ) Message("ntsteps = %ld", ntsteps);

int ncvarid = cdfDefVar(streamptr, varID);

int gridID   = vlistInqVarGrid(vlistID, varID);
int zaxisID  = vlistInqVarZaxis(vlistID, varID);
int timetype = vlistInqVarTimetype(vlistID, varID);

if ( gridInqType(gridID) == GRID_TRAJECTORY )
    {
    cdfWriteGridTraj(streamptr, gridID);
    }
else
    {
    int gridindex = nc_grid_index(streamptr, gridID);
    xid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
    yid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y];
    }

int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);
int zid = streamptr->zaxisID[zaxisindex];

if ( vlistHasTime(vlistID) && timetype != TIME_CONSTANT )
    {
    start[ndims] = (size_t)ntsteps - 1;
    count[ndims] = 1;
    ndims++;
    }
if ( zid != CDI_UNDEFID )
    {
    int size = zaxisInqSize(zaxisID);
    xassert(rect[2][0] >= 0 && rect[2][0] <= rect[2][1]
            && rect[2][1] <= size);
    start[ndims] = (size_t)rect[2][0];
    count[ndims] = (size_t)rect[2][1] - (size_t)rect[2][0] + 1;
    ndims++;
    }
if ( yid != CDI_UNDEFID )
    {
    size_t size;
    cdf_inq_dimlen(fileID, yid, &size);
    xassert(rect[1][0] >= 0 && rect[1][0] <= rect[1][1]
            && (size_t)rect[1][1] <= size);
    start[ndims] = (size_t)rect[1][0];
    count[ndims] = (size_t)rect[1][1] - (size_t)rect[1][0] + 1;
    ndims++;
    }
if ( xid != CDI_UNDEFID )
    {
    size_t size;
    cdf_inq_dimlen(fileID, xid, &size);
    xassert(rect[0][0] >= 0 && rect[0][0] <= rect[0][1]
            && (size_t)rect[0][1] <= size);
    start[ndims] = (size_t)rect[0][0];
    count[ndims] = (size_t)rect[0][1] - (size_t)rect[0][0] + 1;
    ndims++;
    }

if ( CDI_Debug )
    for (size_t idim = 0; idim < ndims; idim++)
    Message("dim = %d  start = %d  count = %d", idim, start[idim], count[idim]);

if ( streamptr->ncmode == 1 )
    {
    cdf_enddef(fileID);
    streamptr->ncmode = 2;
    }

int dtype = vlistInqVarDatatype(vlistID, varID);

if ( nmiss > 0 ) cdfDefVarMissval(streamptr, varID, dtype, 1);

size_t nvals = gridInqSize(gridID) * (size_t)(zaxisInqSize(zaxisID));

cdf_write_var_data(fileID, vlistID, varID, ncvarid, dtype, nvals,
                    xsize, ysize, swapxy, start, count, memtype, data, nmiss);
}


void cdf_write_var_slice(stream_t *streamptr, int varID, int levelID, int memtype, const void *data, size_t nmiss)
{
if ( streamptr->accessmode == 0 ) cdfEndDef(streamptr);

size_t xsize = 0, ysize = 0;
size_t start[5];
size_t count[5];
int dimorder[3];
int xid = CDI_UNDEFID, yid = CDI_UNDEFID;

if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamptr->self, varID);

int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;

long ntsteps = streamptr->ntsteps;
if ( CDI_Debug ) Message("ntsteps = %ld", ntsteps);

int ncvarid = cdfDefVar(streamptr, varID);

int gridID   = vlistInqVarGrid(vlistID, varID);
int zaxisID  = vlistInqVarZaxis(vlistID, varID);
int timetype = vlistInqVarTimetype(vlistID, varID);
vlistInqVarDimorder(vlistID, varID, &dimorder);

if ( gridInqType(gridID) == GRID_TRAJECTORY )
    {
    cdfWriteGridTraj(streamptr, gridID);
    }
else
    {
    int gridindex = nc_grid_index(streamptr, gridID);
    xid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
    yid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y];
    }

int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);
int zid = streamptr->zaxisID[zaxisindex];

bool swapxy = (dimorder[2] == 2 || dimorder[0] == 1) && xid != CDI_UNDEFID && yid != CDI_UNDEFID;

size_t ndims = 0;
if ( vlistHasTime(vlistID) && timetype != TIME_CONSTANT )
    {
    start[ndims] = (size_t)ntsteps - 1;
    count[ndims] = 1;
    ndims++;
    }

for ( int id = 0; id < 3; ++id )
    {
    if ( dimorder[id] == 3 && zid != CDI_UNDEFID )
        {
        start[ndims] = (size_t)levelID;
        count[ndims] = 1;
        ndims++;
        }
    else if ( dimorder[id] == 2 && yid != CDI_UNDEFID )
        {
        start[ndims] = 0;
        cdf_inq_dimlen(fileID, yid, &ysize);
        count[ndims] = ysize;
        ndims++;
        }
    else if ( dimorder[id] == 1 && xid != CDI_UNDEFID )
        {
        start[ndims] = 0;
        cdf_inq_dimlen(fileID, xid, &xsize);
        count[ndims] = xsize;
        ndims++;
        }
    }

if ( CDI_Debug )
    for (size_t idim = 0; idim < ndims; idim++)
    Message("dim = %d  start = %d  count = %d", idim, start[idim], count[idim]);

int dtype = vlistInqVarDatatype(vlistID, varID);

if ( nmiss > 0 ) cdfDefVarMissval(streamptr, varID, dtype, 1);

size_t nvals = gridInqSize(gridID);

cdf_write_var_data(fileID, vlistID, varID, ncvarid, dtype, nvals, xsize, ysize, swapxy, start, count, memtype, data, nmiss);
}


void cdf_write_record(stream_t *streamptr, int memtype, const void *data, size_t nmiss)
{
int varID   = streamptr->record->varID;
int levelID = streamptr->record->levelID;

cdf_write_var_slice(streamptr, varID, levelID, memtype, data, nmiss);
}

#endif


#ifdef HAVE_CONFIG_H
#endif

#ifdef HAVE_LIBNETCDF

#include <limits.h>
#include <float.h>



static
void cdfReadGridTraj(stream_t *streamptr, int gridID)
{
int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;

int gridindex = vlistGridIndex(vlistID, gridID);
int lonID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
int latID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];

int tsID = streamptr->curTsID;
size_t index = (size_t)tsID;

double xlon, xlat;
cdf_get_var1_double(fileID, lonID, &index, &xlon);
cdf_get_var1_double(fileID, latID, &index, &xlat);

gridDefXvals(gridID, &xlon);
gridDefYvals(gridID, &xlat);
}

static
void cdfGetSlapDescription(stream_t *streamptr, int varID, size_t (*start)[4], size_t (*count)[4])
{
int vlistID = streamptr->vlistID;
int tsID = streamptr->curTsID;
int gridID = vlistInqVarGrid(vlistID, varID);
int zaxisID = vlistInqVarZaxis(vlistID, varID);
int timetype = vlistInqVarTimetype(vlistID, varID);
int gridindex = vlistGridIndex(vlistID, gridID);

if ( CDI_Debug ) Message("tsID = %d", tsID);

int xid = CDI_UNDEFID, yid = CDI_UNDEFID;
if ( gridInqType(gridID) == GRID_TRAJECTORY )
    {
    cdfReadGridTraj(streamptr, gridID);
    }
else
    {
    xid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
    yid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y];
    }
int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);
int zid = streamptr->zaxisID[zaxisindex];

int ndims = 0;
#define addDimension(startCoord, length) do \
    { \
    (*start)[ndims] = startCoord; \
    (*count)[ndims] = length; \
    ndims++; \
    } while(0)
if ( timetype != TIME_CONSTANT ) addDimension((size_t)tsID, 1);
if ( zid != CDI_UNDEFID ) addDimension(0, (size_t)zaxisInqSize(zaxisID));
if ( yid != CDI_UNDEFID ) addDimension(0, gridInqYsize(gridID));
if ( xid != CDI_UNDEFID ) addDimension(0, gridInqXsize(gridID));
#undef addDimension

assert(ndims <= (int)(sizeof(*start)/sizeof(**start)));
assert(ndims <= (int)(sizeof(*count)/sizeof(**count)));

if ( CDI_Debug )
    for (int idim = 0; idim < ndims; idim++)
    Message("dim = %d  start = %d  count = %d", idim, start[idim], count[idim]);
}



static
size_t cdfDoInputDataTransformationDP(size_t valueCount, double *data, bool haveMissVal, double missVal, double scaleFactor, double offset, double validMin, double validMax)
{
const bool haveOffset   = IS_NOT_EQUAL(offset, 0);
const bool haveScaleFactor = IS_NOT_EQUAL(scaleFactor, 1);
size_t missValCount = 0;

if ( IS_EQUAL(validMin, VALIDMISS) ) validMin = DBL_MIN;
if ( IS_EQUAL(validMax, VALIDMISS) ) validMax = DBL_MAX;

bool haveRangeCheck = (IS_NOT_EQUAL(validMax, DBL_MAX)) | (IS_NOT_EQUAL(validMin,DBL_MIN));
assert(!haveRangeCheck || haveMissVal);

switch ((int)haveMissVal | ((int)haveScaleFactor << 1)
        | ((int)haveOffset << 2) | ((int)haveRangeCheck << 3))
    {
    case 15:
    for ( size_t i = 0; i < valueCount; i++ )
        {
        int outOfRange = data[i] < validMin || data[i] > validMax;
        int isMissVal = DBL_IS_EQUAL(data[i], missVal);
        missValCount += (size_t)(outOfRange | isMissVal);
        data[i] = outOfRange ? missVal
            : isMissVal ? data[i] : data[i] * scaleFactor + offset;
        }
    break;
    case 13:
    for ( size_t i = 0; i < valueCount; i++ )
        {
        int outOfRange = data[i] < validMin || data[i] > validMax;
        int isMissVal = DBL_IS_EQUAL(data[i], missVal);
        missValCount += (size_t)(outOfRange | isMissVal);
        data[i] = outOfRange ? missVal
            : isMissVal ? data[i] : data[i] + offset;
        }
    break;
    case 11:
    for ( size_t i = 0; i < valueCount; i++ )
        {
        int outOfRange = data[i] < validMin || data[i] > validMax;
        int isMissVal = DBL_IS_EQUAL(data[i], missVal);
        missValCount += (size_t)(outOfRange | isMissVal);
        data[i] = outOfRange ? missVal
            : isMissVal ? data[i] : data[i] * scaleFactor;
        }
    break;
    case 9:
    for ( size_t i = 0; i < valueCount; i++ )
        {
        int outOfRange = data[i] < validMin || data[i] > validMax;
        int isMissVal = DBL_IS_EQUAL(data[i], missVal);
        missValCount += (size_t)(outOfRange | isMissVal);
        data[i] = outOfRange ? missVal : data[i];
        }
    break;
    case 7:
    for ( size_t i = 0; i < valueCount; i++ )
        if ( DBL_IS_EQUAL(data[i], missVal) )
        missValCount++;
        else
        data[i] = data[i] * scaleFactor + offset;
    break;
    case 6:
    for ( size_t i = 0; i < valueCount; i++ )
        data[i] = data[i] * scaleFactor + offset;
    break;
    case 5:
    for ( size_t i = 0; i < valueCount; i++ )
        if ( DBL_IS_EQUAL(data[i], missVal) )
        missValCount++;
        else
        data[i] += offset;
    break;
    case 4:
    for ( size_t i = 0; i < valueCount; i++ )
        data[i] += offset;
    break;
    case 3:
    for ( size_t i = 0; i < valueCount; i++ )
        if ( DBL_IS_EQUAL(data[i], missVal) )
        missValCount++;
        else
        data[i] *= scaleFactor;
    break;
    case 2:
    for ( size_t i = 0; i < valueCount; i++ )
        data[i] *= scaleFactor;
    break;
    case 1:
    for ( size_t i = 0; i < valueCount; i++ )
        missValCount += (unsigned)DBL_IS_EQUAL(data[i], missVal);
    break;
    }

return missValCount;
}

static
size_t cdfDoInputDataTransformationSP(size_t valueCount, float *data, bool haveMissVal, double missVal, double scaleFactor, double offset, double validMin, double validMax)
{
const bool haveOffset   = IS_NOT_EQUAL(offset, 0);
const bool haveScaleFactor = IS_NOT_EQUAL(scaleFactor, 1);
size_t missValCount = 0;

if ( IS_EQUAL(validMin, VALIDMISS) ) validMin = DBL_MIN;
if ( IS_EQUAL(validMax, VALIDMISS) ) validMax = DBL_MAX;

bool haveRangeCheck = (IS_NOT_EQUAL(validMax, DBL_MAX)) | (IS_NOT_EQUAL(validMin,DBL_MIN));
assert(!haveRangeCheck || haveMissVal);

switch ((int)haveMissVal | ((int)haveScaleFactor << 1)
        | ((int)haveOffset << 2) | ((int)haveRangeCheck << 3))
    {
    case 15:
    for ( size_t i = 0; i < valueCount; i++ )
        {
        int outOfRange = data[i] < validMin || data[i] > validMax;
        int isMissVal = DBL_IS_EQUAL(data[i], missVal);
        missValCount += (size_t)(outOfRange | isMissVal);
        data[i] = outOfRange ? (float)missVal
            : isMissVal ? data[i] : (float)(data[i] * scaleFactor + offset);
        }
    break;
    case 13:
    for ( size_t i = 0; i < valueCount; i++ )
        {
        int outOfRange = data[i] < validMin || data[i] > validMax;
        int isMissVal = DBL_IS_EQUAL(data[i], missVal);
        missValCount += (size_t)(outOfRange | isMissVal);
        data[i] = outOfRange ? (float)missVal
            : isMissVal ? data[i] : (float)(data[i] + offset);
        }
    break;
    case 11:
    for ( size_t i = 0; i < valueCount; i++ )
        {
        int outOfRange = data[i] < validMin || data[i] > validMax;
        int isMissVal = DBL_IS_EQUAL(data[i], missVal);
        missValCount += (size_t)(outOfRange | isMissVal);
        data[i] = outOfRange ? (float)missVal
            : isMissVal ? data[i] : (float)(data[i] * scaleFactor);
        }
    break;
    case 9:
    for ( size_t i = 0; i < valueCount; i++ )
        {
        int outOfRange = data[i] < validMin || data[i] > validMax;
        int isMissVal = DBL_IS_EQUAL(data[i], missVal);
        missValCount += (size_t)(outOfRange | isMissVal);
        data[i] = outOfRange ? (float)missVal : data[i];
        }
    break;
    case 7:
    for ( size_t i = 0; i < valueCount; i++ )
        if ( DBL_IS_EQUAL(data[i], missVal) )
        missValCount++;
        else
        data[i] = (float)(data[i] * scaleFactor + offset);
    break;
    case 6:
    for ( size_t i = 0; i < valueCount; i++ )
        data[i] = (float)(data[i] * scaleFactor + offset);
    break;
    case 5:
    for ( size_t i = 0; i < valueCount; i++ )
        if ( DBL_IS_EQUAL(data[i], missVal) )
        missValCount++;
        else
        data[i] = (float)(data[i] + offset);
    break;
    case 4:
    for ( size_t i = 0; i < valueCount; i++ )
        data[i] = (float)(data[i] + offset);
    break;
    case 3:
    for ( size_t i = 0; i < valueCount; i++ )
        if ( DBL_IS_EQUAL(data[i], missVal) )
        missValCount++;
        else
        data[i] = (float)(data[i] * scaleFactor);
    break;
    case 2:
    for ( size_t i = 0; i < valueCount; i++ )
        data[i] = (float)(data[i] * scaleFactor);
    break;
    case 1:
    for ( size_t i = 0; i < valueCount; i++ )
        missValCount += (unsigned)DBL_IS_EQUAL(data[i], missVal);
    break;
    }

return missValCount;
}

static
size_t min_size(size_t a, size_t b)
{
return a < b ? a : b;
}

static
void transpose2dArrayDP(size_t inWidth, size_t inHeight, double *data)
{
const size_t cacheBlockSize = 256;

double **out = (double**) malloc(inWidth*sizeof(double*));
double **temp = (double**) malloc(inHeight*sizeof(double*));
temp[0] = (double *) malloc(inHeight*inWidth*sizeof(double));
memcpy(temp[0], data, inHeight*inWidth*sizeof(double));
for (size_t i = 0; i < inWidth; i++) out[i] = data + (inHeight*i);
for (size_t i = 1; i < inHeight; i++) temp[i] = temp[0] + (inWidth*i);



for ( size_t yBlock = 0; yBlock < inHeight; yBlock += cacheBlockSize )
    for ( size_t xBlock = 0; xBlock < inWidth; xBlock += cacheBlockSize )
    for ( size_t y = yBlock, yEnd = min_size(yBlock + cacheBlockSize, inHeight); y < yEnd; y++ )
        for ( size_t x = xBlock, xEnd = min_size(xBlock + cacheBlockSize, inWidth); x < xEnd; x++ )
        {
            out[x][y] = temp[y][x];
        }

free(out);
free(temp[0]);
free(temp);
}

static
void transpose2dArraySP(size_t inWidth, size_t inHeight, float *data)
{
const size_t cacheBlockSize = 256;

float **out = (float**) malloc(inWidth*sizeof(float*));
float **temp = (float**) malloc(inHeight*sizeof(float*));
temp[0] = (float *) malloc(inHeight*inWidth*sizeof(float));
memcpy(temp[0], data, inHeight*inWidth*sizeof(float));
for (size_t i = 0; i < inWidth; i++) out[i] = data + (inHeight*i);
for (size_t i = 1; i < inHeight; i++) temp[i] = temp[0] + (inWidth*i);



for ( size_t yBlock = 0; yBlock < inHeight; yBlock += cacheBlockSize )
    for ( size_t xBlock = 0; xBlock < inWidth; xBlock += cacheBlockSize )
    for ( size_t y = yBlock, yEnd = min_size(yBlock + cacheBlockSize, inHeight); y < yEnd; y++ )
        for ( size_t x = xBlock, xEnd = min_size(xBlock + cacheBlockSize, inWidth); x < xEnd; x++ )
        {
            out[x][y] = temp[y][x];
        }

free(out);
free(temp[0]);
free(temp);
}

static
void cdfInqDimIds(stream_t *streamptr, int varId, int (*outDimIds)[3])
{
int gridId = vlistInqVarGrid(streamptr->vlistID, varId);
int gridindex = vlistGridIndex(streamptr->vlistID, gridId);

(*outDimIds)[0] = (*outDimIds)[1] = (*outDimIds)[2] = CDI_UNDEFID;
switch ( gridInqType(gridId) )
    {
    case GRID_TRAJECTORY:
        cdfReadGridTraj(streamptr, gridId);
        break;

    case GRID_UNSTRUCTURED:
        (*outDimIds)[0] = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
        break;

    default:
        (*outDimIds)[0] = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
        (*outDimIds)[1] = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y];
        break;
    }

int zaxisID = vlistInqVarZaxis(streamptr->vlistID, varId);
int zaxisindex = vlistZaxisIndex(streamptr->vlistID, zaxisID);
(*outDimIds)[2] = streamptr->zaxisID[zaxisindex];
}

static
int cdfGetSkipDim(int fileId, int ncvarid, int (*dimIds)[3])
{
if((*dimIds)[0] != CDI_UNDEFID) return 0;
if((*dimIds)[1] != CDI_UNDEFID) return 0;
int nvdims;
cdf_inq_varndims(fileId, ncvarid, &nvdims);
if(nvdims != 3) return 0;

int varDimIds[3];
cdf_inq_vardimid(fileId, ncvarid, varDimIds);
size_t size = 0;
if ( (*dimIds)[2] == varDimIds[2] )
    {
    cdf_inq_dimlen(fileId, varDimIds[1], &size);
    if ( size == 1 ) return 1;
    }
else if ( (*dimIds)[2] == varDimIds[1] )
    {
    cdf_inq_dimlen(fileId, varDimIds[2], &size);
    if ( size == 1 ) return 2;
    }
return 0;
}

static
void cdfGetSliceSlapDescription(stream_t *streamptr, int varId, int levelId, bool *outSwapXY, size_t (*start)[4], size_t (*count)[4])
{
int tsID = streamptr->curTsID;
if ( CDI_Debug ) Message("tsID = %d", tsID);

int fileId = streamptr->fileID;
int vlistId = streamptr->vlistID;
int ncvarid = streamptr->vars[varId].ncvarid;

int gridId = vlistInqVarGrid(vlistId, varId);
int timetype = vlistInqVarTimetype(vlistId, varId);
size_t gridsize = gridInqSize(gridId);

streamptr->numvals += gridsize;

int dimIds[3];
cdfInqDimIds(streamptr, varId, &dimIds);

int skipdim = cdfGetSkipDim(fileId, ncvarid, &dimIds);

int dimorder[3];
vlistInqVarDimorder(vlistId, varId, &dimorder);

*outSwapXY = (dimorder[2] == 2 || dimorder[0] == 1) && dimIds[0] != CDI_UNDEFID && dimIds[1] != CDI_UNDEFID ;

int ndims = 0;

#define addDimension(startIndex, extent) do {   \
    (*start)[ndims] = startIndex; \
    (*count)[ndims] = extent; \
    ndims++; \
} while(0)

if ( timetype != TIME_CONSTANT ) addDimension((size_t)tsID, 1);
if ( skipdim == 1 ) addDimension(0, 1);

for ( int id = 0; id < 3; ++id )
    {
    size_t size;
    int curDimId = dimIds[dimorder[id]-1];
    if ( curDimId == CDI_UNDEFID ) continue;
    switch ( dimorder[id] )
        {
        case 1:
        case 2:
            cdf_inq_dimlen(fileId, curDimId, &size);
            addDimension(0, size);
            break;

        case 3:
            addDimension((size_t)levelId, 1);
            break;

        default:
            Error("Internal error: Malformed dimension order encountered. Please report this bug.\n");
        }
    }

if ( skipdim == 2 ) addDimension(0, 1);

assert(ndims <= (int)(sizeof(*start)/sizeof(**start)));
assert(ndims <= (int)(sizeof(*count)/sizeof(**count)));

#undef addDimension

if ( CDI_Debug )
    for (int idim = 0; idim < ndims; idim++)
    Message("dim = %d  start = %d  count = %d", idim, (*start)[idim], (*count)[idim]);

int nvdims;
cdf_inq_varndims(fileId, ncvarid, &nvdims);

if ( nvdims != ndims )
    Error("Internal error, variable %s has an unsupported array structure!", vlistInqVarNamePtr(vlistId, varId));
}

static
void cdfReadVarDP(stream_t *streamptr, int varID, double *data, size_t *nmiss)
{
if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamptr->self, varID);

int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;

int ncvarid = streamptr->vars[varID].ncvarid;

int gridID  = vlistInqVarGrid(vlistID, varID);
int zaxisID = vlistInqVarZaxis(vlistID, varID);

size_t start[4];
size_t count[4];
cdfGetSlapDescription(streamptr, varID, &start, &count);

cdf_get_vara_double(fileID, ncvarid, start, count, data);

size_t size = gridInqSize(gridID)*(size_t)zaxisInqSize(zaxisID);
double missval = vlistInqVarMissval(vlistID, varID);
const bool haveMissVal = vlistInqVarMissvalUsed(vlistID, varID);
double validRange[2];
if (!(haveMissVal && vlistInqVarValidrange(vlistID, varID, validRange)))
    validRange[0] = DBL_MIN, validRange[1] = DBL_MAX;
double addoffset   = vlistInqVarAddoffset(vlistID, varID);
double scalefactor = vlistInqVarScalefactor(vlistID, varID);
size_t nmiss_ = cdfDoInputDataTransformationDP(size, data, haveMissVal, missval, scalefactor, addoffset, validRange[0], validRange[1]);
assert(nmiss_ <= INT_MAX);
*nmiss = (int)nmiss_;
}

static
void cdfReadVarSP(stream_t *streamptr, int varID, float *data, size_t *nmiss)
{
if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamptr->self, varID);

int vlistID = streamptr->vlistID;
int fileID  = streamptr->fileID;

int ncvarid = streamptr->vars[varID].ncvarid;

int gridID  = vlistInqVarGrid(vlistID, varID);
int zaxisID = vlistInqVarZaxis(vlistID, varID);

size_t start[4];
size_t count[4];
cdfGetSlapDescription(streamptr, varID, &start, &count);

cdf_get_vara_float(fileID, ncvarid, start, count, data);

size_t size = gridInqSize(gridID) * (size_t)zaxisInqSize(zaxisID);
double missval = vlistInqVarMissval(vlistID, varID);
const bool haveMissVal = vlistInqVarMissvalUsed(vlistID, varID);
double validRange[2];
if (!(haveMissVal && vlistInqVarValidrange(vlistID, varID, validRange)))
    validRange[0] = DBL_MIN, validRange[1] = DBL_MAX;
double addoffset   = vlistInqVarAddoffset(vlistID, varID);
double scalefactor = vlistInqVarScalefactor(vlistID, varID);
size_t nmiss_ = cdfDoInputDataTransformationSP(size, data, haveMissVal, missval, scalefactor, addoffset, validRange[0], validRange[1]);
assert(nmiss_ <= INT_MAX);
*nmiss = (int)nmiss_;
}


void cdf_read_var(stream_t *streamptr, int varID, int memtype, void *data, size_t *nmiss)
{
if ( memtype == MEMTYPE_DOUBLE )
    cdfReadVarDP(streamptr, varID, (double*) data, nmiss);
else
    cdfReadVarSP(streamptr, varID, (float*) data, nmiss);
}

static
void cdfReadVarSliceDP(stream_t *streamptr, int varID, int levelID, double *data, size_t *nmiss)
{
if ( CDI_Debug )
    Message("streamID = %d  varID = %d  levelID = %d", streamptr->self, varID, levelID);

int vlistID = streamptr->vlistID;
int fileID = streamptr->fileID;

size_t start[4], count[4];
bool swapxy;
cdfGetSliceSlapDescription(streamptr, varID, levelID, &swapxy, &start, &count);

int ncvarid = streamptr->vars[varID].ncvarid;
int gridId = vlistInqVarGrid(vlistID, varID);
size_t gridsize = gridInqSize(gridId);
size_t xsize = gridInqXsize(gridId);
size_t ysize = gridInqYsize(gridId);

if ( vlistInqVarDatatype(vlistID, varID) == CDI_DATATYPE_FLT32 )
    {
    float *data_fp = (float *) Malloc(gridsize*sizeof(*data_fp));
    cdf_get_vara_float(fileID, ncvarid, start, count, data_fp);
    for ( size_t i = 0; i < gridsize; i++ )
        data[i] = (double) data_fp[i];
    Free(data_fp);
    }
else
    {
    cdf_get_vara_double(fileID, ncvarid, start, count, data);

    if ( vlistInqVarDatatype(vlistID, varID) == CDI_DATATYPE_UINT8 )
        {
        nc_type xtype;
        cdf_inq_vartype(fileID, ncvarid, &xtype);
        if ( xtype == NC_BYTE )
            {
            for ( size_t i = 0; i < gridsize; i++ )
                if ( data[i] < 0 ) data[i] += 256;
            }
        }
    }

if ( swapxy ) transpose2dArrayDP(ysize, xsize, data);

double missval = vlistInqVarMissval(vlistID, varID);
const bool haveMissVal = vlistInqVarMissvalUsed(vlistID, varID);
double validRange[2];
if (!(haveMissVal && vlistInqVarValidrange(vlistID, varID, validRange)))
    validRange[0] = DBL_MIN, validRange[1] = DBL_MAX;
double addoffset   = vlistInqVarAddoffset(vlistID, varID);
double scalefactor = vlistInqVarScalefactor(vlistID, varID);
size_t nmiss_ = cdfDoInputDataTransformationDP(gridsize, data, haveMissVal, missval, scalefactor, addoffset, validRange[0], validRange[1]);
assert(nmiss_ <= INT_MAX);
*nmiss = (int)nmiss_;
}

static
void cdfReadVarSliceSP(stream_t *streamptr, int varID, int levelID, float *data, size_t *nmiss)
{
if ( CDI_Debug )
    Message("streamID = %d  varID = %d  levelID = %d", streamptr->self, varID, levelID);

int vlistID = streamptr->vlistID;
int fileID = streamptr->fileID;

size_t start[4], count[4];
bool swapxy;
cdfGetSliceSlapDescription(streamptr, varID, levelID, &swapxy, &start, &count);

int ncvarid = streamptr->vars[varID].ncvarid;
int gridId = vlistInqVarGrid(vlistID, varID);
size_t gridsize = gridInqSize(gridId);
size_t xsize = gridInqXsize(gridId);
size_t ysize = gridInqYsize(gridId);

if ( vlistInqVarDatatype(vlistID, varID) == CDI_DATATYPE_FLT64 )
    {
    double *data_dp = (double *) Malloc(gridsize*sizeof(*data_dp));
    cdf_get_vara_double(fileID, ncvarid, start, count, data_dp);
    for ( size_t i = 0; i < gridsize; i++ )
        data[i] = (float) data_dp[i];
    Free(data_dp);
    }
else
    {
    cdf_get_vara_float(fileID, ncvarid, start, count, data);

    if ( vlistInqVarDatatype(vlistID, varID) == CDI_DATATYPE_UINT8 )
        {
        nc_type xtype;
        cdf_inq_vartype(fileID, ncvarid, &xtype);
        if ( xtype == NC_BYTE )
            {
            for ( size_t i = 0; i < gridsize; i++ )
                if ( data[i] < 0 ) data[i] += 256;
            }
        }
    }

if ( swapxy ) transpose2dArraySP(ysize, xsize, data);

double missval = vlistInqVarMissval(vlistID, varID);
bool haveMissVal = vlistInqVarMissvalUsed(vlistID, varID);
double validRange[2];
if (!(haveMissVal && vlistInqVarValidrange(vlistID, varID, validRange)))
    validRange[0] = DBL_MIN, validRange[1] = DBL_MAX;
double addoffset   = vlistInqVarAddoffset(vlistID, varID);
double scalefactor = vlistInqVarScalefactor(vlistID, varID);
size_t nmiss_ = cdfDoInputDataTransformationSP(gridsize, data, haveMissVal, missval, scalefactor, addoffset, validRange[0], validRange[1]);
assert(nmiss_ <= INT_MAX);
*nmiss = (int)nmiss_;
}


void cdf_read_var_slice(stream_t *streamptr, int varID, int levelID, int memtype, void *data, size_t *nmiss)
{
if ( memtype == MEMTYPE_DOUBLE )
    cdfReadVarSliceDP(streamptr, varID, levelID, (double*) data, nmiss);
else
    cdfReadVarSliceSP(streamptr, varID, levelID, (float*) data, nmiss);
}


void cdf_read_record(stream_t *streamptr, int memtype, void *data, size_t *nmiss)
{
if ( CDI_Debug ) Message("streamID = %d", streamptr->self);

int tsID    = streamptr->curTsID;
int vrecID  = streamptr->tsteps[tsID].curRecID;
int recID   = streamptr->tsteps[tsID].recIDs[vrecID];
int varID   = streamptr->tsteps[tsID].records[recID].varID;
int levelID = streamptr->tsteps[tsID].records[recID].levelID;

if ( memtype == MEMTYPE_DOUBLE )
    cdfReadVarSliceDP(streamptr, varID, levelID, (double*) data, nmiss);
else
    cdfReadVarSliceSP(streamptr, varID, levelID, (float*) data, nmiss);
}





void cdfReadVarSliceDPPart(stream_t *streamptr, int varID, int levelID, int varType, int startpoint, size_t length, double *data, size_t *nmiss)
{
(void)(varType);
size_t start[4];
size_t count[4];

if ( CDI_Debug )
    Message("streamID = %d  varID = %d  levelID = %d", streamptr->self, varID, levelID);

int vlistID = streamptr->vlistID;
int fileID = streamptr->fileID;

bool swapxy;
cdfGetSliceSlapDescription(streamptr, varID, levelID, &swapxy, &start, &count);

int ncvarid = streamptr->vars[varID].ncvarid;
int gridId = vlistInqVarGrid(vlistID, varID);

size_t gridsize = (size_t)gridInqSize(gridId);
size_t xsize = (size_t)gridInqXsize(gridId);
size_t ysize = (size_t)gridInqYsize(gridId);

unsigned int position = 0;
for (int i=0 ; i<4 ; i++)
    if (count[i] == gridsize)
    position = i;

start[position] = start[position]+startpoint;
count[position] = length;

if ( vlistInqVarDatatype(vlistID, varID) == CDI_DATATYPE_FLT32 )
    {
    float *data_fp = (float *) Malloc(length*sizeof(*data_fp));
    cdf_get_vara_float(fileID, ncvarid, start, count, data_fp);

    for ( size_t i = 0; i < length; i++ )
        data[i] = (double) data_fp[i];

    Free(data_fp);
    }
else if ( vlistInqVarDatatype(vlistID, varID) == CDI_DATATYPE_UINT8 )
    {
    nc_type xtype;
    cdf_inq_vartype(fileID, ncvarid, &xtype);
    if ( xtype == NC_BYTE )
        {
        for ( size_t i = 0; i < length; i++ )
            if ( data[i] < 0 ) data[i] += 256;
        }
    }
else
    {
    cdf_get_vara_double(fileID, ncvarid, start, count, data);
    }

if ( swapxy ) transpose2dArrayDP(ysize, xsize, data);

double missval = vlistInqVarMissval(vlistID, varID);
const bool haveMissVal = vlistInqVarMissvalUsed(vlistID, varID);
double validRange[2];
if (!(haveMissVal && vlistInqVarValidrange(vlistID, varID, validRange)))
    validRange[0] = DBL_MIN, validRange[1] = DBL_MAX;
double addoffset = vlistInqVarAddoffset(vlistID, varID);
double scalefactor = vlistInqVarScalefactor(vlistID, varID);
size_t nmiss_ = cdfDoInputDataTransformationDP(length, data, haveMissVal, missval, scalefactor, addoffset, validRange[0], validRange[1]);
assert(nmiss_ <= INT_MAX);
*nmiss = (size_t)nmiss_;
}

void cdfReadVarSliceSPPart(stream_t *streamptr, int varID, int levelID, int varType, int startpoint, size_t length, float *data, size_t *nmiss)
{
(void)(varType);
size_t start[4];
size_t count[4];

if ( CDI_Debug )
    Message("streamID = %d  varID = %d  levelID = %d", streamptr->self, varID, levelID);

int vlistID = streamptr->vlistID;
int fileID = streamptr->fileID;

bool swapxy;
cdfGetSliceSlapDescription(streamptr, varID, levelID, &swapxy, &start, &count);

int ncvarid = streamptr->vars[varID].ncvarid;
int gridId = vlistInqVarGrid(vlistID, varID);

size_t gridsize = (size_t)gridInqSize(gridId);
size_t xsize = (size_t)gridInqXsize(gridId);
size_t ysize = (size_t)gridInqYsize(gridId);

unsigned int position = 0;
for (int i=0 ; i<4 ; i++)
    if (count[i] == gridsize)
    position = i;

start[position] = start[position]+startpoint;
count[position] = length;

if ( vlistInqVarDatatype(vlistID, varID) == CDI_DATATYPE_FLT32 )
    {
    float *data_fp = (float *) Malloc(length*sizeof(*data_fp));
    cdf_get_vara_float(fileID, ncvarid, start, count, data_fp);

    for ( size_t i = 0; i < length; i++ )
        data[i] = (float) data_fp[i];

    Free(data_fp);
    }
else if ( vlistInqVarDatatype(vlistID, varID) == CDI_DATATYPE_UINT8 )
    {
    nc_type xtype;
    cdf_inq_vartype(fileID, ncvarid, &xtype);
    if ( xtype == NC_BYTE )
        {
        for ( size_t i = 0; i < length; i++ )
            if ( data[i] < 0 ) data[i] += 256;
        }
    }
else
    {
    cdf_get_vara_float(fileID, ncvarid, start, count, data);
    }

if ( swapxy ) transpose2dArraySP(ysize, xsize, data);

float missval = vlistInqVarMissval(vlistID, varID);
const bool haveMissVal = vlistInqVarMissvalUsed(vlistID, varID);
double validRange[2];
if (!(haveMissVal && vlistInqVarValidrange(vlistID, varID, validRange)))
    validRange[0] = DBL_MIN, validRange[1] = DBL_MAX;
float addoffset = vlistInqVarAddoffset(vlistID, varID);
float scalefactor = vlistInqVarScalefactor(vlistID, varID);
size_t nmiss_ = cdfDoInputDataTransformationSP(length, data, haveMissVal, missval, scalefactor, addoffset, validRange[0], validRange[1]);
assert(nmiss_ <= INT_MAX);
*nmiss = (size_t)nmiss_;
}

static
int cdiStreamReadVarSlicePart(int streamID, int varID, int levelID, int varType, int start, size_t size, int memtype, void *data, size_t *nmiss)
{
int status = 0;

if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamID, varID);

check_parg(data);
check_parg(nmiss);

stream_t *streamptr = stream_to_pointer(streamID);
int filetype = streamptr->filetype;

*nmiss = 0;


switch (filetype)
    {
#if defined (HAVE_LIBNETCDF)
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    {
        if ( memtype == MEMTYPE_FLOAT )
        cdfReadVarSliceSPPart(streamptr, varID, levelID, varType, start, size, (float *)data, nmiss);
        else
        cdfReadVarSliceDPPart(streamptr, varID, levelID, varType, start, size, (double *)data, nmiss);
        break;
    }
#endif
    default:
    {
        Error("%s support not compiled in!", strfiletype(filetype));
        status = 2;
        break;
    }
    }

return status;
}


void cdfReadVarDPPart(stream_t *streamptr, int varID, int varType, int startpoint, size_t length, double *data, size_t *nmiss)
{
(void)(varType);
if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamptr->self, varID);

int vlistID = streamptr->vlistID;
int fileID = streamptr->fileID;

int ncvarid = streamptr->vars[varID].ncvarid;

int gridID = vlistInqVarGrid(vlistID, varID);
int zaxisID = vlistInqVarZaxis(vlistID, varID);

size_t start[4];
size_t count[4];
cdfGetSlapDescription(streamptr, varID, &start, &count);

int time = vlistInqVarTimetype(vlistID, varID);
if (time)
    {
    start[2] = start[2]+startpoint;
    count[2] = length;
    }
else
    {
    start[1] = start[1]+startpoint;
    count[1] = length;
    }

cdf_get_vara_double(fileID, ncvarid, start, count, data);

size_t size = (size_t)gridInqSize(gridID)*(size_t)zaxisInqSize(zaxisID);
double missval = vlistInqVarMissval(vlistID, varID);
const bool haveMissVal = vlistInqVarMissvalUsed(vlistID, varID);
double validRange[2];
if (!(haveMissVal && vlistInqVarValidrange(vlistID, varID, validRange)))
    validRange[0] = DBL_MIN, validRange[1] = DBL_MAX;
double addoffset = vlistInqVarAddoffset(vlistID, varID);
double scalefactor = vlistInqVarScalefactor(vlistID, varID);
size_t nmiss_ = cdfDoInputDataTransformationDP(size, data, haveMissVal, missval, scalefactor, addoffset, validRange[0], validRange[1]);
assert(nmiss_ <= INT_MAX);
*nmiss = (size_t)nmiss_;
}

void cdfReadVarSPPart(stream_t *streamptr, int varID, int varType, int startpoint, size_t length, float *data, size_t *nmiss)
{
(void)(varType);
if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamptr->self, varID);

int vlistID = streamptr->vlistID;
int fileID = streamptr->fileID;

int ncvarid = streamptr->vars[varID].ncvarid;

int gridID = vlistInqVarGrid(vlistID, varID);
int zaxisID = vlistInqVarZaxis(vlistID, varID);

size_t start[4];
size_t count[4];
cdfGetSlapDescription(streamptr, varID, &start, &count);

int time = vlistInqVarTimetype(vlistID, varID);
if (time)
    {
    start[2] = start[2]+startpoint;
    count[2] = length;
    }
else
    {
    start[1] = start[1]+startpoint;
    count[1] = length;
    }

cdf_get_vara_float(fileID, ncvarid, start, count, data);

size_t size = (size_t)gridInqSize(gridID)*(size_t)zaxisInqSize(zaxisID);
float missval = vlistInqVarMissval(vlistID, varID);
const bool haveMissVal = vlistInqVarMissvalUsed(vlistID, varID);
double validRange[2];
if (!(haveMissVal && vlistInqVarValidrange(vlistID, varID, validRange)))
    validRange[0] = DBL_MIN, validRange[1] = DBL_MAX;
float addoffset = vlistInqVarAddoffset(vlistID, varID);
float scalefactor = vlistInqVarScalefactor(vlistID, varID);
size_t nmiss_ = cdfDoInputDataTransformationSP(size, data, haveMissVal, missval, scalefactor, addoffset, validRange[0], validRange[1]);
assert(nmiss_ <= INT_MAX);
*nmiss = (size_t)nmiss_;
}

static
void cdiStreamReadVarPart(int streamID, int varID, int varType, int start, size_t size, int memtype, void *data, size_t *nmiss)
{
(void)(varType);
if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamID, varID);

check_parg(data);
check_parg(nmiss);

stream_t *streamptr = stream_to_pointer(streamID);
int filetype = streamptr->filetype;

*nmiss = 0;


switch (filetype)
    {
#if defined (HAVE_LIBNETCDF)
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
    {
        if ( memtype == MEMTYPE_FLOAT )
        cdfReadVarSPPart(streamptr, varID, varType, start, size, (float *)data, nmiss);
        else
        cdfReadVarDPPart(streamptr, varID, varType, start, size, (double *)data, nmiss);

        break;
    }
#endif
    default:
    {
        Error("%s support not compiled in!", strfiletype(filetype));
        break;
    }
    }
}

void streamReadVarSlicePart(int streamID, int varID, int levelID, int varType, int start, size_t size, void *data, size_t *nmiss, int memtype)
{
if ( cdiStreamReadVarSlicePart(streamID, varID, levelID, varType, start, size, memtype, data, nmiss) )
    {
    Error("Unexpected error returned from cdiStreamReadVarSlicePart()!");
    }
}

void streamReadVarPart(int streamID, int varID, int varType, int start, size_t size, void *data, size_t *nmiss, int memtype)
{
cdiStreamReadVarPart(streamID, varID, varType, start, size, memtype, data, nmiss);
}

#endif

#ifndef _SUBTYPE_H
#define _SUBTYPE_H


enum {

SUBTYPE_ATT_TILEINDEX                 = 0,
SUBTYPE_ATT_TOTALNO_OF_TILEATTR_PAIRS = 1,
SUBTYPE_ATT_TILE_CLASSIFICATION       = 2,
SUBTYPE_ATT_NUMBER_OF_TILES           = 3,
SUBTYPE_ATT_NUMBER_OF_ATTR            = 4,
SUBTYPE_ATT_TILEATTRIBUTE             = 5,

nSubtypeAttributes
};



extern const char * const cdiSubtypeAttributeName[];


struct subtype_attr_t {
int   key, val;
struct subtype_attr_t* next;
};



struct subtype_entry_t {
int                     self;
struct subtype_entry_t *next;


struct subtype_attr_t  *atts;
};



typedef struct  {
int                     self;
int                     subtype;
int                     nentries;


int                     active_subtype_index;
struct subtype_entry_t  globals;


struct subtype_entry_t *entries;
} subtype_t;





void  subtypeAllocate(subtype_t **subtype_ptr2, int subtype);
int   subtypePush(subtype_t *subtype_ptr);
void  subtypeDestroyPtr(void *ptr);
void  subtypeDuplicate(subtype_t *subtype_ptr, subtype_t **dst);
struct subtype_entry_t* subtypeEntryInsert(subtype_t* head);


void  subtypePrint(int subtypeID);
void  subtypePrintPtr(subtype_t* subtype_ptr);
void  subtypeDefGlobalDataP(subtype_t *subtype_ptr, int key, int val);
void  subtypeDefGlobalData(int subtypeID, int key, int val);
int   subtypeGetGlobalData(int subtypeID, int key);
int   subtypeGetGlobalDataP(subtype_t *subtype_ptr, int key);
int   subtypeComparePtr(int s1_ID, subtype_t *s2);


void  subtypeDefEntryDataP(struct subtype_entry_t *subtype_entry_ptr, int key, int val);



void  tilesetInsertP(subtype_t *s1, subtype_t *s2);


int vlistDefTileSubtype(int vlistID, subtype_t *tiles);


int vlistInsertTrivialTileSubtype(int vlistID);


#endif



































static const char* subtypeName[] = {
"tileset"
};

const char * const cdiSubtypeAttributeName[] = {
"tileIndex",
"totalNumberOfTileAttributePairs",
"tileClassification",
"numberOfTiles",
"numberOfTileAttributes",
"tileAttribute"
};



static int    subtypeCompareP    (subtype_t *z1, subtype_t *z2);
static void   subtypeDestroyP    ( void * subtype_ptr );
static void   subtypePrintP      ( void * subtype_ptr, FILE * fp );
static int    subtypeGetPackSize ( void * subtype_ptr, void *context);
static void   subtypePack        ( void * subtype_ptr, void * buffer, int size, int *pos, void *context);
static int    subtypeTxCode      ( void );

static const resOps subtypeOps = {
(int (*) (void *, void *)) subtypeCompareP,
(void (*)(void *))         subtypeDestroyP,
(void (*)(void *, FILE *)) subtypePrintP,
(int (*) (void *, void *)) subtypeGetPackSize,
                            subtypePack,
                            subtypeTxCode
};

enum {
differ = 1,
};








static int attribute_to_index(const char *key)
{
if (key == NULL)  Error("Internal error!");
for (int i=0; i<nSubtypeAttributes; i++)
    if ( strcmp(key, cdiSubtypeAttributeName[i]) == 0 ) return i;
return -1;
}




static struct subtype_attr_t* subtypeAttrNewList(struct subtype_entry_t* head, int key, int val)
{
if (head == NULL)  Error("Internal error!");
struct subtype_attr_t *ptr = (struct subtype_attr_t*) Malloc(sizeof(struct subtype_attr_t));
if(NULL == ptr)  Error("Node creation failed");
ptr->key   = key;
ptr->val   = val;
ptr->next  = NULL;

head->atts = ptr;
return ptr;
}



static struct subtype_attr_t* subtypeAttrInsert(struct subtype_entry_t* head, int key, int val)
{
if (head == NULL)  Error("Internal error!");
if (head->atts == NULL)  return (subtypeAttrNewList(head, key, val));


struct subtype_attr_t* ptr = (struct subtype_attr_t*) Malloc(sizeof(struct subtype_attr_t));
if(NULL == ptr)    Error("Node creation failed");

ptr->key   = key;
ptr->val   = val;
ptr->next  = NULL;


if (head->atts->key >= key) {

    ptr->next = head->atts;
    head->atts = ptr;
} else {
    struct subtype_attr_t** predec = &head->atts;
    while (((*predec)->next != NULL) && ((*predec)->next->key < key)) {
    predec = &((*predec)->next);
    }
    ptr->next = (*predec)->next;
    (*predec)->next = ptr;
}
return ptr;
}



static void subtypeAttrDestroy(struct subtype_attr_t* head)
{
if (head == NULL) return;
subtypeAttrDestroy(head->next);
Free(head);
head = NULL;
}



static struct subtype_attr_t* subtypeAttrFind(struct subtype_attr_t* head, int key)
{
if (head == NULL)
    return NULL;
else if (head->key == key)
    return head;
else
    return subtypeAttrFind(head->next, key);
}



static int subtypeAttsCompare(struct subtype_attr_t *a1, struct subtype_attr_t *a2)
{
if ((a1 == NULL) && (a2 == NULL))
    return 0;
else if ((a1 == NULL) && (a2 != NULL))
    {
    return differ;
    }
else if ((a1 != NULL) && (a2 == NULL))
    {
    return differ;
    }

if (a1->key != a2->key)
    {
    return differ;
    }
if (a1->val != a2->val)
    return differ;

return subtypeAttsCompare(a1->next, a2->next);
}



static void subtypeAttsDuplicate(struct subtype_attr_t *a1, struct subtype_entry_t* dst)
{
if (a1 == NULL)  return;

subtypeAttsDuplicate(a1->next, dst);
(void) subtypeAttrInsert(dst, a1->key, a1->val);
}









static struct subtype_entry_t* subtypeEntryNewList(subtype_t* head)
{
struct subtype_entry_t *ptr = (struct subtype_entry_t*) Malloc(sizeof(struct subtype_entry_t));
if(NULL == ptr)  Error("Node creation failed");
ptr->atts      = NULL;
ptr->next      = NULL;
head->entries  = ptr;
head->nentries = 0;
ptr->self      = head->nentries++;
return ptr;
}



struct subtype_entry_t* subtypeEntryInsert(subtype_t* head)
{
if (head == NULL)  Error("Internal error!");
if (head->entries == NULL)  return (subtypeEntryNewList(head));


struct subtype_entry_t* ptr = (struct subtype_entry_t*) Malloc(sizeof(struct subtype_entry_t));
if(NULL == ptr)    Error("Node creation failed");

ptr->atts     = NULL;
ptr->self     = head->nentries++;


if (head->entries->self >= ptr->self) {

    ptr->next     = head->entries;
    head->entries = ptr;
} else {
    struct subtype_entry_t** predec = &head->entries;
    while (((*predec)->next != NULL) && ((*predec)->next->self < ptr->self)) {
    predec = &((*predec)->next);
    }
    ptr->next = (*predec)->next;
    (*predec)->next = ptr;
}
return ptr;
}



static struct subtype_entry_t* subtypeEntryAppend(subtype_t* head)
{
if (head == NULL)  Error("Internal error!");
if (head->entries == NULL)  return (subtypeEntryNewList(head));


struct subtype_entry_t* ptr = (struct subtype_entry_t*) Malloc(sizeof(struct subtype_entry_t));
if(NULL == ptr)    Error("Node creation failed");

ptr->atts     = NULL;
ptr->next     = NULL;
ptr->self     = head->nentries++;


struct subtype_entry_t* prec_ptr = head->entries;
while (prec_ptr->next != NULL)
    prec_ptr = prec_ptr->next;

prec_ptr->next  = ptr;
return ptr;
}



static void subtypeEntryDestroy(struct subtype_entry_t *entry)
{
if (entry == NULL) return;
subtypeEntryDestroy(entry->next);
subtypeAttrDestroy(entry->atts);
Free(entry);
entry = NULL;
}



static int subtypeEntryCompare(struct subtype_entry_t *e1, struct subtype_entry_t *e2)
{
if (e1 == NULL)  Error("Internal error!");
if (e2 == NULL)  Error("Internal error!");
return
    (e1->self == e2->self) &&
    subtypeAttsCompare(e1->atts, e2->atts);
}



static void subtypeEntryDuplicate(struct subtype_entry_t *a1, subtype_t* dst)
{
if (a1 == NULL) return;

struct subtype_entry_t *ptr = subtypeEntryAppend(dst);

subtypeAttsDuplicate(a1->atts, ptr);
ptr->self = a1->self;

subtypeEntryDuplicate(a1->next, dst);
}








static void subtypePrintKernel(subtype_t *subtype_ptr, FILE *fp)
{
if (subtype_ptr == NULL)  Error("Internal error!");
fprintf(fp, "# %s (subtype ID %d)\n", subtypeName[subtype_ptr->subtype], subtype_ptr->self);

struct subtype_attr_t* ptr = subtype_ptr->globals.atts;
if (ptr != NULL)  fprintf(fp, "#\n# global attributes:\n");
while (ptr != NULL) {
    fprintf(fp, "#   %-40s   (%2d) : %d\n", cdiSubtypeAttributeName[ptr->key], ptr->key, ptr->val);
    ptr = ptr->next;
}

fprintf(fp, "# %d local entries:\n", subtype_ptr->nentries);
struct subtype_entry_t *entry = subtype_ptr->entries;
while (entry != NULL) {
    fprintf(fp, "# subtype entry %d\n", entry->self);
    ptr = entry->atts;
    if (ptr != NULL)  fprintf(fp, "#   attributes:\n");
    while (ptr != NULL) {
    fprintf(fp, "#     %-40s (%2d) : %d\n", cdiSubtypeAttributeName[ptr->key], ptr->key, ptr->val);
    ptr = ptr->next;
    }
    entry = entry->next;
}
fprintf(fp, "\n");
}



static int subtypeCompareP(subtype_t *s1, subtype_t *s2)
{
xassert(s1 && s2);
if (s1->subtype != s2->subtype) return differ;
if (subtypeEntryCompare(&s1->globals, &s2->globals) != 0) return differ;

struct subtype_entry_t *entry1 = s1->entries;
struct subtype_entry_t *entry2 = s2->entries;
while ((entry1 != NULL) && (entry2 != NULL)) {
    if (subtypeEntryCompare(entry1, entry2) != 0)  return differ;
    entry1 = entry1->next;
    entry2 = entry2->next;
}

if ((entry1 != NULL) || (entry2 != NULL))  return differ;
return 0;
}



static void subtypeDestroyP(void *ptr)
{
subtype_t *subtype_ptr = (subtype_t*) ptr;

subtypeAttrDestroy(subtype_ptr->globals.atts);

subtypeEntryDestroy(subtype_ptr->entries);
subtype_ptr->entries = NULL;
Free(subtype_ptr);
subtype_ptr = NULL;
}



void subtypeDestroyPtr(void *ptr)
{
subtypeDestroyP(ptr);
}



int subtypeComparePtr(int s1_ID, subtype_t *s2)
{
subtype_t *subtype_ptr = (subtype_t *)reshGetVal(s1_ID, &subtypeOps);
if (subtype_ptr == NULL)  Error("Internal error");
return subtypeCompareP(subtype_ptr,s2);
}



static void subtypePrintP(void * subtype_ptr, FILE * fp)
{  subtypePrintKernel((subtype_t *)subtype_ptr, fp); }




void subtypePrintPtr(subtype_t* subtype_ptr)
{
subtypePrintKernel(subtype_ptr, stdout);
}



static void subtypeDefaultValue(subtype_t *subtype_ptr)
{
if (subtype_ptr == NULL)  Error("Internal error!");
subtype_ptr->self                 = CDI_UNDEFID;
subtype_ptr->nentries             = 0;
subtype_ptr->entries              = NULL;
subtype_ptr->globals.atts         = NULL;
subtype_ptr->globals.next         = NULL;
subtype_ptr->globals.self         = -1;
subtype_ptr->active_subtype_index = 0;
}


void subtypeAllocate(subtype_t **subtype_ptr2, int subtype)
{

(*subtype_ptr2) = (subtype_t *) Malloc(sizeof(subtype_t));
subtype_t* subtype_ptr = *subtype_ptr2;
subtypeDefaultValue(subtype_ptr);
subtype_ptr->subtype = subtype;
subtype_ptr->self    = CDI_UNDEFID;
}



void subtypeDuplicate(subtype_t *subtype_ptr, subtype_t **dst_ptr)
{
if (subtype_ptr == NULL)  Error("Internal error!");
subtypeAllocate(dst_ptr, subtype_ptr->subtype);
subtype_t *dst = (*dst_ptr);

subtypeAttsDuplicate(subtype_ptr->globals.atts, &dst->globals);
dst->globals.self = subtype_ptr->globals.self;

subtypeEntryDuplicate( subtype_ptr->entries, dst);
}



int subtypePush(subtype_t *subtype_ptr)
{
if (subtype_ptr == NULL)  Error("Internal error!");
subtype_ptr->self = reshPut(subtype_ptr, &subtypeOps);
return subtype_ptr->self;
}




void subtypeDefGlobalDataP(subtype_t *subtype_ptr, int key, int val)
{
if (subtype_ptr == NULL)  Error("Internal error!");

struct subtype_attr_t* att_ptr = subtypeAttrFind(subtype_ptr->globals.atts, key);
if (att_ptr == NULL)
    subtypeAttrInsert(&subtype_ptr->globals, key, val);
else
    att_ptr->val = val;
}



void subtypeDefGlobalData(int subtypeID, int key, int val)
{
subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps);
subtypeDefGlobalDataP(subtype_ptr, key, val);
}



int subtypeGetGlobalDataP(subtype_t *subtype_ptr, int key)
{
if (subtype_ptr == NULL)  Error("Internal error!");

struct subtype_attr_t* att_ptr = subtypeAttrFind(subtype_ptr->globals.atts, key);
if (att_ptr == NULL)
    return -1;
else
    return att_ptr->val;
}



int subtypeGetGlobalData(int subtypeID, int key)
{
subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps);
return subtypeGetGlobalDataP(subtype_ptr, key);
}



void subtypeDefEntryDataP(struct subtype_entry_t *subtype_entry_ptr, int key, int val)
{
if (subtype_entry_ptr == NULL)  Error("Internal error!");

struct subtype_attr_t* att_ptr = subtypeAttrFind(subtype_entry_ptr->atts, key);
if (att_ptr == NULL)
    subtypeAttrInsert(subtype_entry_ptr, key, val);
else
    att_ptr->val = val;
}









subtype_query_t keyValuePair(const char* key, int value)
{
subtype_query_t result;
result.nAND = 1;
result.key_value_pairs[0][0] = attribute_to_index(key);
result.key_value_pairs[1][0] = value;
if (CDI_Debug) {
    Message("key  %s matches %d", key, result.key_value_pairs[0][0]);
    Message("%d --?-- %d", result.key_value_pairs[0][0], result.key_value_pairs[1][0]);
}
return result;
}



subtype_query_t matchAND(subtype_query_t q1, subtype_query_t q2)
{
if ((q1.nAND + q2.nAND) > MAX_KV_PAIRS_MATCH)  Error("Internal error");
subtype_query_t result;
memset(&result, 0, sizeof(subtype_query_t));
result.nAND = q1.nAND;
for (int i=0; i<q1.nAND; i++)
    {
    result.key_value_pairs[0][i] = q1.key_value_pairs[0][i];
    result.key_value_pairs[1][i] = q1.key_value_pairs[1][i];
    }
for (int i=0; i<q2.nAND; i++)
    {
    result.key_value_pairs[0][result.nAND] = q2.key_value_pairs[0][i];
    result.key_value_pairs[1][result.nAND] = q2.key_value_pairs[1][i];
    result.nAND++;
    }

if (CDI_Debug) {
    Message("combined criterion:");
    for (int i=0; i<result.nAND; i++)
    Message("%d --?-- %d", result.key_value_pairs[0][i], result.key_value_pairs[1][i]);
}
return result;
}









void tilesetInsertP(subtype_t *s1, subtype_t *s2)
{
if (s1 == NULL)  Error("Internal error!");
if (s2 == NULL)  Error("Internal error!");
struct subtype_entry_t
    *entry1 = s1->entries,
    *entry2 = s2->entries;
struct subtype_attr_t *att_ptr2;


if (subtypeAttsCompare(s1->globals.atts, s2->globals.atts) != differ)
    {
    while (entry1 != NULL) {
        int found = 1;
        entry2 = s2->entries;
        while (entry2 != NULL) {
        found &= (subtypeAttsCompare(entry1->atts, entry2->atts) != differ);
        entry2 = entry2->next;
        }
        if (found)
        {
            return;
        }
        entry1 = entry1->next;
    }

    entry2 = s2->entries;
    while (entry2 != NULL) {
        entry1 = subtypeEntryInsert(s1);

        att_ptr2 = entry2->atts;
        while (att_ptr2 != NULL) {
        (void) subtypeAttrInsert(entry1, att_ptr2->key, att_ptr2->val);
        att_ptr2 = att_ptr2->next;
        }
        entry2 = entry2->next;
    }
    }
else
    {
    fprintf(stderr, "\n# SUBTYPE A:\n");
    subtypePrintKernel(s1, stderr);
    fprintf(stderr, "\n# SUBTYPE B:\n");
    subtypePrintKernel(s2, stderr);
    Error("Attempting to insert subtype entry into subtype with different global attributes!");
    }
}









int subtypeCreate(int subtype)
{
if ( CDI_Debug )  Message("subtype: %d ", subtype);
Message("subtype: %d ", subtype);


subtype_t *subtype_ptr;
subtypeAllocate(&subtype_ptr, subtype);

return subtypePush(subtype_ptr);
}



void subtypePrint(int subtypeID)
{
subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps);
subtypePrintKernel(subtype_ptr, stdout);
}



int subtypeCompare(int subtypeID1, int subtypeID2)
{
subtype_t *subtype_ptr1 = (subtype_t *)reshGetVal(subtypeID1, &subtypeOps);
subtype_t *subtype_ptr2 = (subtype_t *)reshGetVal(subtypeID2, &subtypeOps);
return subtypeCompareP(subtype_ptr1,subtype_ptr2);
}



int subtypeInqSize(int subtypeID)
{
if ( subtypeID == CDI_UNDEFID )
    {
    return 0;
    }
else
    {
    subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps);
    return subtype_ptr->nentries;
    }
}



int subtypeInqActiveIndex(int subtypeID)
{
if (subtypeID == CDI_UNDEFID)  return 0;
subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps);
return subtype_ptr->active_subtype_index;
}



void subtypeDefActiveIndex(int subtypeID, int index)
{
subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps);
subtype_ptr->active_subtype_index = index;
}



int subtypeInqSubEntry(int subtypeID, subtype_query_t criterion)
{
subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps);
struct subtype_entry_t *entry = subtype_ptr->entries;

while (entry != NULL) {
    {
    int match = 1;

    for (int j=0; (j<criterion.nAND) && (match); j++)
        {
        if (CDI_Debug)  Message("check criterion %d :  %d --?-- %d", j,
                                criterion.key_value_pairs[0][j], criterion.key_value_pairs[1][j]);
        struct subtype_attr_t* att_ptr =
            subtypeAttrFind(entry->atts, criterion.key_value_pairs[0][j]);
        if (att_ptr == NULL)
            {
            match = 0;
            if (CDI_Debug)  Message("did not find %d", criterion.key_value_pairs[0][j]);
            }
        else
            {
            if (CDI_Debug)  Message("found %d", criterion.key_value_pairs[0][j]);
            match &= (att_ptr->val == criterion.key_value_pairs[1][j]);
            }
        }
    if (match) return entry->self;
    }
    entry = entry->next;
}
return CDI_UNDEFID;
}


int subtypeInqTile(int subtypeID, int tileindex, int attribute)
{
return subtypeInqSubEntry(subtypeID,
                            matchAND(keyValuePair(cdiSubtypeAttributeName[SUBTYPE_ATT_TILEINDEX], tileindex),
                                    keyValuePair(cdiSubtypeAttributeName[SUBTYPE_ATT_TILEATTRIBUTE], attribute)));
}

int subtypeInqAttribute(int subtypeID, int index, const char* key, int* outValue)
{

if(subtypeID == CDI_UNDEFID) xabort("CDI_UNDEFID was passed to %s() as a subtypeID. Please check the origin of that ID.", __func__);
subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps);
if(!subtype_ptr) xabort("Internal error: subtypeID %d resolved to NULL.", subtypeID);

if((unsigned)index >= (unsigned)subtype_ptr->nentries)
    {
    xabort("index argument of %s() is out of range. Expected 0 <= index < %d, but got index = %d.", __func__, subtype_ptr->nentries, index);
    }

#ifndef __cplusplus
if(!outValue) outValue = &(int){0};
#else
int dummy = 0;
if(!outValue) outValue = &dummy;
#endif

if(!key) return CDI_EINVAL;
int iKey = attribute_to_index(key);
if(iKey < 0) return CDI_EINVAL;


struct subtype_entry_t* entry = subtype_ptr->entries;
for(; index--; entry = entry->next) if(!entry) xabort("internal error: preliminary end of subtype entry list");


for(struct subtype_attr_t* attribute = entry->atts; attribute; attribute = attribute->next)
    {
    if(attribute->key == iKey)
        {
        *outValue = attribute->val;
        return CDI_NOERR;
        }
    }


return CDI_EINVAL;
}


int vlistDefTileSubtype(int vlistID, subtype_t *tiles)
{
int subtypeID = CDI_UNDEFID;


vlist_t *vlistptr = vlist_to_pointer(vlistID);
int      tileset_defined = 0;
for (int isub=0; isub<vlistptr->nsubtypes; isub++)
    {

    subtypeID = vlistptr->subtypeIDs[isub];
    if (subtypeComparePtr(subtypeID, tiles) == 0)
        {
        tileset_defined = 1;
        break;
        }
    }


if (tileset_defined == 0)  {
    subtype_t *tiles_duplicate = NULL;
    subtypeDuplicate(tiles, &tiles_duplicate);
    subtypeID = vlistptr->subtypeIDs[vlistptr->nsubtypes++] = subtypePush(tiles_duplicate);
}

return subtypeID;
}



int vlistInsertTrivialTileSubtype(int vlistID)
{

subtype_t *subtype_ptr;
subtypeAllocate(&subtype_ptr, SUBTYPE_TILES);


(void) subtypeEntryInsert(subtype_ptr);


vlist_t *vlistptr = vlist_to_pointer(vlistID);
int subtypeID = vlistptr->subtypeIDs[vlistptr->nsubtypes++] = subtypePush(subtype_ptr);
return subtypeID;
}








static int subtypeGetPackSize( void * subtype_ptr, void *context)
{
(void)subtype_ptr; (void)context;
Error("Not yet implemented for subtypes!");
return 0;
}

static void subtypePack( void * subtype_ptr, void * buffer, int size, int *pos, void *context)
{
(void)subtype_ptr; (void)buffer; (void)size; (void)pos; (void)context;
Error("Not yet implemented for subtypes!");
}

static int subtypeTxCode( void )
{  Error("Not yet implemented for subtypes!");  return 0; }



#include <stddef.h>



static int DefaultTimeType = TAXIS_ABSOLUTE;
static int DefaultTimeUnit = TUNIT_HOUR;


static const char *Timeunits[] = {
"undefined",
"seconds",
"minutes",
"quarters",
"30minutes",
"hours",
"3hours",
"6hours",
"12hours",
"days",
"months",
"years",
};


static int    taxisCompareP    ( void * taxisptr1, void * taxisptr2 );
static void   taxisDestroyP    ( void * taxisptr );
static void   taxisPrintKernel(taxis_t *taxisptr, FILE * fp);
static int    taxisGetPackSize ( void * taxisptr, void *context );
static void   taxisPack        ( void * taxisptr, void *buf, int size,
                                int *position, void *context );
static int    taxisTxCode      ( void );

const resOps taxisOps = {
taxisCompareP,
taxisDestroyP,
(void (*)(void *, FILE *))taxisPrintKernel,
taxisGetPackSize,
taxisPack,
taxisTxCode
};

#define container_of(ptr, type, member) \
((type *)(void*)((unsigned char *)ptr - offsetof(type,member)))

struct refcount_string
{
int ref_count;
char string[];
};

static char *
new_refcount_string(size_t len)
{
struct refcount_string *container
    = (struct refcount_string *) Malloc(sizeof (*container) + len + 1);
container->ref_count = 1;
return container->string;
}

static void
delete_refcount_string(void *p)
{
if (p)
    {
    struct refcount_string *container
        = container_of(p, struct refcount_string, string);
    if (!--(container->ref_count))
        Free(container);
    }
}

static char *
dup_refcount_string(char *p)
{
if (p)
    {
    struct refcount_string *container
        = container_of(p, struct refcount_string, string);
    ++(container->ref_count);
    }
return p;
}


#undef container_of

static int  TAXIS_Debug = 0;


const char *tunitNamePtr(int unitID)
{
int size = sizeof(Timeunits)/sizeof(*Timeunits);
return (unitID > 0 && unitID < size) ? Timeunits[unitID] : Timeunits[0];
}

#if 0
static
void taxis_defaults(void)
{
char *timeunit = getenv("TIMEUNIT");
if ( timeunit )
    {
    if ( strcmp(timeunit, "minutes") == 0 )
        DefaultTimeUnit = TUNIT_MINUTE;
    else if ( strcmp(timeunit, "hours") == 0 )
        DefaultTimeUnit = TUNIT_HOUR;
    else if ( strcmp(timeunit, "3hours") == 0 )
        DefaultTimeUnit = TUNIT_3HOURS;
    else if ( strcmp(timeunit, "6hours") == 0 )
        DefaultTimeUnit = TUNIT_6HOURS;
    else if ( strcmp(timeunit, "12hours") == 0 )
        DefaultTimeUnit = TUNIT_12HOURS;
    else if ( strcmp(timeunit, "days") == 0 )
        DefaultTimeUnit = TUNIT_DAY;
    else if ( strcmp(timeunit, "months") == 0 )
        DefaultTimeUnit = TUNIT_MONTH;
    else if ( strcmp(timeunit, "years") == 0 )
        DefaultTimeUnit = TUNIT_YEAR;
    else
        Warning("Unsupported TIMEUNIT %s!", timeunit);
    }
}
#endif

static
void taxisDefaultValue(taxis_t* taxisptr)
{
taxisptr->self        = CDI_UNDEFID;
taxisptr->used        = false;
taxisptr->datatype    = CDI_DATATYPE_FLT64;
taxisptr->type        = DefaultTimeType;
taxisptr->vdate       = 0;
taxisptr->vtime       = 0;
taxisptr->rdate       = CDI_UNDEFID;
taxisptr->rtime       = 0;
taxisptr->fdate       = CDI_UNDEFID;
taxisptr->ftime       = 0;
taxisptr->calendar    = cdiDefaultCalendar;
taxisptr->unit        = DefaultTimeUnit;
taxisptr->numavg      = 0;
taxisptr->climatology = false;
taxisptr->has_bounds  = false;
taxisptr->vdate_lb    = 0;
taxisptr->vtime_lb    = 0;
taxisptr->vdate_ub    = 0;
taxisptr->vtime_ub    = 0;
taxisptr->fc_unit     = DefaultTimeUnit;
taxisptr->fc_period   = 0;
taxisptr->name        = NULL;
taxisptr->longname    = NULL;
taxisptr->units       = NULL;
}

static taxis_t *
taxisNewEntry(cdiResH resH)
{
taxis_t *taxisptr = (taxis_t*) Malloc(sizeof(taxis_t));

taxisDefaultValue(taxisptr);
if (resH == CDI_UNDEFID)
    taxisptr->self = reshPut(taxisptr, &taxisOps);
else
    {
    taxisptr->self = resH;
    reshReplace(resH, taxisptr, &taxisOps);
    }

return taxisptr;
}

static
void taxisInit(void)
{
static bool taxisInitialized = false;

if ( taxisInitialized ) return;

taxisInitialized = true;

char *env = getenv("TAXIS_DEBUG");
if ( env ) TAXIS_Debug = atoi(env);
}


int taxisCreate(int taxistype)
{
if ( CDI_Debug ) Message("taxistype: %d", taxistype);

taxisInit ();

taxis_t *taxisptr = taxisNewEntry(CDI_UNDEFID);
taxisptr->type = taxistype;

int taxisID = taxisptr->self;

if ( CDI_Debug ) Message("taxisID: %d", taxisID);

return taxisID;
}


void taxisDestroyKernel(taxis_t *taxisptr)
{
delete_refcount_string(taxisptr->name);
delete_refcount_string(taxisptr->longname);
delete_refcount_string(taxisptr->units);
}


void taxisDestroy(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);
reshRemove(taxisID, &taxisOps);
taxisDestroyKernel(taxisptr);
Free(taxisptr);
}


void taxisDestroyP( void * taxisptr )
{
taxisDestroyKernel((taxis_t *)taxisptr);
Free(taxisptr);
}


int taxisDuplicate(int taxisID1)
{
taxis_t *taxisptr1 = (taxis_t *)reshGetVal(taxisID1, &taxisOps);
taxis_t *taxisptr2 = taxisNewEntry(CDI_UNDEFID);

int taxisID2 = taxisptr2->self;

if ( CDI_Debug ) Message("taxisID2: %d", taxisID2);

ptaxisCopy(taxisptr2, taxisptr1);

return taxisID2;
}


void taxisDefType(int taxisID, int taxistype)
{
taxis_t *taxisptr = (taxis_t *) reshGetVal(taxisID, &taxisOps);

if ( taxisptr->type != taxistype )
    {
    taxisptr->type = taxistype;
    if ( taxisptr->units )
        {
        delete_refcount_string(taxisptr->units);
        taxisptr->units = NULL;
        }
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefVdate(int taxisID, int64_t vdate)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);

if (taxisptr->vdate != vdate)
    {
    taxisptr->vdate = vdate;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefVtime(int taxisID, int vtime)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);

if (taxisptr->vtime != vtime)
    {
    taxisptr->vtime = vtime;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefRdate(int taxisID, int64_t rdate)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if (taxisptr->rdate != rdate)
    {
    taxisptr->rdate = rdate;

    if ( taxisptr->units )
        {
        delete_refcount_string(taxisptr->units);
        taxisptr->units = NULL;
        }
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefRtime(int taxisID, int rtime)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if (taxisptr->rtime != rtime)
    {
    taxisptr->rtime = rtime;
    if ( taxisptr->units )
        {
        delete_refcount_string(taxisptr->units);
        taxisptr->units = NULL;
        }
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefFdate(int taxisID, int64_t fdate)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if (taxisptr->fdate != fdate)
    {
    taxisptr->fdate = fdate;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefFtime(int taxisID, int ftime)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if (taxisptr->ftime != ftime)
    {
    taxisptr->ftime = ftime;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefCalendar(int taxisID, int calendar)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if (taxisptr->calendar != calendar)
    {
    taxisptr->calendar = calendar;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefTunit(int taxisID, int unit)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if (taxisptr->unit != unit)
    {
    taxisptr->unit = unit;
    if ( taxisptr->units )
        {
        delete_refcount_string(taxisptr->units);
        taxisptr->units = NULL;
        }
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefForecastTunit(int taxisID, int unit)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);

if (taxisptr->fc_unit != unit)
    {
    taxisptr->fc_unit = unit;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefForecastPeriod(int taxisID, double fc_period)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if ( IS_NOT_EQUAL(taxisptr->fc_period, fc_period) )
    {
    taxisptr->fc_period = fc_period;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDefNumavg(int taxisID, int numavg)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if (taxisptr->numavg != numavg)
    {
    taxisptr->numavg = numavg;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


int taxisInqType(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);
return taxisptr->type;
}


int taxisHasBounds(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);
return taxisptr->has_bounds;
}


void taxisWithBounds(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);

if ( taxisptr->has_bounds == false )
    {
    taxisptr->has_bounds = true;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisDeleteBounds(int taxisID)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if ( taxisptr->has_bounds )
    {
    taxisptr->has_bounds = false;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


void taxisCopyTimestep(int taxisID2, int taxisID1)
{
taxis_t *taxisptr1 = (taxis_t *)reshGetVal(taxisID1, &taxisOps),
        *taxisptr2 = (taxis_t *)reshGetVal(taxisID2, &taxisOps);

reshLock();

if (taxisptr2->units && taxisptr2->rdate != CDI_UNDEFID)
    {
    if (taxisptr2->rdate != taxisptr1->rdate || taxisptr2->rtime != taxisptr1->rtime)
        {
        delete_refcount_string(taxisptr2->units);
        taxisptr2->units = NULL;
        }
    }

taxisptr2->rdate = taxisptr1->rdate;
taxisptr2->rtime = taxisptr1->rtime;

taxisptr2->vdate = taxisptr1->vdate;
taxisptr2->vtime = taxisptr1->vtime;

if ( taxisptr2->has_bounds )
    {
    taxisptr2->vdate_lb = taxisptr1->vdate_lb;
    taxisptr2->vtime_lb = taxisptr1->vtime_lb;
    taxisptr2->vdate_ub = taxisptr1->vdate_ub;
    taxisptr2->vtime_ub = taxisptr1->vtime_ub;
    }

taxisptr2->fdate = taxisptr1->fdate;
taxisptr2->ftime = taxisptr1->ftime;

taxisptr2->fc_unit   = taxisptr1->fc_unit;
taxisptr2->fc_period = taxisptr1->fc_period;

reshSetStatus(taxisID2, &taxisOps, RESH_DESYNC_IN_USE);
reshUnlock();
}


int64_t taxisInqVdate(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);
return taxisptr->vdate;
}


void taxisInqVdateBounds(int taxisID, int64_t *vdate_lb, int64_t *vdate_ub)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);

*vdate_lb = taxisptr->vdate_lb;
*vdate_ub = taxisptr->vdate_ub;
}


void taxisDefVdateBounds(int taxisID, int64_t vdate_lb, int64_t vdate_ub)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if ( taxisptr->vdate_lb != vdate_lb
    || taxisptr->vdate_ub != vdate_ub
    || taxisptr->has_bounds == false )
    {
    taxisptr->vdate_lb = vdate_lb;
    taxisptr->vdate_ub = vdate_ub;
    taxisptr->has_bounds = true;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


int taxisInqVtime(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);
return taxisptr->vtime;
}


void taxisInqVtimeBounds(int taxisID, int *vtime_lb, int *vtime_ub)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);

*vtime_lb = taxisptr->vtime_lb;
*vtime_ub = taxisptr->vtime_ub;
}


void taxisDefVtimeBounds(int taxisID, int vtime_lb, int vtime_ub)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );

if ( taxisptr->vtime_lb != vtime_lb
    || taxisptr->vtime_ub != vtime_ub
    || taxisptr->has_bounds == false )
    {
    taxisptr->vtime_lb = vtime_lb;
    taxisptr->vtime_ub = vtime_ub;
    taxisptr->has_bounds = true;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }
}


int64_t taxisInqRdate(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);

if ( taxisptr->rdate == -1 )
    {
    taxisptr->rdate = taxisptr->vdate;
    taxisptr->rtime = taxisptr->vtime;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }

return taxisptr->rdate;
}


int taxisInqRtime(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);

if ( taxisptr->rdate == -1 )
    {
    taxisptr->rdate = taxisptr->vdate;
    taxisptr->rtime = taxisptr->vtime;
    reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
    }

return taxisptr->rtime;
}


int64_t taxisInqFdate(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);

if ( taxisptr->fdate == -1 )
    {
    taxisptr->fdate = taxisptr->vdate;
    taxisptr->ftime = taxisptr->vtime;
    }

return taxisptr->fdate;
}


int taxisInqFtime(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);

if ( taxisptr->fdate == -1 )
    {
    taxisptr->fdate = taxisptr->vdate;
    taxisptr->ftime = taxisptr->vtime;
    }

return taxisptr->ftime;
}


int taxisInqCalendar(int taxisID)
{
taxis_t *taxisptr = (taxis_t *) reshGetVal ( taxisID, &taxisOps );
return taxisptr->calendar;
}


int taxisInqTunit(int taxisID)
{
taxis_t *taxisptr = (taxis_t *) reshGetVal ( taxisID, &taxisOps );
return taxisptr->unit;
}


int taxisInqForecastTunit(int taxisID)
{
taxis_t *taxisptr = (taxis_t *) reshGetVal ( taxisID, &taxisOps );
return taxisptr->fc_unit;
}


double taxisInqForecastPeriod(int taxisID)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
return taxisptr->fc_period;
}


int taxisInqNumavg(int taxisID)
{
taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
return taxisptr->numavg;
}


taxis_t *taxisPtr(int taxisID)
{
taxis_t *taxisptr = (taxis_t *)reshGetVal(taxisID, &taxisOps);
return taxisptr;
}


void ptaxisDefDatatype(taxis_t *taxisptr, int datatype)
{
taxisptr->datatype = datatype;
}


void ptaxisDefName(taxis_t *taxisptr, const char *name)
{
if ( name )
    {
    size_t len = strlen(name);
    delete_refcount_string(taxisptr->name);
    char *taxisname = taxisptr->name = new_refcount_string(len);
    strcpy(taxisname, name);
    }
}


void ptaxisDefLongname(taxis_t *taxisptr, const char *longname)
{
if ( longname )
    {
    size_t len = strlen(longname);
    delete_refcount_string(taxisptr->longname);
    char *taxislongname = taxisptr->longname = new_refcount_string(len);
    strcpy(taxislongname, longname);
    }
}


void ptaxisDefUnits(taxis_t *taxisptr, const char *units)
{
if ( units )
    {
    size_t len = strlen(units);
    delete_refcount_string(taxisptr->units);
    char *taxisunits = taxisptr->units = new_refcount_string(len);
    strcpy(taxisunits, units);
    }
}


static void
cdiDecodeTimevalue(int timeunit, double timevalue, int64_t *days, int *secs)
{
*days = 0;
*secs = 0;

if ( timeunit == TUNIT_MINUTE )
    {
    timevalue *= 60;
    timeunit = TUNIT_SECOND;
    }
else if ( timeunit == TUNIT_HOUR )
    {
    timevalue /= 24;
    timeunit = TUNIT_DAY;
    }

if ( timeunit == TUNIT_SECOND )
    {
    *days = (int) (timevalue/86400);
    double seconds = timevalue - *days*86400.;
    *secs = (int)lround(seconds);
    if ( *secs < 0 ) { *days -= 1; *secs += 86400; };

    }
else if ( timeunit == TUNIT_DAY )
    {
    *days = (int) timevalue;
    double seconds = (timevalue - *days)*86400;
    *secs = (int)lround(seconds);
    if ( *secs < 0 ) { *days -= 1; *secs += 86400; };

    }
else
    {
    static bool lwarn = true;
    if ( lwarn )
        {
        Warning("timeunit %s unsupported!", tunitNamePtr(timeunit));
        lwarn = false;
        }
    }
}

static
void cdiEncodeTimevalue(int days, int secs, int timeunit, double *timevalue)
{
if ( timeunit == TUNIT_SECOND )
    {
    *timevalue = days*86400. + secs;
    }
else if ( timeunit == TUNIT_MINUTE  ||
            timeunit == TUNIT_QUARTER ||
            timeunit == TUNIT_30MINUTES )
    {
    *timevalue = days*1440. + secs/60.;
    }
else if ( timeunit == TUNIT_HOUR   ||
            timeunit == TUNIT_3HOURS ||
            timeunit == TUNIT_6HOURS ||
            timeunit == TUNIT_12HOURS )
    {
    *timevalue = days*24. + secs/3600.;
    }
else if ( timeunit == TUNIT_DAY )
    {
    *timevalue = days + secs/86400.;
    }
else
    {
    static bool lwarn = true;
    if ( lwarn )
        {
        Warning("timeunit %s unsupported!", tunitNamePtr(timeunit));
        lwarn = false;
        }
    }
}


void timeval2vtime(double timevalue, taxis_t *taxis, int64_t *vdate, int *vtime)
{
int64_t rdate = taxis->rdate;
int rtime = taxis->rtime;

if ( DBL_IS_EQUAL(timevalue, 0.) )
    {
    *vdate = rdate;
    *vtime = rtime;
    return;
    }

int year, month, day, hour, minute, second;
cdiDecodeDate(rdate, &year, &month, &day);
cdiDecodeTime(rtime, &hour, &minute, &second);

int timeunit = taxis->unit;
int calendar = taxis->calendar;

if ( timeunit == TUNIT_MONTH && calendar == CALENDAR_360DAYS )
    {
    timeunit = TUNIT_DAY;
    timevalue *= 30;
    }

if ( timeunit == TUNIT_MONTH || timeunit == TUNIT_YEAR )
    {
    if ( timeunit == TUNIT_YEAR ) timevalue *= 12;

    int nmon = (int) timevalue;
    double fmon = timevalue - nmon;

    month += nmon;

    while ( month > 12 ) { month -= 12; year++; }
    while ( month <  1 ) { month += 12; year--; }

    int dpm = days_per_month(calendar, year, month);
    timeunit = TUNIT_DAY;
    timevalue = fmon*dpm;
    }

int64_t julday, days;
int secofday, secs;
encode_caldaysec(calendar, year, month, day, hour, minute, second, &julday, &secofday);

cdiDecodeTimevalue(timeunit, timevalue, &days, &secs);

julday_add(days, secs, &julday, &secofday);

decode_caldaysec(calendar, julday, secofday, &year, &month, &day, &hour, &minute, &second);

*vdate = cdiEncodeDate(year, month, day);
*vtime = cdiEncodeTime(hour, minute, second);
}


double vtime2timeval(int64_t vdate, int vtime, taxis_t *taxis)
{
double value = 0;
int64_t julday1, julday2, days;
int secofday1, secofday2, secs;

int timeunit = (*taxis).unit;
int calendar = (*taxis).calendar;

int64_t rdate = (*taxis).rdate;
int rtime = (*taxis).rtime;
if ( rdate == -1 )
    {
    rdate = (*taxis).vdate;
    rtime = (*taxis).vtime;
    }

if ( rdate == 0 && rtime == 0 && vdate == 0 && vtime == 0 ) return value;

int ryear, rmonth;
int year, month, day, hour, minute, second;
cdiDecodeDate(rdate, &ryear, &rmonth, &day);
cdiDecodeTime(rtime, &hour, &minute, &second);

encode_caldaysec(calendar, ryear, rmonth, day, hour, minute, second, &julday1, &secofday1);

cdiDecodeDate(vdate, &year, &month, &day);
cdiDecodeTime(vtime, &hour, &minute, &second);

int timeunit0 = timeunit;

if ( timeunit == TUNIT_MONTH && calendar == CALENDAR_360DAYS )
    {
    timeunit = TUNIT_DAY;
    }

if ( timeunit == TUNIT_MONTH || timeunit == TUNIT_YEAR )
    {
    value = (year-ryear)*12 - rmonth + month;

    int nmonth = (int) value;
    month -= nmonth;

    while ( month > 12 ) { month -= 12; year++; }
    while ( month <  1 ) { month += 12; year--; }

    int dpm = days_per_month(calendar, year, month);

    encode_caldaysec(calendar, year, month, day, hour, minute, second, &julday2, &secofday2);

    julday_sub(julday1, secofday1, julday2, secofday2, &days, &secs);

    value += (days+secs/86400.)/dpm;

    if ( timeunit == TUNIT_YEAR ) value = value/12;
    }
else
    {
    encode_caldaysec(calendar, year, month, day, hour, minute, second, &julday2, &secofday2);

    julday_sub(julday1, secofday1, julday2, secofday2, &days, &secs);

    cdiEncodeTimevalue(days, secs, timeunit, &value);
    }

if ( timeunit0 == TUNIT_MONTH && calendar == CALENDAR_360DAYS )
    {
    value /= 30;
    }

return value;
}


static
void conv_timeval(double timevalue, int64_t *rvdate, int *rvtime)
{
int daysec;

int64_t vdate = (int) timevalue;
if ( vdate < 0 )
    daysec = (int) (-(timevalue - vdate)*86400 + 0.01);
else
    daysec = (int) ( (timevalue - vdate)*86400 + 0.01);

int hour   =  daysec / 3600;
int minute = (daysec - hour*3600)/60;
int second =  daysec - hour*3600 - minute*60;
int vtime  = cdiEncodeTime(hour, minute, second);

*rvdate = vdate;
*rvtime = vtime;
}


static
void splitTimevalue(double timevalue, int timeunit, int64_t *date, int *time)
{
int64_t vdate = 0;
int vtime = 0;

if ( timeunit == TUNIT_SECOND )
    {
    timevalue /= 86400;
    conv_timeval(timevalue, &vdate, &vtime);
    }
else if ( timeunit == TUNIT_HOUR )
    {
    timevalue /= 24;
    conv_timeval(timevalue, &vdate, &vtime);
    }
else if ( timeunit == TUNIT_DAY )
    {
    conv_timeval(timevalue, &vdate, &vtime);
    }
else if ( timeunit == TUNIT_MONTH )
    {
    vdate = (int64_t) timevalue*100 - ((vdate < 0) * 2 - 1);
    vtime = 0;
    }
else if ( timeunit == TUNIT_YEAR )
    {
    if ( timevalue < -214700 )
        {
        Warning("Year %g out of range, set to -214700", timevalue);
        timevalue = -214700;
        }
    else if ( timevalue > 214700 )
        {
        Warning("Year %g out of range, set to 214700", timevalue);
        timevalue = 214700;
        }

    vdate = (int64_t) timevalue*10000;
    vdate += 101;
    vtime = 0;
    }
else
    {
    static bool lwarn = true;
    if ( lwarn )
        {
        Warning("timeunit %s unsupported!", tunitNamePtr(timeunit));
        lwarn = false;
        }
    }



int year, month, day;
cdiDecodeDate(vdate, &year, &month, &day);
int hour, minute, second;
cdiDecodeTime(vtime, &hour, &minute, &second);

if ( month > 17 || day > 31 || hour > 23 || minute > 59 || second > 59 )
    {
    if ( (month  > 17 || day > 31) && (year < -9999 || year > 9999) ) year = 1;
    if ( month  > 17 ) month  = 1;
    if ( day    > 31 ) day    = 1;
    if ( hour   > 23 ) hour   = 0;
    if ( minute > 59 ) minute = 0;
    if ( second > 59 ) second = 0;

    vdate = cdiEncodeDate(year, month, day);
    vtime = cdiEncodeTime(hour, minute, second);

    static bool lwarn = true;
    if ( lwarn )
        {
        lwarn = false;
        Warning("Reset wrong date/time to %4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d!",
                year, month, day, hour, minute, second);
        }
    }

*date = vdate;
*time = vtime;
}


void cdiSetForecastPeriod(double timevalue, taxis_t *taxis)
{
int64_t julday, days;
int secofday, secs;

(*taxis).fc_period = timevalue;

int timeunit = (*taxis).fc_unit;
int calendar = (*taxis).calendar;

int64_t vdate = (*taxis).vdate;
int vtime = (*taxis).vtime;

if ( vdate == 0 && vtime == 0 && DBL_IS_EQUAL(timevalue, 0.) ) return;

int year, month, day, hour, minute, second;
cdiDecodeDate(vdate, &year, &month, &day);
cdiDecodeTime(vtime, &hour, &minute, &second);

if ( timeunit == TUNIT_MONTH && calendar == CALENDAR_360DAYS )
    {
    timeunit = TUNIT_DAY;
    timevalue *= 30;
    }

if ( timeunit == TUNIT_MONTH || timeunit == TUNIT_YEAR )
    {
    int nmon, dpm;
    double fmon;

    if ( timeunit == TUNIT_YEAR ) timevalue *= 12;

    nmon = (int) timevalue;
    fmon = timevalue - nmon;

    month -= nmon;

    while ( month > 12 ) { month -= 12; year++; }
    while ( month <  1 ) { month += 12; year--; }

    dpm = days_per_month(calendar, year, month);
    timeunit = TUNIT_DAY;
    timevalue = fmon*dpm;
    }

encode_caldaysec(calendar, year, month, day, hour, minute, second, &julday, &secofday);

cdiDecodeTimevalue(timeunit, timevalue, &days, &secs);

julday_add(-days, -secs, &julday, &secofday);

decode_caldaysec(calendar, julday, secofday, &year, &month, &day, &hour, &minute, &second);

(*taxis).fdate = cdiEncodeDate(year, month, day);
(*taxis).ftime = cdiEncodeTime(hour, minute, second);
}


void cdiDecodeTimeval(double timevalue, taxis_t *taxis, int64_t *date, int *time)
{
if ( taxis->type == TAXIS_ABSOLUTE )
    splitTimevalue(timevalue, taxis->unit, date, time);
else
    timeval2vtime(timevalue, taxis, date, time);
}


double cdiEncodeTimeval(int64_t date, int time, taxis_t *taxis)
{
double timevalue;

if ( taxis->type == TAXIS_ABSOLUTE )
    {
    if ( taxis->unit == TUNIT_YEAR )
        {
        int year, month, day;
        cdiDecodeDate(date, &year, &month, &day);

        timevalue = year;
        }
    else if ( taxis->unit == TUNIT_MONTH )
        {
        int year, month, day;
        cdiDecodeDate(date, &year, &month, &day);
        timevalue = date/100
            + copysign((double)(day != 0) * 0.5, (double)date);
        }
    else
        {
        int hour, minute, second;
        cdiDecodeTime(time, &hour, &minute, &second);
        timevalue = copysign(1.0, (double)date)
            * (fabs((double)date) + (hour*3600 + minute*60 + second)/86400.);
        }
    }
else
    timevalue = vtime2timeval(date, time, taxis);

return timevalue;
}


void ptaxisInit(taxis_t *taxisptr)
{
taxisDefaultValue ( taxisptr );
}


void ptaxisCopy(taxis_t *dest, taxis_t *source)
{
reshLock ();


dest->used        = source->used;
dest->datatype    = source->datatype;
dest->type        = source->type;
dest->vdate       = source->vdate;
dest->vtime       = source->vtime;
dest->rdate       = source->rdate;
dest->rtime       = source->rtime;
dest->fdate       = source->fdate;
dest->ftime       = source->ftime;
dest->calendar    = source->calendar;
dest->unit        = source->unit;
dest->numavg      = source->numavg;
dest->climatology = source->climatology;
dest->has_bounds  = source->has_bounds;
dest->vdate_lb    = source->vdate_lb;
dest->vtime_lb    = source->vtime_lb;
dest->vdate_ub    = source->vdate_ub;
dest->vtime_ub    = source->vtime_ub;
dest->fc_unit     = source->fc_unit;
dest->fc_period   = source->fc_period;

dest->climatology = source->climatology;
delete_refcount_string(dest->name);
delete_refcount_string(dest->longname);
delete_refcount_string(dest->units);
dest->name = dup_refcount_string(source->name);
dest->longname = dup_refcount_string(source->longname);
dest->units = dup_refcount_string(source->units);
if (dest->self != CDI_UNDEFID)
    reshSetStatus(dest->self, &taxisOps, RESH_DESYNC_IN_USE);

reshUnlock ();
}


static void
taxisPrintKernel(taxis_t * taxisptr, FILE * fp)
{
int64_t vdate_lb, vdate_ub;
int vtime_lb, vtime_ub;

taxisInqVdateBounds ( taxisptr->self, &vdate_lb, &vdate_ub);
taxisInqVtimeBounds ( taxisptr->self, &vtime_lb, &vtime_ub);

fprintf(fp,
        "#\n"
        "# taxisID %d\n"
        "#\n"
        "self        = %d\n"
        "used        = %d\n"
        "type        = %d\n"
        "vdate       = %ld\n"
        "vtime       = %d\n"
        "rdate       = %ld\n"
        "rtime       = %d\n"
        "fdate       = %ld\n"
        "ftime       = %d\n"
        "calendar    = %d\n"
        "unit        = %d\n"
        "numavg      = %d\n"
        "climatology = %d\n"
        "has_bounds  = %d\n"
        "vdate_lb    = %ld\n"
        "vtime_lb    = %d\n"
        "vdate_ub    = %ld\n"
        "vtime_ub    = %d\n"
        "fc_unit     = %d\n"
        "fc_period   = %g\n"
        "\n", taxisptr->self, taxisptr->self,
        (int)taxisptr->used, taxisptr->type,
        taxisptr->vdate, taxisptr->vtime,
        taxisptr->rdate, taxisptr->rtime,
        taxisptr->fdate, taxisptr->ftime,
        taxisptr->calendar, taxisptr->unit,
        taxisptr->numavg, (int)taxisptr->climatology,
        (int) taxisptr->has_bounds,
        vdate_lb, vtime_lb, vdate_ub, vtime_ub,
        taxisptr->fc_unit, taxisptr->fc_period );
}

static int
taxisCompareP(void *taxisptr1, void *taxisptr2)
{
const taxis_t *t1 = ( const taxis_t * ) taxisptr1,
    *t2 = ( const taxis_t * ) taxisptr2;

xassert ( t1 && t2 );

return ! ( t1->used        == t2->used        &&
            t1->type        == t2->type        &&
            t1->vdate       == t2->vdate       &&
            t1->vtime       == t2->vtime       &&
            t1->rdate       == t2->rdate       &&
            t1->rtime       == t2->rtime       &&
            t1->fdate       == t2->fdate       &&
            t1->ftime       == t2->ftime       &&
            t1->calendar    == t2->calendar    &&
            t1->unit        == t2->unit        &&
            t1->fc_unit     == t2->fc_unit     &&
            t1->numavg      == t2->numavg      &&
            t1->climatology == t2->climatology &&
            t1->has_bounds  == t2->has_bounds  &&
            t1->vdate_lb    == t2->vdate_lb    &&
            t1->vtime_lb    == t2->vtime_lb    &&
            t1->vdate_ub    == t2->vdate_ub    &&
            t1->vtime_ub    == t2->vtime_ub );
}


static int
taxisTxCode ( void )
{
return TAXIS;
}

enum { taxisNint = 22 };

static int
taxisGetPackSize(void *p, void *context)
{
taxis_t *taxisptr = (taxis_t*) p;
int packBufferSize
    = serializeGetSize(taxisNint, CDI_DATATYPE_INT, context)
    + serializeGetSize(1, CDI_DATATYPE_UINT32, context)
    + (taxisptr->name ?
    serializeGetSize((int)strlen(taxisptr->name), CDI_DATATYPE_TXT, context) : 0)
    + (taxisptr->longname ?
    serializeGetSize((int)strlen(taxisptr->longname), CDI_DATATYPE_TXT, context) : 0)
    + (taxisptr->units ?
    serializeGetSize((int)strlen(taxisptr->units), CDI_DATATYPE_TXT, context) : 0);
return packBufferSize;
}

int
taxisUnpack(char * unpackBuffer, int unpackBufferSize, int * unpackBufferPos,
            int originNamespace, void *context, int force_id)
{
taxis_t * taxisP;
int intBuffer[taxisNint];
uint32_t d;
int idx = 0;

serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                intBuffer, taxisNint, CDI_DATATYPE_INT, context);
serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                &d, 1, CDI_DATATYPE_UINT32, context);

xassert(cdiCheckSum(CDI_DATATYPE_INT, taxisNint, intBuffer) == d);

taxisInit();

cdiResH targetID = namespaceAdaptKey(intBuffer[idx++], originNamespace);
taxisP = taxisNewEntry(force_id?targetID:CDI_UNDEFID);

xassert(!force_id || targetID == taxisP->self);

taxisP->used        = (short)intBuffer[idx++];
taxisP->type        = intBuffer[idx++];
taxisP->vdate       = intBuffer[idx++];
taxisP->vtime       = intBuffer[idx++];
taxisP->rdate       = intBuffer[idx++];
taxisP->rtime       = intBuffer[idx++];
taxisP->fdate       = intBuffer[idx++];
taxisP->ftime       = intBuffer[idx++];
taxisP->calendar    = intBuffer[idx++];
taxisP->unit        = intBuffer[idx++];
taxisP->fc_unit     = intBuffer[idx++];
taxisP->numavg      = intBuffer[idx++];
taxisP->climatology = intBuffer[idx++];
taxisP->has_bounds  = (short)intBuffer[idx++];
taxisP->vdate_lb    = intBuffer[idx++];
taxisP->vtime_lb    = intBuffer[idx++];
taxisP->vdate_ub    = intBuffer[idx++];
taxisP->vtime_ub    = intBuffer[idx++];

if (intBuffer[idx])
    {
    int len = intBuffer[idx];
    char *name = new_refcount_string((size_t)len);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    name, len, CDI_DATATYPE_TXT, context);
    name[len] = '\0';
    taxisP->name = name;
    }
idx++;
if (intBuffer[idx])
    {
    int len = intBuffer[idx];
    char *longname = new_refcount_string((size_t)len);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    longname, len, CDI_DATATYPE_TXT, context);
    longname[len] = '\0';
    taxisP->longname = longname;
    }
if (intBuffer[idx])
    {
    int len = intBuffer[idx];
    char *units = new_refcount_string((size_t)len);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    units, len, CDI_DATATYPE_TXT, context);
    units[len] = '\0';
    taxisP->units = units;
    }

reshSetStatus(taxisP->self, &taxisOps,
                reshGetStatus(taxisP->self, &taxisOps) & ~RESH_SYNC_BIT);

return taxisP->self;
}


static void
taxisPack(void * voidP, void * packBuffer, int packBufferSize, int * packBufferPos,
        void *context)
{
taxis_t *taxisP = (taxis_t *)voidP;
int intBuffer[taxisNint];
uint32_t d;
int idx = 0;

intBuffer[idx++] = taxisP->self;
intBuffer[idx++] = taxisP->used;
intBuffer[idx++] = taxisP->type;
intBuffer[idx++] = taxisP->vdate;
intBuffer[idx++] = taxisP->vtime;
intBuffer[idx++] = taxisP->rdate;
intBuffer[idx++] = taxisP->rtime;
intBuffer[idx++] = taxisP->fdate;
intBuffer[idx++] = taxisP->ftime;
intBuffer[idx++] = taxisP->calendar;
intBuffer[idx++] = taxisP->unit;
intBuffer[idx++] = taxisP->fc_unit;
intBuffer[idx++] = taxisP->numavg;
intBuffer[idx++] = taxisP->climatology;
intBuffer[idx++] = taxisP->has_bounds;
intBuffer[idx++] = taxisP->vdate_lb;
intBuffer[idx++] = taxisP->vtime_lb;
intBuffer[idx++] = taxisP->vdate_ub;
intBuffer[idx++] = taxisP->vtime_ub;
intBuffer[idx++] = taxisP->name ? (int)strlen(taxisP->name) : 0;
intBuffer[idx++] = taxisP->longname ? (int)strlen(taxisP->longname) : 0;
intBuffer[idx++] = taxisP->units ? (int)strlen(taxisP->units) : 0;

serializePack(intBuffer, taxisNint, CDI_DATATYPE_INT,
                packBuffer, packBufferSize, packBufferPos, context);
d = cdiCheckSum(CDI_DATATYPE_INT, taxisNint, intBuffer);
serializePack(&d, 1, CDI_DATATYPE_UINT32,
                packBuffer, packBufferSize, packBufferPos, context);
if (taxisP->name)
    serializePack(taxisP->name, intBuffer[15], CDI_DATATYPE_TXT,
                packBuffer, packBufferSize, packBufferPos, context);
if (taxisP->longname)
    serializePack(taxisP->longname, intBuffer[16], CDI_DATATYPE_TXT,
                packBuffer, packBufferSize, packBufferPos, context);
if (taxisP->units)
    serializePack(taxisP->units, intBuffer[16], CDI_DATATYPE_TXT,
                packBuffer, packBufferSize, packBufferPos, context);

}


#include <math.h>



void decode_julday(int calendar,
                int64_t julday,
                int *year,
                int *mon,
                int *day)
{
int64_t a = julday;

double b = floor((a - 1867216.25)/36524.25);
double c = a + b - floor(b/4) + 1525;

if ( calendar == CALENDAR_STANDARD || calendar == CALENDAR_GREGORIAN )
    if ( a < 2299161 ) c = a + 1524;

double d = floor((c - 122.1)/365.25);
double e = floor(365.25*d);
double f = floor((c - e)/30.6001);

*day  = (int)(c - e - floor(30.6001*f));
*mon  = (int)(f - 1 - 12*floor(f/14));
*year = (int)(d - 4715 - floor((7 + *mon)/10));
}



int64_t encode_julday(int calendar, int year, int month, int day)
{
int iy;
int im;
int ib;

if ( month <= 2 )
    {
    iy = year  - 1;
    im = month + 12;
    }
else
    {
    iy = year;
    im = month;
    }


if ( iy < 0 )
    ib = (int)((iy+1)/400) - (int)((iy+1)/100);
else
    ib = (int)(iy/400) - (int)(iy/100);

if ( calendar == CALENDAR_STANDARD || calendar == CALENDAR_GREGORIAN )
    {
    if ( year > 1582 || (year == 1582 && (month > 10 || (month == 10 && day >= 15))) )
        {

        }
    else
        {

        ib = -2;
        }
    }

int64_t julday = (int64_t) (floor(365.25*iy) + (int64_t)(30.6001*(im+1)) + ib + 1720996.5 + day + 0.5);

return julday;
}


int64_t date_to_julday(int calendar, int64_t date)
{
int year, month, day;
cdiDecodeDate(date, &year, &month, &day);

int64_t julday = encode_julday(calendar, year, month, day);

return julday;
}


int64_t julday_to_date(int calendar, int64_t julday)
{
int year, month, day;
decode_julday(calendar, julday, &year, &month, &day);

int64_t date = cdiEncodeDate(year, month, day);

return date;
}


int time_to_sec(int time)
{
int hour, minute, second;
cdiDecodeTime(time, &hour, &minute, &second);

int secofday = hour*3600 + minute*60 + second;

return secofday;
}


int sec_to_time(int secofday)
{
int hour   = secofday/3600;
int minute = secofday/60 - hour*60;
int second = secofday - hour*3600 - minute*60;

int time = cdiEncodeTime(hour, minute, second);

return time;
}

static
void adjust_seconds(int64_t *julday, int64_t *secofday)
{
int64_t secperday = 86400;

while ( *secofday >= secperday )
    {
    *secofday -= secperday;
    (*julday)++;
    }

while ( *secofday <  0 )
    {
    *secofday += secperday;
    (*julday)--;
    }
}


void julday_add_seconds(int64_t seconds, int64_t *julday, int *secofday)
{
int64_t sec_of_day = *secofday;

sec_of_day += seconds;

adjust_seconds(julday, &sec_of_day);

*secofday = (int) sec_of_day;
}


void julday_add(int days, int secs, int64_t *julday, int *secofday)
{
int64_t sec_of_day = *secofday;

sec_of_day += secs;
*julday    += days;

adjust_seconds(julday, &sec_of_day);

*secofday = (int) sec_of_day;
}


double julday_sub(int64_t julday1, int secofday1, int64_t julday2, int secofday2, int64_t *days, int *secs)
{
*days = julday2 - julday1;
*secs = secofday2 - secofday1;

int64_t sec_of_day = *secs;

adjust_seconds(days, &sec_of_day);

*secs = (int) sec_of_day;

int64_t seconds = (int64_t)(*days) * (int64_t)86400 + sec_of_day;

return (double)seconds;
}


void encode_juldaysec(int calendar, int year, int month, int day, int hour, int minute, int second, int64_t *julday, int *secofday)
{
*julday = encode_julday(calendar, year, month, day);

*secofday = (hour*60 + minute)*60 + second;
}


void decode_juldaysec(int calendar, int64_t julday, int secofday, int *year, int *month, int *day, int *hour, int *minute, int *second)
{
decode_julday(calendar, julday, year, month, day);

*hour   = secofday/3600;
*minute = secofday/60 - *hour*60;
*second = secofday - *hour*3600 - *minute*60;
}


#ifdef TEST
int main(void)
{
int nmin;
int64_t vdate0, vdate;
int vtime0, vtime;
int ijulinc;
int i, j = 0;
int year, mon, day, hour, minute, second;
int64_t julday;
int secofday;
int calendar = CALENDAR_STANDARD;



nmin = 11000;
vdate0 = -80001201;
vtime0 = 120500;

printf("start time: %8d %4d\n", vdate0, vtime0);

for ( i = 0; i < nmin; i++ )
    {
    cdiDecodeDate(vdate0, &year, &mon, &day);
    cdiDecodeTime(vtime0, &hour, &minute, &second);

    julday  = date_to_julday(calendar, vdate0);
    secofday = time_to_sec(vtime0);

    vdate = julday_to_date(calendar, julday);
    vtime = sec_to_time(secofday);

    if ( vdate0 != vdate || vtime0 != vtime )
        printf("%4d %8d %4d %8d %4d %9d %9d\n",
            ++j, vdate0, vtime0, vdate, vtime, julday, secofday);

    year++;
    vdate0 = cdiEncodeDate(year, mon, day);
    vtime0 = cdiEncodeTime(hour, minute, second);
    }

printf("stop time: %8d %4d\n", vdate0, vtime0);



nmin = 120000;
ijulinc = 60;
vdate0 = 20001201;
vtime0 = 0;

printf("start time: %8d %4d\n", vdate0, vtime0);

julday = date_to_julday(calendar, vdate0);
secofday = time_to_sec(vtime0);
for ( i = 0; i < nmin; i++ )
    {
    cdiDecodeDate(vdate0, &year, &mon, &day);
    cdiDecodeTime(vtime0, &hour, &minute, &second);

    if ( ++minute >= 60 )
        {
        minute = 0;
        if ( ++hour >= 24 )
            {
            hour = 0;
            if ( ++day >= 32 )
                {
                day = 1;
                if ( ++mon >= 13 )
                    {
                    mon = 1;
                    year++;
                    }
                }
            }
        }

    vdate0 = cdiEncodeDate(year, mon, day);
    vtime0 = cdiEncodeTime(hour, minute, second);

    julday_add_seconds(ijulinc, &julday, &secofday);

    vdate = julday_to_date(calendar, julday);
    vtime = sec_to_time(secofday);
    if ( vdate0 != vdate || vtime0 != vtime )
        printf("%4d %8d %4d %8d %4d %9d %9d\n",
            ++j, vdate0, vtime0, vdate, vtime, julday, secofday);
    }

printf("stop time: %8d %4d\n", vdate0, vtime0);

return (0);
}
#endif


#ifdef TEST2
int main(void)
{
int i;
int julday, secofday;
int year, month, day, hour, minute, second;
int value = 30;
int factor = 86400;
int calendar = CALENDAR_STANDARD;

year=1979; month=1; day=15; hour=12; minute=30, second=17;

printf("%d/%02d/%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second);

encode_juldaysec(calendar, year, month, day, hour, minute, second, &julday, &secofday);

decode_juldaysec(calendar, julday, secofday, &year, &month, &day, &hour, &minute, &second);
printf("%d/%02d/%02d %02d:%02d:%02d   %d %d\n", year, month, day, hour, minute, second, julday, secofday);

for ( i = 0; i < 420; i++ )
    {

    decode_juldaysec(calendar, julday, secofday, &year, &month, &day, &hour, &minute, &second);
    printf("%2d %d/%02d/%02d %02d:%02d:%02d\n", i, year, month, day, hour, minute, second);
    julday_add_seconds(value*factor, &julday, &secofday);
    }

return (0);
}
#endif

#include <limits.h>




static
void tstepsInitEntry(stream_t *streamptr, size_t tsID)
{
streamptr->tsteps[tsID].curRecID     = CDI_UNDEFID;
streamptr->tsteps[tsID].position     = 0;
streamptr->tsteps[tsID].records      = NULL;
streamptr->tsteps[tsID].recordSize   = 0;
streamptr->tsteps[tsID].nallrecs     = 0;
streamptr->tsteps[tsID].recIDs       = NULL;
streamptr->tsteps[tsID].nrecs        = 0;
streamptr->tsteps[tsID].next         = 0;

ptaxisInit(&streamptr->tsteps[tsID].taxis);
}


int tstepsNewEntry(stream_t *streamptr)
{
size_t tsID            = (size_t)streamptr->tstepsNextID++;
size_t tstepsTableSize = (size_t)streamptr->tstepsTableSize;
tsteps_t *tstepsTable  = streamptr->tsteps;


if ( tsID == tstepsTableSize )
    {
    if ( tstepsTableSize == 0 ) tstepsTableSize = 1;
    if ( tstepsTableSize <= INT_MAX / 2)
        tstepsTableSize *= 2;
    else if ( tstepsTableSize < INT_MAX)
        tstepsTableSize = INT_MAX;
    else
        Error("Resizing of tstep table failed!");
    tstepsTable = (tsteps_t *) Realloc(tstepsTable,
                                        tstepsTableSize * sizeof (tsteps_t));
    }

streamptr->tstepsTableSize = (int)tstepsTableSize;
streamptr->tsteps          = tstepsTable;

tstepsInitEntry(streamptr, tsID);

streamptr->tsteps[tsID].taxis.used = true;

return (int)tsID;
}


void cdiCreateTimesteps(stream_t *streamptr)
{
if ( streamptr->ntsteps < 0 || streamptr->tstepsTableSize > 0 )
    return;

long ntsteps = (streamptr->ntsteps == 0) ? 1 : streamptr->ntsteps;

streamptr->tsteps = (tsteps_t *) Malloc((size_t)ntsteps*sizeof(tsteps_t));

streamptr->tstepsTableSize = (int)ntsteps;
streamptr->tstepsNextID    = (int)ntsteps;

for ( long tsID = 0; tsID < ntsteps; tsID++ )
    {
    tstepsInitEntry(streamptr, (size_t)tsID);
    streamptr->tsteps[tsID].taxis.used = true;
    }
}

#ifdef HAVE_CONFIG_H
#endif

#define _XOPEN_SOURCE 600

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>



static const char uuidFmt[] = "%02hhx%02hhx%02hhx%02hhx-"
"%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-"
"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx";

enum {
uuidNumHexChars = 36,
};


void cdiUUID2Str(const unsigned char *uuid, char *uuidstr)
{

if ( uuid == NULL || uuidstr == NULL ) return;

int iret = sprintf(uuidstr, uuidFmt,
                    uuid[0], uuid[1], uuid[2], uuid[3],
                    uuid[4], uuid[5], uuid[6], uuid[7],
                    uuid[8], uuid[9], uuid[10], uuid[11],
                    uuid[12], uuid[13], uuid[14], uuid[15]);

if ( iret != uuidNumHexChars ) uuidstr[0] = 0;
}


int cdiStr2UUID(const char *uuidstr, unsigned char *uuid)
{
if ( uuid == NULL || uuidstr == NULL || strlen(uuidstr) != uuidNumHexChars)
    return -1;

int iret = sscanf(uuidstr, uuidFmt,
                    &uuid[0], &uuid[1], &uuid[2], &uuid[3],
                    &uuid[4], &uuid[5], &uuid[6], &uuid[7],
                    &uuid[8], &uuid[9], &uuid[10], &uuid[11],
                    &uuid[12], &uuid[13], &uuid[14], &uuid[15]);
if ( iret != CDI_UUID_SIZE ) return -1;
return iret;
}


char* cdiEscapeSpaces(const char* string)
{

size_t escapeCount = 0, length = 0;
for(; string[length]; ++length)
    escapeCount += string[length] == ' ' || string[length] == '\\';

char* result = (char *) Malloc(length + escapeCount + 1);
if(!result) return NULL;


for(size_t in = 0, out = 0; in < length; ++out, ++in)
    {
    if(string[in] == ' ' || string[in] == '\\') result[out++] = '\\';
    result[out] = string[in];
    }
result[length + escapeCount] = 0;
return result;
}




char* cdiUnescapeSpaces(const char* string, const char** outStringEnd)
{

size_t escapeCount = 0, length = 0;
for(const char* current = string; *current && *current != ' '; current++)
    {
    if(*current == '\\')
        {
        current++, escapeCount++;
        if(!current) return NULL;
        }
    length++;
    }

char* result = (char *) Malloc(length + 1);
if(!result) return NULL;


for(size_t in = 0, out = 0; out < length;)
    {
    if(string[in] == '\\') in++;
    result[out++] = string[in++];
    }
result[length] = 0;
if(outStringEnd) *outStringEnd = &string[length + escapeCount];
return result;
}

#if defined(HAVE_DECL_UUID_GENERATE) && defined(HAVE_UUID_UUID_H)
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <uuid/uuid.h>
void cdiCreateUUID(unsigned char *uuid)
{
// commented out for Windows build -- not needed  
/*
static int uuid_seeded = 0;
static char uuid_rand_state[31 * sizeof (long)];
char *caller_rand_state;
if (uuid_seeded)
    caller_rand_state = setstate(uuid_rand_state);
else
    {
    struct timeval tv;
    int status = gettimeofday(&tv, NULL);
    if (status != 0)
        {
        perror("uuid random seed generation failed!");
        exit(1);
        }
    unsigned seed = (unsigned)(tv.tv_sec ^ tv.tv_usec);
    caller_rand_state = initstate(seed, uuid_rand_state, sizeof (uuid_rand_state));
    uuid_seeded = 1;
    }
uuid_generate(uuid);
setstate(caller_rand_state);
*/
}
#elif defined (HAVE_DECL_UUID_CREATE) && defined (HAVE_UUID_H)
#  ifdef HAVE_DECL_UUID_MAKE_V5
#    include <uuid.h>
void cdiCreateUUID(unsigned char *uuid)
{
static const char error_stage[][16]
    = { "uuid_create", "uuid_create", "uuid_load", "uuid_make", "uuid_export",
        "uuid_destroy1", "uuid_destroy2" };
uuid_t *objuuid = NULL, *nsuuid = NULL;
int stage = 0;
uuid_rc_t status;
if ((status = uuid_create(&objuuid)) == UUID_RC_OK)
    {
    ++stage;
    if ((status = uuid_create(&nsuuid)) == UUID_RC_OK)
        {
        ++stage;
        if ((status = uuid_load(nsuuid, "ns:OID")) == UUID_RC_OK)
            {
            ++stage;
            if ((status = uuid_make(objuuid, UUID_MAKE_V5, nsuuid, cdiLibraryVersion()))
                == UUID_RC_OK)
                {
                ++stage;
                size_t datalen = CDI_UUID_SIZE;
                status = uuid_export(objuuid, UUID_FMT_BIN, &uuid, &datalen);
                }
            }
        }
    }
if (status != UUID_RC_OK)
    Error("failed to generate UUID at stage %s\n", error_stage[stage]);
stage = 5;
if ((status = uuid_destroy(nsuuid)) != UUID_RC_OK)
    Error("failed to generate UUID at stage %s\n", error_stage[stage]);
++stage;
if ((status = uuid_destroy(objuuid)) != UUID_RC_OK)
    Error("failed to generate UUID at stage %s\n", error_stage[stage]);
}
#  else
#include <inttypes.h>
typedef uint8_t u_int8_t;
typedef uint16_t u_int16_t;
typedef uint32_t u_int32_t;
#include <uuid.h>
void cdiCreateUUID(unsigned char *uuid)
{
uint32_t status;
uuid_create((uuid_t *)(void *)uuid, &status);
if (status != uuid_s_ok)
    {
    perror("uuid generation failed!");
    exit(1);
    }
}
#  endif
#else
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
void cdiCreateUUID(unsigned char *uuid)
{
// commented out for Windows build -- not needed  
/*static int uuid_seeded = 0;
#ifndef _SX
static char uuid_rand_state[31 * sizeof (long)];
char *caller_rand_state;
if (uuid_seeded)
    caller_rand_state = setstate(uuid_rand_state);
else
    {
#ifdef HAVE_SYS_TIME_H
    struct timeval tv;
    int status = gettimeofday(&tv, NULL);
    if (status != 0)
        {
        perror("failed seed generation!");
        exit(1);
        }
    unsigned seed = tv.tv_sec ^ tv.tv_usec;
#else
    unsigned seed = 0;
#endif
    caller_rand_state = initstate(seed, uuid_rand_state,
                                    sizeof (uuid_rand_state));
    uuid_seeded = 1;
    }
for (size_t i = 0; i < CDI_UUID_SIZE; ++i)
    uuid[i] = (unsigned char)random();
#else
unsigned short caller_rand_state[3];
{
    static unsigned short our_rand_state[3];
    if (!uuid_seeded)
    {
#ifdef HAVE_SYS_TIME_H
        struct timeval tv;
        int status = gettimeofday(&tv, NULL);
        if (status != 0)
        {
            perror("failed seed generation!");
            exit(1);
        }
        unsigned seed = tv.tv_sec ^ tv.tv_usec;
#else
        unsigned seed = 0;
#endif
        our_rand_state[0] = 0x330E;
        our_rand_state[1] = (unsigned short)(seed & 0xFFFFU);
        our_rand_state[2] = (unsigned short)((seed >> 16) & 0xFFFFU);
    }
    unsigned short *p = seed48(our_rand_state);
    uuid_seeded = 1;
    memcpy(caller_rand_state, p, sizeof (caller_rand_state));
}
for (size_t i = 0; i < CDI_UUID_SIZE; ++i)
    uuid[i] = (unsigned char)lrand48();
#endif

uuid[8] = (unsigned char)((uuid[8] & 0x3f) | (1 << 7));

uuid[7] = (unsigned char)((uuid[7] & 0x0f) | (4 << 4));
#ifndef _SX
setstate(caller_rand_state);
#else
seed48(caller_rand_state);
#endif*/
}
#endif


#ifdef HAVE_CONFIG_H
#endif




static size_t Vctsize = 0;
static double *Vct = NULL;

static int numberOfVerticalLevels = 0;
static int numberOfVerticalGrid = 0;
static unsigned char uuidVGrid[CDI_UUID_SIZE];


typedef struct
{
int      level1;
int      level2;
int      recID;
int      lindex;
}
leveltable_t;


typedef struct
{
int           subtypeIndex;

unsigned      nlevels;
int           levelTableSize;
leveltable_t* levelTable;
} subtypetable_t;


typedef struct
{
int            varID;
int            param;
int            prec;
int            tsteptype;
int            gridID;
int            zaxistype;
int            ltype1;
int            ltype2;
int            lbounds;
int            level_sf;
int            level_unit;
int            zaxisID;

int            nsubtypes_alloc;
int            nsubtypes;
subtypetable_t *recordTable;

int            instID;
int            modelID;
int            tableID;
int            comptype;
int            complevel;
short          timave;
bool           lmissval;
double         missval;
char          *name;
char          *stdname;
char          *longname;
char          *units;


subtype_t     *tiles;

cdi_keys_t     keys;

int                 opt_grib_nentries;
int                 opt_grib_kvpair_size;
opt_key_val_pair_t *opt_grib_kvpair;
}
vartable_t;


static vartable_t *vartable;
static unsigned varTablesize = 0;
static unsigned varTableUsed = 0;

static
void paramInitEntry(unsigned varID, int param)
{
vartable[varID].varID          = (int)varID;
vartable[varID].param          = param;
vartable[varID].prec           = 0;
vartable[varID].tsteptype      = TSTEP_INSTANT;
vartable[varID].timave         = 0;
vartable[varID].gridID         = CDI_UNDEFID;
vartable[varID].zaxistype      = 0;
vartable[varID].ltype1         = 0;
vartable[varID].ltype2         = -1;
vartable[varID].lbounds        = 0;
vartable[varID].level_sf       = 0;
vartable[varID].level_unit     = 0;
vartable[varID].recordTable    = NULL;
vartable[varID].nsubtypes_alloc= 0;
vartable[varID].nsubtypes      = 0;
vartable[varID].instID         = CDI_UNDEFID;
vartable[varID].modelID        = CDI_UNDEFID;
vartable[varID].tableID        = CDI_UNDEFID;
vartable[varID].keys.nalloc    = MAX_KEYS;
vartable[varID].keys.nelems    = 0;
vartable[varID].comptype       = CDI_COMPRESS_NONE;
vartable[varID].complevel      = 1;
vartable[varID].lmissval       = false;
vartable[varID].missval        = 0;
vartable[varID].name           = NULL;
vartable[varID].stdname        = NULL;
vartable[varID].longname       = NULL;
vartable[varID].units          = NULL;
vartable[varID].tiles          = NULL;
}


static unsigned
varGetEntry(int param, int gridID, int zaxistype, int ltype1, int tsteptype, const char *name, const var_tile_t *tiles)
{
for ( unsigned varID = 0; varID < varTablesize; varID++ )
    {

    if ( vartable[varID].param == param )
        {
        int no_of_tiles = -1;
        if ( tiles ) no_of_tiles = tiles->numberOfTiles;
        int vt_no_of_tiles = -1;
        if ( vartable[varID].tiles )
            vt_no_of_tiles = subtypeGetGlobalDataP(vartable[varID].tiles,
                                                SUBTYPE_ATT_NUMBER_OF_TILES);
        if ( (vartable[varID].zaxistype  == zaxistype)               &&
            (vartable[varID].ltype1     == ltype1   )               &&
            (vartable[varID].tsteptype  == tsteptype)               &&
            (vartable[varID].gridID     == gridID   )               &&
            (vt_no_of_tiles == no_of_tiles) )
            {
            if ( name && name[0] && vartable[varID].name && vartable[varID].name[0] )
                {
                if ( strcmp(name, vartable[varID].name) == 0 ) return varID;
                }
            else
                {
                return varID;
                }
            }
        }
    }

return (unsigned)-1;
}

static
void varFree(void)
{
if ( CDI_Debug ) Message("call to varFree");

for ( size_t varID = 0; varID < varTableUsed; varID++ )
    {
    if ( vartable[varID].recordTable )
        {
        for (int isub=0; isub<vartable[varID].nsubtypes_alloc; isub++)
            Free(vartable[varID].recordTable[isub].levelTable);
        Free(vartable[varID].recordTable);
        }

    if ( vartable[varID].name )     Free(vartable[varID].name);
    if ( vartable[varID].stdname )  Free(vartable[varID].stdname);
    if ( vartable[varID].longname ) Free(vartable[varID].longname);
    if ( vartable[varID].units )    Free(vartable[varID].units);
    if ( vartable[varID].tiles )    subtypeDestroyPtr(vartable[varID].tiles);

    cdi_keys_t *keysp = &(vartable[varID].keys);
    cdiDeleteVarKeys(keysp);

    if ( vartable[varID].opt_grib_kvpair )
        {
        for (int i=0; i<vartable[varID].opt_grib_nentries; i++) {
            if ( vartable[varID].opt_grib_kvpair[i].keyword )
            Free(vartable[varID].opt_grib_kvpair[i].keyword);
        }
        Free(vartable[varID].opt_grib_kvpair);
        }
    vartable[varID].opt_grib_nentries    = 0;
    vartable[varID].opt_grib_kvpair_size = 0;
    vartable[varID].opt_grib_kvpair      = NULL;
    }

if ( vartable )
    Free(vartable);

vartable = NULL;
varTablesize = 0;
varTableUsed = 0;

if ( Vct )
    Free(Vct);

Vct = NULL;
Vctsize = 0;
}


static int tileGetEntry(unsigned varID, int tile_index)
{
for (int isub=0; isub<vartable[varID].nsubtypes; isub++)
    if (vartable[varID].recordTable[isub].subtypeIndex == tile_index)
    return isub;
return CDI_UNDEFID;
}



static int tileNewEntry(int varID)
{
int tileID = 0;
if (vartable[varID].nsubtypes_alloc == 0)
    {

    vartable[varID].nsubtypes_alloc = 2;
    vartable[varID].nsubtypes       = 0;
    vartable[varID].recordTable     =
        (subtypetable_t *) Malloc((size_t)vartable[varID].nsubtypes_alloc * sizeof (subtypetable_t));
    if( vartable[varID].recordTable == NULL )
        SysError("Allocation of leveltable failed!");

    for (int isub = 0; isub<vartable[varID].nsubtypes_alloc; isub++) {
        vartable[varID].recordTable[isub].levelTable     = NULL;
        vartable[varID].recordTable[isub].levelTableSize = 0;
        vartable[varID].recordTable[isub].nlevels        = 0;
        vartable[varID].recordTable[isub].subtypeIndex   = CDI_UNDEFID;
    }
    }
else
    {

    while(tileID <  vartable[varID].nsubtypes_alloc)
        {
        if (vartable[varID].recordTable[tileID].levelTable == NULL) break;
        tileID++;
        }
    }


if (tileID == vartable[varID].nsubtypes_alloc)
    {
    tileID = vartable[varID].nsubtypes_alloc;
    vartable[varID].nsubtypes_alloc *= 2;
    vartable[varID].recordTable   =
        (subtypetable_t *) Realloc(vartable[varID].recordTable,
                                (size_t)vartable[varID].nsubtypes_alloc * sizeof (subtypetable_t));
    if (vartable[varID].recordTable == NULL)
        SysError("Reallocation of leveltable failed");
    for(int isub=tileID; isub<vartable[varID].nsubtypes_alloc; isub++) {
        vartable[varID].recordTable[isub].levelTable     = NULL;
        vartable[varID].recordTable[isub].levelTableSize = 0;
        vartable[varID].recordTable[isub].nlevels        = 0;
        vartable[varID].recordTable[isub].subtypeIndex   = CDI_UNDEFID;
    }
    }

return tileID;
}


static int levelNewEntry(unsigned varID, int level1, int level2, int tileID)
{
int levelID = 0;
int levelTableSize = vartable[varID].recordTable[tileID].levelTableSize;
leveltable_t *levelTable = vartable[varID].recordTable[tileID].levelTable;


if ( ! levelTableSize )
    {
    levelTableSize = 2;
    levelTable = (leveltable_t *) Malloc((size_t)levelTableSize
                                        * sizeof (leveltable_t));
    for ( int i = 0; i < levelTableSize; i++ )
        levelTable[i].recID = CDI_UNDEFID;
    }
else
    {
    while( levelID < levelTableSize
            && levelTable[levelID].recID != CDI_UNDEFID )
        ++levelID;
    }

if ( levelID == levelTableSize )
    {
    levelTable = (leveltable_t *) Realloc(levelTable,
                                            (size_t)(levelTableSize *= 2)
                                            * sizeof (leveltable_t));
    for( int i = levelID; i < levelTableSize; i++ )
        levelTable[i].recID = CDI_UNDEFID;
    }

levelTable[levelID].level1   = level1;
levelTable[levelID].level2   = level2;
levelTable[levelID].lindex   = levelID;

vartable[varID].recordTable[tileID].nlevels        = (unsigned)levelID+1;
vartable[varID].recordTable[tileID].levelTableSize = levelTableSize;
vartable[varID].recordTable[tileID].levelTable     = levelTable;

return levelID;
}

#define  UNDEF_PARAM  -4711

static unsigned
paramNewEntry(int param)
{
unsigned varID = 0;


if ( ! varTablesize )
    {
    varTablesize = 2;
    vartable = (vartable_t *) Malloc((size_t)varTablesize
                                    * sizeof (vartable_t));
    if( vartable == NULL )
        {
        Message("varTablesize = %d", varTablesize);
        SysError("Allocation of vartable failed");
        }

    for( unsigned i = 0; i < varTablesize; i++ )
        {
        vartable[i].param = UNDEF_PARAM;
        vartable[i].opt_grib_kvpair      = NULL;
        vartable[i].opt_grib_kvpair_size = 0;
        vartable[i].opt_grib_nentries    = 0;
        }
    }
else
    {
    while( varID < varTablesize )
        {
        if ( vartable[varID].param == UNDEF_PARAM ) break;
        varID++;
        }
    }

if ( varID == varTablesize )
    {
    vartable = (vartable_t *) Realloc(vartable, (size_t)(varTablesize *= 2)
                                        * sizeof (vartable_t));
    for ( size_t i = varID; i < varTablesize; i++ )
        {
        vartable[i].param = UNDEF_PARAM;
        vartable[i].opt_grib_kvpair      = NULL;
        vartable[i].opt_grib_kvpair_size = 0;
        vartable[i].opt_grib_nentries    = 0;
        }
    }

paramInitEntry(varID, param);

return varID;
}



static
int varInsertTileSubtype(vartable_t *vptr, const var_tile_t *tiles)
{
if ( tiles == NULL ) return -1;



subtype_t *subtype_ptr;
subtypeAllocate(&subtype_ptr, SUBTYPE_TILES);
subtypeDefGlobalDataP(subtype_ptr, SUBTYPE_ATT_TOTALNO_OF_TILEATTR_PAIRS, tiles->totalno_of_tileattr_pairs);
subtypeDefGlobalDataP(subtype_ptr, SUBTYPE_ATT_TILE_CLASSIFICATION      , tiles->tileClassification);
subtypeDefGlobalDataP(subtype_ptr, SUBTYPE_ATT_NUMBER_OF_TILES          , tiles->numberOfTiles);


struct subtype_entry_t *entry = subtypeEntryInsert(subtype_ptr);
subtypeDefEntryDataP(entry, SUBTYPE_ATT_NUMBER_OF_ATTR,            tiles->numberOfAttributes);
subtypeDefEntryDataP(entry, SUBTYPE_ATT_TILEINDEX,                 tiles->tileindex);
subtypeDefEntryDataP(entry, SUBTYPE_ATT_TILEATTRIBUTE,             tiles->attribute);

if (vptr->tiles == NULL) {
    vptr->tiles = subtype_ptr;
    return 0;
}
else {
    tilesetInsertP(vptr->tiles, subtype_ptr);
    subtypeDestroyPtr(subtype_ptr);
    return vptr->tiles->nentries - 1;
}

return CDI_UNDEFID;
}


void varAddRecord(int recID, int param, int gridID, int zaxistype, int lbounds,
                int level1, int level2, int level_sf, int level_unit, int prec,
                int *pvarID, int *plevelID, int tsteptype, int numavg, int ltype1, int ltype2,
                const char *name, const char *stdname, const char *longname, const char *units,
                const var_tile_t *tiles, int *tile_index)
{
unsigned varID = (cdiSplitLtype105 != 1 || zaxistype != ZAXIS_HEIGHT) ?
    varGetEntry(param, gridID, zaxistype, ltype1, tsteptype, name, tiles) : (unsigned) CDI_UNDEFID;

if ( varID == (unsigned) CDI_UNDEFID )
    {
    varTableUsed++;
    varID = paramNewEntry(param);
    vartable[varID].gridID     = gridID;
    vartable[varID].zaxistype  = zaxistype;
    vartable[varID].ltype1     = ltype1;
    vartable[varID].ltype2     = ltype2;
    vartable[varID].lbounds    = lbounds;
    vartable[varID].level_sf   = level_sf;
    vartable[varID].level_unit = level_unit;
    vartable[varID].tsteptype  = tsteptype;

    if ( numavg ) vartable[varID].timave = 1;

    if ( name && name[0] )         vartable[varID].name     = strdup(name);
    if ( stdname && stdname[0] )   vartable[varID].stdname  = strdup(stdname);
    if ( longname && longname[0] ) vartable[varID].longname = strdup(longname);
    if ( units && units[0] )       vartable[varID].units    = strdup(units);
    }
else
    {
    char paramstr[32];
    cdiParamToString(param, paramstr, sizeof(paramstr));

    if ( vartable[varID].gridID != gridID )
        {
        Message("param = %s gridID = %d", paramstr, gridID);
        Error("horizontal grid must not change for same parameter!");
        }
    if ( vartable[varID].zaxistype != zaxistype )
        {
        Message("param = %s zaxistype = %d", paramstr, zaxistype);
        Error("zaxistype must not change for same parameter!");
        }
    }

if ( prec > vartable[varID].prec ) vartable[varID].prec = prec;


int this_tile = varInsertTileSubtype(&vartable[varID], tiles);
int tileID = tileGetEntry(varID, this_tile);
if ( tile_index ) (*tile_index) = this_tile;
if ( tileID == CDI_UNDEFID )
    {
    tileID = tileNewEntry((int)varID);
    vartable[varID].recordTable[tileID].subtypeIndex = this_tile;
    vartable[varID].nsubtypes++;
    }


int levelID = levelNewEntry(varID, level1, level2, tileID);
if (CDI_Debug)
    Message("vartable[%d].recordTable[%d].levelTable[%d].recID = %d; level1,2=%d,%d",
            varID, tileID, levelID, recID, level1, level2);
vartable[varID].recordTable[tileID].levelTable[levelID].recID = recID;

*pvarID   = (int) varID;
*plevelID = levelID;
}



static
int cmpLevelTable(const void* s1, const void* s2)
{
int cmp = 0;
const leveltable_t *x = (const leveltable_t*) s1;
const leveltable_t *y = (const leveltable_t*) s2;

if      ( x->level1 < y->level1 ) cmp = -1;
else if ( x->level1 > y->level1 ) cmp =  1;

return cmp;
}

static
int cmpLevelTableInv(const void* s1, const void* s2)
{
int cmp = 0;
const leveltable_t *x = (const leveltable_t*) s1;
const leveltable_t *y = (const leveltable_t*) s2;

if      ( x->level1 < y->level1 ) cmp =  1;
else if ( x->level1 > y->level1 ) cmp = -1;

return cmp;
}


void varCopyKeys(int vlistID, int varID)
{
cdi_keys_t *keysp = &(vartable[varID].keys);
cdiCopyVarKeys(keysp, vlistID, varID);
}


struct cdi_generate_varinfo
{
int        varid;
const char *name;
};

static
int cdi_generate_cmp_varname(const void *s1, const void *s2)
{
const struct cdi_generate_varinfo *x = (const struct cdi_generate_varinfo *)s1,
                                    *y = (const struct cdi_generate_varinfo *)s2;
return strcmp(x->name, y->name);
}

void cdi_generate_vars(stream_t *streamptr)
{
int vlistID = streamptr->vlistID;

int *varids = (int *) Malloc(varTableUsed*sizeof(int));
for ( size_t varID = 0; varID < varTableUsed; varID++ ) varids[varID] = (int)varID;



for ( size_t index = 0; index < varTableUsed; index++ )
    {
    int varid      = varids[index];

    int gridID     = vartable[varid].gridID;
    int param      = vartable[varid].param;
    int ltype1     = vartable[varid].ltype1;
    int ltype2     = vartable[varid].ltype2;
    int zaxistype  = vartable[varid].zaxistype;
    if ( ltype1 == 0 && zaxistype == ZAXIS_GENERIC && cdiDefaultLeveltype != -1 )
        zaxistype = cdiDefaultLeveltype;
    int lbounds    = vartable[varid].lbounds;
    int prec       = vartable[varid].prec;
    int instID     = vartable[varid].instID;
    int modelID    = vartable[varid].modelID;
    int tableID    = vartable[varid].tableID;
    int tsteptype  = vartable[varid].tsteptype;
    int timave     = vartable[varid].timave;
    int comptype   = vartable[varid].comptype;

    double level_sf = 1;
    if ( vartable[varid].level_sf != 0 ) level_sf = 1./vartable[varid].level_sf;


    unsigned nlevels = vartable[varid].recordTable[0].nlevels;
    for ( int isub = 1; isub < vartable[varid].nsubtypes; isub++ ) {
        if ( vartable[varid].recordTable[isub].nlevels != nlevels )
        {
            fprintf(stderr, "var \"%s\": isub = %d / %d :: "
                    "nlevels = %d, vartable[varid].recordTable[isub].nlevels = %d\n",
                    vartable[varid].name, isub, vartable[varid].nsubtypes,
                    nlevels, vartable[varid].recordTable[isub].nlevels);
            Error("zaxis size must not change for same parameter!");
        }

        leveltable_t *t1 = vartable[varid].recordTable[isub-1].levelTable;
        leveltable_t *t2 = vartable[varid].recordTable[isub  ].levelTable;
        for ( unsigned ilev = 0; ilev < nlevels; ilev++ )
        if ((t1[ilev].level1 != t2[ilev].level1)  ||
            (t1[ilev].level2 != t2[ilev].level2)  ||
            (t1[ilev].lindex != t2[ilev].lindex))
            {
            fprintf(stderr, "var \"%s\", varID=%d: isub = %d / %d :: "
                    "nlevels = %d, vartable[varid].recordTable[isub].nlevels = %d\n",
                    vartable[varid].name, varid, isub, vartable[varid].nsubtypes,
                    nlevels, vartable[varid].recordTable[isub].nlevels);
            Message("t1[ilev].level1=%d / t2[ilev].level1=%d", t1[ilev].level1, t2[ilev].level1);
            Message("t1[ilev].level2=%d / t2[ilev].level2=%d", t1[ilev].level2, t2[ilev].level2);
            Message("t1[ilev].lindex=%d / t2[ilev].lindex=%d", t1[ilev].lindex, t2[ilev].lindex);
            Error("zaxis type must not change for same parameter!");
            }
    }
    leveltable_t *levelTable = vartable[varid].recordTable[0].levelTable;

    if ( ltype1 == 0 && zaxistype == ZAXIS_GENERIC && nlevels == 1 && levelTable[0].level1 == 0 )
        zaxistype = ZAXIS_SURFACE;

    double *dlevels = (double *) Malloc(nlevels*sizeof(double));

    if ( lbounds && zaxistype != ZAXIS_HYBRID && zaxistype != ZAXIS_HYBRID_HALF )
        for ( unsigned levelID = 0; levelID < nlevels; levelID++ )
        dlevels[levelID] = (level_sf*levelTable[levelID].level1 +
                            level_sf*levelTable[levelID].level2)/2;
    else
        for ( unsigned levelID = 0; levelID < nlevels; levelID++ )
        dlevels[levelID] = level_sf*levelTable[levelID].level1;

    if ( nlevels > 1 )
        {
        bool linc = true, ldec = true, lsort = false;
        for ( unsigned levelID = 1; levelID < nlevels; levelID++ )
            {

            linc &= (dlevels[levelID] > dlevels[levelID-1]);

            ldec &= (dlevels[levelID] < dlevels[levelID-1]);
            }

        if ( (!linc && !ldec) && zaxistype == ZAXIS_PRESSURE )
            {
            qsort(levelTable, nlevels, sizeof(leveltable_t), cmpLevelTableInv);
            lsort = true;
            }

        else if ( (!linc && !ldec) ||
                    zaxistype == ZAXIS_HYBRID ||
                    zaxistype == ZAXIS_DEPTH_BELOW_LAND )
            {
            qsort(levelTable, nlevels, sizeof(leveltable_t), cmpLevelTable);
            lsort = true;
            }

        if ( lsort )
            {
            if ( lbounds && zaxistype != ZAXIS_HYBRID && zaxistype != ZAXIS_HYBRID_HALF )
                for ( unsigned levelID = 0; levelID < nlevels; levelID++ )
                dlevels[levelID] = (level_sf*levelTable[levelID].level1 +
                                    level_sf*levelTable[levelID].level2)/2.;
            else
                for ( unsigned levelID = 0; levelID < nlevels; levelID++ )
                dlevels[levelID] = level_sf*levelTable[levelID].level1;
            }
        }

    double *dlevels1 = NULL;
    double *dlevels2 = NULL;
    if ( lbounds )
        {
        dlevels1 = (double *) Malloc(nlevels*sizeof(double));
        for ( unsigned levelID = 0; levelID < nlevels; levelID++ )
            dlevels1[levelID] = level_sf*levelTable[levelID].level1;
        dlevels2 = (double *) Malloc(nlevels*sizeof(double));
        for ( unsigned levelID = 0; levelID < nlevels; levelID++ )
            dlevels2[levelID] = level_sf*levelTable[levelID].level2;
        }

    const char **cvals = NULL;
    const char *unitptr = cdiUnitNamePtr(vartable[varid].level_unit);
    int zaxisID = varDefZaxis(vlistID, zaxistype, (int)nlevels, dlevels, cvals, 0, lbounds, dlevels1, dlevels2,
                                (int)Vctsize, Vct, NULL, NULL, unitptr, 0, 0, ltype1);

    if ( CDI_cmor_mode && nlevels == 1 && zaxistype != ZAXIS_HYBRID ) zaxisDefScalar(zaxisID);

    if ( ltype1 != ltype2 && ltype2 != -1 ) zaxisDefLtype2(zaxisID, ltype2);

    if ( zaxisInqType(zaxisID) == ZAXIS_REFERENCE )
        {
        if ( numberOfVerticalLevels > 0 ) zaxisDefNlevRef(zaxisID, numberOfVerticalLevels);
        if ( numberOfVerticalGrid > 0 ) zaxisDefNumber(zaxisID, numberOfVerticalGrid);
        if ( !cdiUUIDIsNull(uuidVGrid) ) zaxisDefUUID(zaxisID, uuidVGrid);
        }

    if ( lbounds ) Free(dlevels1);
    if ( lbounds ) Free(dlevels2);
    Free(dlevels);


    int tilesetID = CDI_UNDEFID;
    if ( vartable[varid].tiles ) tilesetID = vlistDefTileSubtype(vlistID, vartable[varid].tiles);


    int varID = stream_new_var(streamptr, gridID, zaxisID, tilesetID);
    varID = vlistDefVarTiles(vlistID, gridID, zaxisID, TIME_VARYING, tilesetID);

    vlistDefVarTsteptype(vlistID, varID, tsteptype);
    vlistDefVarParam(vlistID, varID, param);
    vlistDefVarDatatype(vlistID, varID, prec);
    vlistDefVarTimave(vlistID, varID, timave);
    vlistDefVarCompType(vlistID, varID, comptype);

    varCopyKeys(vlistID, varID);

    if ( vartable[varid].lmissval ) vlistDefVarMissval(vlistID, varID, vartable[varid].missval);
    if ( vartable[varid].name )     vlistDefVarName(vlistID, varID, vartable[varid].name);
    if ( vartable[varid].stdname )  vlistDefVarStdname(vlistID, varID, vartable[varid].stdname);
    if ( vartable[varid].longname ) vlistDefVarLongname(vlistID, varID, vartable[varid].longname);
    if ( vartable[varid].units )    vlistDefVarUnits(vlistID, varID, vartable[varid].units);

    vlist_t *vlistptr = vlist_to_pointer(vlistID);
    for ( int i = 0; i < vartable[varid].opt_grib_nentries; i++ )
        {
        resize_opt_grib_entries(&vlistptr->vars[varID], vlistptr->vars[varID].opt_grib_nentries+1);
        vlistptr->vars[varID].opt_grib_nentries += 1;
        int idx = vlistptr->vars[varID].opt_grib_nentries-1;

        vlistptr->vars[varID].opt_grib_kvpair[idx] = vartable[varid].opt_grib_kvpair[i];
        vlistptr->vars[varID].opt_grib_kvpair[idx].keyword = NULL;
        if ( vartable[varid].opt_grib_kvpair[i].keyword )
            vlistptr->vars[varID].opt_grib_kvpair[idx].keyword =
            strdupx(vartable[varid].opt_grib_kvpair[i].keyword);
        vlistptr->vars[varID].opt_grib_kvpair[i].update = true;
        }


    if ( cdiDefaultTableID != CDI_UNDEFID )
        {
        int pdis, pcat, pnum;
        cdiDecodeParam(param, &pnum, &pcat, &pdis);
        char name[CDI_MAX_NAME]; name[0] = 0;
        char longname[CDI_MAX_NAME]; longname[0] = 0;
        char units[CDI_MAX_NAME]; units[0] = 0;
        tableInqEntry(cdiDefaultTableID, pnum, -1, name, longname, units);
        if ( name[0] )
            {
            if ( tableID != CDI_UNDEFID )
                {
                vlistDefVarName(vlistID, varID, name);
                if ( longname[0] ) vlistDefVarLongname(vlistID, varID, longname);
                if ( units[0] ) vlistDefVarUnits(vlistID, varID, units);
                }
            else
                tableID = cdiDefaultTableID;
            }
        if ( cdiDefaultModelID != CDI_UNDEFID ) modelID = cdiDefaultModelID;
        if ( cdiDefaultInstID  != CDI_UNDEFID )  instID = cdiDefaultInstID;
        }

    if ( instID  != CDI_UNDEFID ) vlistDefVarInstitut(vlistID, varID, instID);
    if ( modelID != CDI_UNDEFID ) vlistDefVarModel(vlistID, varID, modelID);
    if ( tableID != CDI_UNDEFID ) vlistDefVarTable(vlistID, varID, tableID);
    }

for ( size_t index = 0; index < varTableUsed; index++ )
    {
    int varid = varids[index];
    unsigned nlevels = vartable[varid].recordTable[0].nlevels;

    unsigned nsub = vartable[varid].nsubtypes >= 0 ? (unsigned)vartable[varid].nsubtypes : 0U;
    for ( size_t isub = 0; isub < nsub; isub++ )
        {
        sleveltable_t *restrict streamRecordTable = streamptr->vars[index].recordTable + isub;
        leveltable_t *restrict vartableLevelTable = vartable[varid].recordTable[isub].levelTable;
        for ( unsigned levelID = 0; levelID < nlevels; levelID++ )
            {
            streamRecordTable->recordID[levelID] = vartableLevelTable[levelID].recID;
            unsigned lindex;
            for ( lindex = 0; lindex < nlevels; lindex++ )
                if ( levelID == (unsigned)vartableLevelTable[lindex].lindex )
                break;
            if ( lindex == nlevels )
                Error("Internal problem! lindex not found.");
            streamRecordTable->lindex[levelID] = (int)lindex;
            }
        }
    }

Free(varids);

varFree();
}


void varDefVCT(size_t vctsize, double *vctptr)
{
if ( Vct == NULL && vctptr != NULL && vctsize > 0 )
    {
    Vctsize = vctsize;
    Vct = (double *) Malloc(vctsize*sizeof(double));
    memcpy(Vct, vctptr, vctsize*sizeof(double));
    }
}


void varDefZAxisReference(int nhlev, int nvgrid, unsigned char uuid[CDI_UUID_SIZE])
{
numberOfVerticalLevels = nhlev;
numberOfVerticalGrid = nvgrid;
memcpy(uuidVGrid, uuid, CDI_UUID_SIZE);
}


bool zaxisCompare(int zaxisID, int zaxistype, int nlevels, bool lbounds, const double *levels, const char *longname, const char *units, int ltype1)
{
bool differ = true;

bool ltype_is_equal = (ltype1 == zaxisInqLtype(zaxisID));

if ( ltype_is_equal && (zaxistype == zaxisInqType(zaxisID) || zaxistype == ZAXIS_GENERIC) )
    {
    bool zlbounds = (zaxisInqLbounds(zaxisID, NULL) > 0);
    if ( nlevels == zaxisInqSize(zaxisID) && zlbounds == lbounds )
        {
        const double *dlevels = zaxisInqLevelsPtr(zaxisID);
        if ( dlevels && levels )
            {
            int levelID;
            for ( levelID = 0; levelID < nlevels; levelID++ )
                {
                if ( fabs(dlevels[levelID] - levels[levelID]) > 1.e-9 )
                    break;
                }
            if ( levelID == nlevels ) differ = false;
            }

        if ( ! differ )
            {
            if ( longname && longname[0] )
                {
                char zlongname[CDI_MAX_NAME]; zlongname[0] = 0;
                zaxisInqLongname(zaxisID, zlongname);
                if ( zlongname[0] && (strcmp(longname, zlongname) != 0) ) differ = true;
                }
            if ( units && units[0] )
                {
                char zunits[CDI_MAX_NAME]; zunits[0] = 0;
                zaxisInqUnits(zaxisID, zunits);
                if ( zunits[0] && (strcmp(units, zunits) != 0) ) differ = true;
                }
            }
        }
    }

return differ;
}

struct varDefZAxisSearchState
{
int resIDValue;
int zaxistype;
int nlevels;
bool lbounds;
const double *levels;
const char *longname;
const char *units;
int ltype;
};

static enum cdiApplyRet
varDefZAxisSearch(int id, void *res, void *data)
{
struct varDefZAxisSearchState *state = (struct varDefZAxisSearchState *)data;
(void)res;
if ( zaxisCompare(id, state->zaxistype, state->nlevels, state->lbounds,
                    state->levels, state->longname, state->units, state->ltype)
    == false)
    {
    state->resIDValue = id;
    return CDI_APPLY_STOP;
    }
else
    return CDI_APPLY_GO_ON;
}


int varDefZaxis(int vlistID, int zaxistype, int nlevels, const double *levels, const char **cvals, size_t clength, bool lbounds,
                const double *levels1, const double *levels2, int vctsize, const double *vct, char *name,
                const char *longname, const char *units, int prec, int mode, int ltype1)
{

int zaxisID = CDI_UNDEFID;
bool zaxisdefined = false;
bool zaxisglobdefined = false;
vlist_t *vlistptr = vlist_to_pointer(vlistID);
int nzaxis = vlistptr->nzaxis;

if ( mode == 0 )
    for ( int index = 0; index < nzaxis; index++ )
    {
        zaxisID = vlistptr->zaxisIDs[index];

        if ( !zaxisCompare(zaxisID, zaxistype, nlevels, lbounds, levels, longname, units, ltype1) )
        {
            zaxisdefined = true;
            break;
        }
    }

if ( ! zaxisdefined )
    {
    struct varDefZAxisSearchState query;
    query.zaxistype = zaxistype;
    query.nlevels = nlevels;
    query.levels = levels;
    query.lbounds = lbounds;
    query.longname = longname;
    query.units = units;
    query.ltype = ltype1;

    if ((zaxisglobdefined
        = (cdiResHFilterApply(getZaxisOps(), varDefZAxisSearch, &query)
            == CDI_APPLY_STOP)))
        zaxisID = query.resIDValue;

    if ( mode == 1 && zaxisglobdefined)
        for (int index = 0; index < nzaxis; index++ )
        if ( vlistptr->zaxisIDs[index] == zaxisID )
            {
            zaxisglobdefined = false;
            break;
            }
    }

if ( ! zaxisdefined )
    {
    if ( ! zaxisglobdefined )
        {
        zaxisID = zaxisCreate(zaxistype, nlevels);
        if ( levels ) zaxisDefLevels(zaxisID, levels);
        if ( lbounds )
            {
            zaxisDefLbounds(zaxisID, levels1);
            zaxisDefUbounds(zaxisID, levels2);
            }

        if ( cvals != NULL && nlevels != 0 && clength != 0 ) zaxisDefCvals(zaxisID, cvals, (int)clength);

        if ( (zaxistype == ZAXIS_HYBRID || zaxistype == ZAXIS_HYBRID_HALF) && vctsize > 0 )
            zaxisDefVct(zaxisID, vctsize, vct);

        if ( name && name[0] ) zaxisDefName(zaxisID, name);
        if ( longname && longname[0] ) zaxisDefLongname(zaxisID, longname);

        if ( units && units[0] ) zaxisDefUnits(zaxisID, units);

        zaxisDefDatatype(zaxisID, prec);
        zaxisDefLtype(zaxisID, ltype1);
        }

    vlistptr->zaxisIDs[nzaxis] = zaxisID;
    vlistptr->nzaxis++;
    }

return zaxisID;
}


void varDefMissval(int varID, double missval)
{
vartable[varID].lmissval = true;
vartable[varID].missval = missval;
}


void varDefCompType(int varID, int comptype)
{
if ( vartable[varID].comptype == CDI_COMPRESS_NONE )
    vartable[varID].comptype = comptype;
}


void varDefCompLevel(int varID, int complevel)
{
vartable[varID].complevel = complevel;
}


int varInqInst(int varID)
{
return vartable[varID].instID;
}


void varDefInst(int varID, int instID)
{
vartable[varID].instID = instID;
}


int varInqModel(int varID)
{
return vartable[varID].modelID;
}


void varDefModel(int varID, int modelID)
{
vartable[varID].modelID = modelID;
}


int varInqTable(int varID)
{
return vartable[varID].tableID;
}


void varDefTable(int varID, int tableID)
{
vartable[varID].tableID = tableID;
}


void varDefKeyInt(int varID, int key, int value)
{
cdi_keys_t *keysp = &(vartable[varID].keys);
cdiDefVarKeyInt(keysp, key, value);
}


void varDefKeyBytes(int varID, int key, const unsigned char *bytes, int length)
{
cdi_keys_t *keysp = &(vartable[varID].keys);
cdiDefVarKeyBytes(keysp, key, bytes, length);
}


#ifdef HAVE_LIBGRIB_API

static
void resize_vartable_opt_grib_entries(vartable_t *var, int nentries)
{
if (var->opt_grib_kvpair_size >= nentries)
    {
    return;
    }
else
    {
    if ( CDI_Debug )
        Message("resize data structure, %d -> %d", var->opt_grib_kvpair_size, nentries);

    int i, new_size;
    new_size = (2*var->opt_grib_kvpair_size) > nentries ? (2*var->opt_grib_kvpair_size) : nentries;
    if (CDI_Debug)
        Message("resize vartable opt_grib_entries array to size %d", new_size);
    opt_key_val_pair_t *tmp = (opt_key_val_pair_t *) Malloc((size_t)new_size * sizeof (opt_key_val_pair_t));
    for (i=0; i<var->opt_grib_kvpair_size; i++) {
        tmp[i] = var->opt_grib_kvpair[i];
    }
    for (i=var->opt_grib_kvpair_size; i<new_size; i++) {
        tmp[i].int_val =     0;
        tmp[i].dbl_val =     0;
        tmp[i].update  = false;
        tmp[i].keyword =  NULL;
    }
    var->opt_grib_kvpair_size = new_size;
    Free(var->opt_grib_kvpair);
    var->opt_grib_kvpair = tmp;
    }
}
#endif

#ifdef HAVE_LIBGRIB_API
void varDefOptGribInt(int varID, int tile_index, long lval, const char *keyword)
{
int idx = -1;
for (int i=0; i<vartable[varID].opt_grib_nentries; i++)
    {
    if ( (strcmp(keyword, vartable[varID].opt_grib_kvpair[i].keyword) == 0 ) &&
        (vartable[varID].opt_grib_kvpair[i].data_type == t_int)             &&
        (vartable[varID].opt_grib_kvpair[i].subtype_index == tile_index) )
        idx = i;
    }

if (idx == -1)
    {
    resize_vartable_opt_grib_entries(&vartable[varID], vartable[varID].opt_grib_nentries+1);
    vartable[varID].opt_grib_nentries += 1;
    idx = vartable[varID].opt_grib_nentries -1;
    }
else
    {
    if (vartable[varID].opt_grib_kvpair[idx].keyword)
        Free(vartable[varID].opt_grib_kvpair[idx].keyword);
    }
vartable[varID].opt_grib_kvpair[idx].data_type     = t_int;
vartable[varID].opt_grib_kvpair[idx].int_val       = (int) lval;
vartable[varID].opt_grib_kvpair[idx].keyword       = strdupx(keyword);
vartable[varID].opt_grib_kvpair[idx].subtype_index = tile_index;
}
#endif


#ifdef HAVE_LIBGRIB_API
void varDefOptGribDbl(int varID, int tile_index, double dval, const char *keyword)
{
int idx = -1;
for (int i=0; i<vartable[varID].opt_grib_nentries; i++)
    {
    if ( (strcmp(keyword, vartable[varID].opt_grib_kvpair[i].keyword) == 0 ) &&
        (vartable[varID].opt_grib_kvpair[i].data_type == t_double)          &&
        (vartable[varID].opt_grib_kvpair[i].subtype_index == tile_index) )
        idx = i;
    }

if (idx == -1)
    {
    resize_vartable_opt_grib_entries(&vartable[varID], vartable[varID].opt_grib_nentries+1);
    vartable[varID].opt_grib_nentries += 1;
    idx = vartable[varID].opt_grib_nentries -1;
    }
else
    {
    if (vartable[varID].opt_grib_kvpair[idx].keyword)
        Free(vartable[varID].opt_grib_kvpair[idx].keyword);
    }
vartable[varID].opt_grib_kvpair[idx].data_type     = t_double;
vartable[varID].opt_grib_kvpair[idx].dbl_val       = dval;
vartable[varID].opt_grib_kvpair[idx].keyword       = strdupx(keyword);
vartable[varID].opt_grib_kvpair[idx].subtype_index = tile_index;
}
#endif


#ifdef HAVE_LIBGRIB_API
int varOptGribNentries(int varID)
{
int nentries = vartable[varID].opt_grib_nentries;
return nentries;
}
#endif


#ifdef  HAVE_CONFIG_H
#endif



#ifdef  HAVE_LIBGRIB_API

int    cdiNAdditionalGRIBKeys = 0;
char*  cdiAdditionalGRIBKeys[MAX_OPT_GRIB_ENTRIES];
#endif

static int VLIST_Debug = 0;

static void vlist_initialize(void);

#ifdef  HAVE_LIBPTHREAD
#include <pthread.h>

static pthread_once_t  _vlist_init_thread = PTHREAD_ONCE_INIT;

#define  VLIST_INIT()        \
pthread_once(&_vlist_init_thread, vlist_initialize)

#else

static bool vlistIsInitialized = false;

#define  VLIST_INIT()               \
if ( !vlistIsInitialized ) vlist_initialize()
#endif


static int
vlist_compare(vlist_t *a, vlist_t *b)
{
int diff = (a->nvars != b->nvars) | (a->ngrids != b->ngrids)
    | (a->nzaxis != b->nzaxis) | (a->instID != b->instID)
    | (a->modelID != b->modelID) | (a->tableID != b->tableID)
    | (a->ntsteps != b->ntsteps) | (a->atts.nelems != b->atts.nelems);
int nvars = a->nvars;
for (int varID = 0; varID < nvars; ++varID)
    diff |= vlistVarCompare(a, varID, b, varID);
size_t natts = a->atts.nelems;
for (size_t attID = 0; attID < natts; ++attID)
    diff |= cdi_att_compare(a, CDI_GLOBAL, b, CDI_GLOBAL, (int)attID);
return diff;
}

static void
vlistPrintKernel(vlist_t *vlistptr, FILE * fp );
static void
vlist_delete(vlist_t *vlistptr);

static int  vlistGetSizeP ( void * vlistptr, void *context);
static void vlistPackP    ( void * vlistptr, void * buff, int size,
                            int *position, void *context);
static int  vlistTxCode   ( void );

#if !defined(__cplusplus)
const
#endif
resOps vlistOps = {
(valCompareFunc)vlist_compare,
(valDestroyFunc)vlist_delete,
(valPrintFunc)vlistPrintKernel,
vlistGetSizeP,
vlistPackP,
vlistTxCode
};


vlist_t *vlist_to_pointer(int vlistID)
{
VLIST_INIT();
return (vlist_t*) reshGetVal(vlistID, &vlistOps);
}

static
void vlist_init_entry(vlist_t *vlistptr)
{
vlistptr->immutable      = 0;
vlistptr->internal       = 0;
vlistptr->self           = CDI_UNDEFID;
vlistptr->nvars          = 0;
vlistptr->vars           = NULL;
vlistptr->ngrids         = 0;
vlistptr->nzaxis         = 0;
vlistptr->taxisID        = CDI_UNDEFID;
vlistptr->instID         = cdiDefaultInstID;
vlistptr->modelID        = cdiDefaultModelID;
vlistptr->tableID        = cdiDefaultTableID;
vlistptr->varsAllocated  = 0;
vlistptr->ntsteps        = CDI_UNDEFID;
vlistptr->keys.nalloc    = MAX_KEYS;
vlistptr->keys.nelems    = 0;
vlistptr->atts.nalloc    = MAX_ATTRIBUTES;
vlistptr->atts.nelems    = 0;
vlistptr->nsubtypes      = 0;
for ( int i = 0; i < MAX_SUBTYPES_PS; i++ )
    vlistptr->subtypeIDs[i] = CDI_UNDEFID;
}

static
vlist_t *vlist_new_entry(cdiResH resH)
{
vlist_t *vlistptr = (vlist_t*) Malloc(sizeof(vlist_t));
vlist_init_entry(vlistptr);
if (resH == CDI_UNDEFID)
    vlistptr->self = reshPut(vlistptr, &vlistOps);
else
    {
    vlistptr->self = resH;
    reshReplace(resH, vlistptr, &vlistOps);
    }
return vlistptr;
}

static
void vlist_delete_entry(vlist_t *vlistptr)
{
int idx = vlistptr->self;

reshRemove(idx, &vlistOps );

Free(vlistptr);

if ( VLIST_Debug )
    Message("Removed idx %d from vlist list", idx);
}

static
void vlist_initialize(void)
{
char *env = getenv("VLIST_DEBUG");
if ( env ) VLIST_Debug = atoi(env);
#ifndef HAVE_LIBPTHREAD
vlistIsInitialized = true;
#endif
}

static
void vlist_copy(vlist_t *vlistptr2, vlist_t *vlistptr1)
{
int vlistID2 = vlistptr2->self;
int vlist2internal = vlistptr2->internal;
memcpy(vlistptr2, vlistptr1, sizeof(vlist_t));
vlistptr2->internal = vlist2internal;
vlistptr2->immutable = 0;
vlistptr2->keys.nelems = 0;
vlistptr2->atts.nelems = 0;
vlistptr2->self = vlistID2;
}

void cdiVlistMakeInternal(int vlistID)
{
vlist_to_pointer(vlistID)->internal = 1;
}

void cdiVlistMakeImmutable(int vlistID)
{
vlist_to_pointer(vlistID)->immutable = 1;
}


int vlistCreate(void)
{
cdiInitialize();

VLIST_INIT();

vlist_t *vlistptr = vlist_new_entry(CDI_UNDEFID);
if ( CDI_Debug ) Message("create vlistID = %d", vlistptr->self);
return vlistptr->self;
}

static
void vlist_delete(vlist_t *vlistptr)
{
int vlistID = vlistptr->self;
if ( CDI_Debug ) Message("call to vlist_delete, vlistID = %d", vlistID);

cdiDeleteKeys(vlistID, CDI_GLOBAL);
cdiDeleteAtts(vlistID, CDI_GLOBAL);

int nvars = vlistptr->nvars;
var_t *vars = vlistptr->vars;

for ( int varID = 0; varID < nvars; varID++ )
    {
    if ( vars[varID].levinfo )  Free(vars[varID].levinfo);
    if ( vars[varID].name )     Free(vars[varID].name);
    if ( vars[varID].longname ) Free(vars[varID].longname);
    if ( vars[varID].stdname )  Free(vars[varID].stdname);
    if ( vars[varID].units )    Free(vars[varID].units);

    if ( vlistptr->vars[varID].opt_grib_kvpair )
        {
        for ( int i = 0; i<vlistptr->vars[varID].opt_grib_nentries; i++ )
            {
            if ( vlistptr->vars[varID].opt_grib_kvpair[i].keyword )
                Free(vlistptr->vars[varID].opt_grib_kvpair[i].keyword);
            }
        Free(vlistptr->vars[varID].opt_grib_kvpair);
        }
    vlistptr->vars[varID].opt_grib_nentries    = 0;
    vlistptr->vars[varID].opt_grib_kvpair_size = 0;
    vlistptr->vars[varID].opt_grib_kvpair      = NULL;

    cdiDeleteKeys(vlistID, varID);
    cdiDeleteAtts(vlistID, varID);
    }

if ( vars ) Free(vars);

vlist_delete_entry(vlistptr);
}



void vlistDestroy(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( vlistptr->internal )
    Warning("Attempt to destroy an internal vlist object by the user (vlistID=%d).", vlistID);
else
    vlist_delete(vlistptr);
}


void cdiVlistDestroy_(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if(!vlistptr->internal)
    Warning("Destroying a vlist object that is owned by the user.\n"
            "This is most likely because of a missing vlistDestroy() in the application code.\n"
            "If that's not the case, and you are absolutely certain about it, please report the bug.");

vlist_delete(vlistptr);
}

static
void var_copy_entries(var_t *var2, var_t *var1)
{
if ( var1->name )     var2->name     = strdupx(var1->name);
if ( var1->longname ) var2->longname = strdupx(var1->longname);
if ( var1->stdname )  var2->stdname  = strdupx(var1->stdname);
if ( var1->units )    var2->units    = strdupx(var1->units);

var2->opt_grib_kvpair_size = 0;
var2->opt_grib_kvpair      = NULL;
var2->opt_grib_nentries    = 0;

resize_opt_grib_entries(var2, var1->opt_grib_nentries);
var2->opt_grib_nentries = var1->opt_grib_nentries;
if ((var2->opt_grib_nentries > 0) && CDI_Debug )
    Message("copy %d optional GRIB keywords", var2->opt_grib_nentries);

for (int i=0; i<var1->opt_grib_nentries; i++) {
    if ( CDI_Debug )  Message("copy entry \"%s\" ...", var1->opt_grib_kvpair[i].keyword);
    var2->opt_grib_kvpair[i].keyword = NULL;
    if ( var1->opt_grib_kvpair[i].keyword != NULL ) {
    var2->opt_grib_kvpair[i]         = var1->opt_grib_kvpair[i];
    var2->opt_grib_kvpair[i].keyword = strdupx(var1->opt_grib_kvpair[i].keyword);
    var2->opt_grib_kvpair[i].update  = true;
    if ( CDI_Debug )  Message("done.");
    }
    else {
    if ( CDI_Debug )  Message("not done.");
    }
}
}


void vlistCopy(int vlistID2, int vlistID1)
{
vlist_t *vlistptr1 = vlist_to_pointer(vlistID1);
vlist_t *vlistptr2 = vlist_to_pointer(vlistID2);
if ( CDI_Debug ) Message("call to vlistCopy, vlistIDs %d -> %d", vlistID1, vlistID2);

var_t *vars1 = vlistptr1->vars;
var_t *vars2 = vlistptr2->vars;
vlist_copy(vlistptr2, vlistptr1);

vlistptr2->keys.nelems = 0;
cdiCopyKeys(vlistID1, CDI_GLOBAL, vlistID2, CDI_GLOBAL);
vlistptr2->atts.nelems = 0;
cdiCopyAtts(vlistID1, CDI_GLOBAL, vlistID2, CDI_GLOBAL);

if ( vars1 )
    {
    int nvars = vlistptr1->nvars;


    size_t n = (size_t)vlistptr2->varsAllocated;
    vars2 = (var_t *) Realloc(vars2, n*sizeof(var_t));
    memcpy(vars2, vars1, n*sizeof(var_t));
    vlistptr2->vars = vars2;

    for ( int varID = 0; varID < nvars; varID++ )
        {
        var_copy_entries(&vars2[varID], &vars1[varID]);
        vlistptr2->vars[varID].keys.nelems = 0;
        cdiCopyKeys(vlistID1, varID, vlistID2, varID);

        vlistptr2->vars[varID].atts.nelems = 0;
        cdiCopyAtts(vlistID1, varID, vlistID2, varID);

        if ( vars1[varID].levinfo )
            {
            n = (size_t)zaxisInqSize(vars1[varID].zaxisID);
            vars2[varID].levinfo = (levinfo_t *) Malloc(n*sizeof(levinfo_t));
            memcpy(vars2[varID].levinfo, vars1[varID].levinfo, n*sizeof(levinfo_t));
            }
        }
    }
}


int vlistDuplicate(int vlistID)
{
if ( CDI_Debug ) Message("call to vlistDuplicate");

int vlistIDnew = vlistCreate();
vlistCopy(vlistIDnew, vlistID);
return vlistIDnew;
}


void vlistClearFlag(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

for ( int varID = 0; varID < vlistptr->nvars; varID++ )
    {
    vlistptr->vars[varID].flag = false;
    if ( vlistptr->vars[varID].levinfo )
        {
        int nlevs = zaxisInqSize(vlistptr->vars[varID].zaxisID);
        for ( int levID = 0; levID < nlevs; levID++ )
            vlistptr->vars[varID].levinfo[levID].flag = false;
        }
    }
}


struct vgzSearchState
{
int resIDValue;
int zaxistype;
int nlevels;
bool lbounds;
const double *levels;
};

static enum cdiApplyRet
vgzZAxisSearch(int id, void *res, void *data)
{
struct vgzSearchState *state = (struct vgzSearchState *)data;
(void)res;
if (zaxisCompare(id, state->zaxistype, state->nlevels, state->lbounds,
                state->levels, NULL, NULL, 0)
    == false)
    {
    state->resIDValue = id;
    return CDI_APPLY_STOP;
    }
else
    return CDI_APPLY_GO_ON;
}

static
int vlist_generate_zaxis(int vlistID, int zaxistype, int nlevels, const double *levels,
                        const double *lbounds, const double *ubounds, int vctsize, const double *vct,
                        const char **cvals, size_t clen)
{
int zaxisID = CDI_UNDEFID;
bool zaxisdefined = false;
bool zaxisglobdefined = false;
bool has_bounds = false;
vlist_t *vlistptr = vlist_to_pointer(vlistID);
int nzaxis = vlistptr->nzaxis;

if ( lbounds && ubounds ) has_bounds = true;

for ( int index = 0; index < nzaxis; ++index )
    {
    zaxisID = vlistptr->zaxisIDs[index];

    if ( zaxisCompare(zaxisID, zaxistype, nlevels, has_bounds, levels, NULL, NULL, 0) == false )
        {
        zaxisdefined = true;
        break;
        }
    }

if ( ! zaxisdefined )
    {
    struct vgzSearchState query;
    query.zaxistype = zaxistype;
    query.nlevels = nlevels;
    query.levels = levels;
    query.lbounds = has_bounds;

    if ((zaxisglobdefined
        = (cdiResHFilterApply(getZaxisOps(), vgzZAxisSearch, &query)
            == CDI_APPLY_STOP)))
        zaxisID = query.resIDValue;
    }

if ( ! zaxisdefined )
    {
    if ( ! zaxisglobdefined )
        {
        zaxisID = zaxisCreate(zaxistype, nlevels);
        zaxisDefLevels(zaxisID, levels);

        if ( zaxistype == ZAXIS_CHAR )
            zaxisDefCvals(zaxisID, cvals, (int)clen);

        if ( has_bounds )
            {
            zaxisDefLbounds(zaxisID, lbounds);
            zaxisDefUbounds(zaxisID, ubounds);
            }

        if ( zaxistype == ZAXIS_HYBRID && vctsize > 0 )
            zaxisDefVct(zaxisID, vctsize, vct);
        }

    nzaxis = vlistptr->nzaxis;
    vlistptr->zaxisIDs[nzaxis] = zaxisID;
    vlistptr->nzaxis++;
    }

return zaxisID;
}


void vlistCopyFlag(int vlistID2, int vlistID1)
{
vlist_t *vlistptr1 = vlist_to_pointer(vlistID1);
vlist_t *vlistptr2 = vlist_to_pointer(vlistID2);
var_t *vars1 = vlistptr1->vars;
var_t *vars2 = vlistptr2->vars;

vlist_copy(vlistptr2, vlistptr1);

vlistptr2->keys.nelems = 0;
cdiCopyKeys(vlistID1, CDI_GLOBAL, vlistID2, CDI_GLOBAL);
vlistptr2->atts.nelems = 0;
cdiCopyAtts(vlistID1, CDI_GLOBAL, vlistID2, CDI_GLOBAL);

if ( vlistptr1->vars )
    {
    vlistptr2->ngrids = 0;
    vlistptr2->nzaxis = 0;

    int nvars = vlistptr1->nvars;
    int nvars2 = 0;
    for ( int varID = 0; varID < nvars; varID++ )
        nvars2 += vars1[varID].flag;

    vlistptr2->nvars = nvars2;
    vlistptr2->varsAllocated = nvars2;
    vars2 = (nvars2 > 0) ? (var_t *) Malloc((size_t)nvars2*sizeof(var_t)) : NULL;

    vlistptr2->vars = vars2;

    int varID2 = 0;
    for ( int varID = 0; varID < nvars; varID++ )
        if ( vars1[varID].flag )
        {
            vlistptr2->vars[varID2].flag = false;
            int zaxisID   = vlistptr1->vars[varID].zaxisID;
            int gridID    = vlistptr1->vars[varID].gridID;
            int subtypeID = vlistptr1->vars[varID].subtypeID;

            memcpy(&vars2[varID2], &vars1[varID], sizeof(var_t));

            vars1[varID].fvarID = varID2;
            vars2[varID2].fvarID = varID;

            vars2[varID2].mvarID = varID2;

            var_copy_entries(&vars2[varID2], &vars1[varID]);
            vlistptr2->vars[varID2].keys.nelems = 0;
            cdiCopyKeys(vlistID1, varID, vlistID2, varID2);

            vlistptr2->vars[varID2].atts.nelems = 0;
            cdiCopyAtts(vlistID1, varID, vlistID2, varID2);

            int nlevs  = zaxisInqSize(vars1[varID].zaxisID);
            int nlevs2 = 0;
            if ( vars1[varID].levinfo )
            for ( int levID = 0; levID < nlevs; levID++ )
                nlevs2 += vars1[varID].levinfo[levID].flag;

            vars2[varID2].levinfo = (levinfo_t *) Malloc((size_t)nlevs2 * sizeof(levinfo_t));

            if ( nlevs != nlevs2 )
            {
                int nvct = 0;
                double *levels = NULL;
                char **cvals1 = NULL, **cvals2 = NULL;
                size_t clen2 = 0;
                double *lbounds = NULL, *ubounds = NULL;
                const double *vct = NULL;
                char ctemp[CDI_MAX_NAME];

                if ( !vars1[varID].levinfo ) cdiVlistCreateVarLevInfo(vlistptr1, varID);

                zaxisID = vars1[varID].zaxisID;
                int zaxisType = zaxisInqType(zaxisID);

                int levID2 = 0;
                for ( int levID = 0; levID < nlevs; ++levID )
                if ( vars1[varID].levinfo[levID].flag )
                    {
                    vars1[varID].levinfo[levID].flevelID = levID2;
                    vars1[varID].levinfo[levID].mlevelID = levID2;
                    }


                if ( zaxisInqLevels(zaxisID, NULL) )
                {
                    levels = (double *) Malloc((size_t)nlevs2 * sizeof (double));

                    levID2 = 0;
                    for ( int levID = 0; levID < nlevs; ++levID )
                    if ( vars1[varID].levinfo[levID].flag )
                        levels[levID2++] = zaxisInqLevel(zaxisID, levID);
                }

                if ( zaxisType == ZAXIS_HYBRID )
                {
                    nvct = zaxisInqVctSize(zaxisID);
                    vct  = zaxisInqVctPtr(zaxisID);
                }

                if ( zaxisType == ZAXIS_CHAR )
                {
                    cvals1 = zaxisInqCValsPtr(zaxisID);
                    size_t clen1 = (size_t)zaxisInqCLen(zaxisID);
                    for ( int levID = 0; levID < nlevs; ++levID )
                    if ( vars1[varID].levinfo[levID].flag )
                        {
                            size_t testlen = clen1;
                            while ( cvals1[levID][testlen] == ' ' )
                            testlen--;
                            if ( clen2 < testlen )
                            clen2 = testlen;
                        }
                    cvals2 = (char **) Malloc((size_t)nlevs2 * sizeof (char *));
                    levID2 = 0;

                    for ( int levID = 0; levID < nlevs; ++levID )
                    if ( vars1[varID].levinfo[levID].flag )
                        {
                        cvals2[levID2] = (char*) Malloc((size_t)(clen2) * sizeof(char));
                        memcpy(cvals2[levID2], cvals1[levID], clen2*sizeof(char));
                        levID2++;
                        }
                }

                if ( zaxisInqLbounds(zaxisID, NULL) && zaxisInqUbounds(zaxisID, NULL) )
                {
                    lbounds = (double *) Malloc(2 * (size_t)nlevs2 * sizeof (double));
                    ubounds = lbounds + nlevs2;

                    double *lbounds1 = (double *) Malloc(2 * (size_t)nlevs * sizeof (double)),
                        *ubounds1 = lbounds1 + nlevs;

                    zaxisInqLbounds(zaxisID, lbounds1);
                    zaxisInqUbounds(zaxisID, ubounds1);

                    levID2 = 0;
                    for ( int levID = 0; levID < nlevs; ++levID )
                    if ( vars1[varID].levinfo[levID].flag )
                        {
                        lbounds[levID2] = lbounds1[levID];
                        ubounds[levID2] = ubounds1[levID];
                        levID2++;
                        }

                    Free(lbounds1);
                }

                int zaxisID2 = vlist_generate_zaxis(vlistID2, zaxisType, nlevs2, levels, lbounds, ubounds, nvct, vct, (const char **)cvals2, clen2);
                if ( levels )  Free(levels);
                if ( lbounds ) Free(lbounds);
                if ( cvals2 )
                {
                    for ( int levID = 0; levID < nlevs2; ++levID )
                    Free(cvals2[levID]);
                    Free(cvals2);
                }

                zaxisInqName(zaxisID, ctemp);
                zaxisDefName(zaxisID2, ctemp);
                zaxisInqLongname(zaxisID, ctemp);
                zaxisDefLongname(zaxisID2, ctemp);
                zaxisInqUnits(zaxisID, ctemp);
                zaxisDefUnits(zaxisID2, ctemp);
                zaxisDefDatatype(zaxisID2, zaxisInqDatatype(zaxisID));

                if ( zaxisType == ZAXIS_CHAR )
                {
                    char dimname[CDI_MAX_NAME+3]; dimname[0] = 0;
                    cdiZaxisInqKeyStr(zaxisID, CDI_KEY_DIMNAME, CDI_MAX_NAME, dimname);
                    if ( dimname[0] == 0 ) { memcpy(dimname, "area_type", 10); dimname[10] = 0; }
                    cdiZaxisDefKeyStr(zaxisID2, CDI_KEY_DIMNAME, CDI_MAX_NAME, dimname);
                }

                if ( zaxisType == ZAXIS_GENERIC ) zaxisDefLtype(zaxisID2, zaxisInqLtype(zaxisID));

                zaxisID = zaxisID2;
                vars2[varID2].zaxisID = zaxisID2;
            }

            for ( int levID = 0; levID < nlevs2; levID++ )
            {
                vars2[varID2].levinfo[levID].flag  = false;
                vars2[varID2].levinfo[levID].index = -1;
            }

            int levID2 = 0;
            for ( int levID = 0; levID < nlevs; levID++ )
            if ( vars1[varID].levinfo[levID].flag )
                {
                vars2[varID2].levinfo[levID2].flevelID = levID;
                vars2[varID2].levinfo[levID2].mlevelID = levID;
                levID2++;
                }

            vlistAdd2GridIDs(vlistptr2, gridID);
            vlistAdd2ZaxisIDs(vlistptr2, zaxisID);
            vlistAdd2SubtypeIDs(vlistptr2, subtypeID);

            varID2++;
        }
    }
}


void vlistCat(int vlistID2, int vlistID1)
{
vlist_t *vlistptr1 = vlist_to_pointer(vlistID1);
vlist_t *vlistptr2 = vlist_to_pointer(vlistID2);
var_t *vars1 = vlistptr1->vars;
var_t *vars2 = vlistptr2->vars;
int nvars1 = vlistptr1->nvars;
int nvars2 = vlistptr2->nvars;
int nvars = nvars1 + nvars2;
vlistptr2->nvars = nvars;

if ( nvars > vlistptr2->varsAllocated )
    {
    vlistptr2->varsAllocated = nvars;
    vars2 = (var_t *) Realloc(vars2, (size_t)nvars*sizeof(var_t));
    vlistptr2->vars = vars2;
    }
memcpy(vars2+nvars2, vars1, (size_t)nvars1 * sizeof(var_t));

for ( int varID = 0; varID < nvars1; varID++ )
    {
    int varID2 = varID + nvars2;
    vars1[varID].fvarID = varID2;
    vars2[varID2].fvarID = varID;

    vars1[varID].mvarID = varID2;
    vars2[varID2].mvarID = varID;

    if ( vars1[varID].param < 0 )
        {
        int pnum, pcat, pdis;
        cdiDecodeParam(vars1[varID].param, &pnum, &pcat, &pdis);
        pnum = -(varID2+1);
        vars2[varID2].param = cdiEncodeParam(pnum, pcat, pdis);
        }

    var_copy_entries(&vars2[varID2], &vars1[varID]);
    vars2[varID2].keys.nelems = 0;
    cdiCopyKeys(vlistID1, varID, vlistID2, varID2);

    if ( vars1[varID].levinfo )
        {
        size_t nlevs = (size_t)zaxisInqSize(vars1[varID].zaxisID);
        vars2[varID2].levinfo = (levinfo_t *) Malloc(nlevs * sizeof(levinfo_t));
        memcpy(vars2[varID2].levinfo, vars1[varID].levinfo,
                nlevs * sizeof(levinfo_t));
        }

    vars2[varID2].atts.nelems = 0;
    cdiCopyAtts(vlistID1, varID, vlistID2, varID2);

    vlistAdd2GridIDs(vlistptr2, vars1[varID].gridID);
    vlistAdd2ZaxisIDs(vlistptr2, vars1[varID].zaxisID);
    vlistAdd2SubtypeIDs(vlistptr2, vars1[varID].subtypeID);
    }
}


void vlistMerge(int vlistID2, int vlistID1)
{
int varID = 0;
vlist_t *vlistptr1 = vlist_to_pointer(vlistID1);
vlist_t *vlistptr2 = vlist_to_pointer(vlistID2);
var_t *vars1 = vlistptr1->vars;
var_t *vars2 = vlistptr2->vars;
int nvars1 = vlistptr1->nvars;
int nvars2 = vlistptr2->nvars;

if ( nvars1 == nvars2 )
    {
    for ( varID = 0; varID < nvars2; varID++ )
        {
        size_t ngp1 = gridInqSize(vars1[varID].gridID);
        size_t ngp2 = gridInqSize(vars2[varID].gridID);
        if ( ngp1 != ngp2 ) break;

        if ( vars1[varID].name && vars2[varID].name )
            {
            if ( strcmp(vars1[varID].name, vars2[varID].name) != 0 ) break;
            }
        else
            {
            if ( vars1[varID].param != vars2[varID].param ) break;
            }
        }
    }

if ( varID == nvars2 )
    {
    for ( varID = 0; varID < nvars2; varID++ )
        {
        vars1[varID].fvarID = varID;
        vars2[varID].fvarID = varID;

        vars1[varID].mvarID = varID;
        vars2[varID].mvarID = varID;

        int nlevs1 = zaxisInqSize(vars1[varID].zaxisID);
        int nlevs2 = zaxisInqSize(vars2[varID].zaxisID);

        int nlevs = nlevs1 + nlevs2;


        if ( vars1[varID].levinfo )
            {
            vars2[varID].levinfo = (levinfo_t*) Realloc(vars2[varID].levinfo,
                                    (size_t)nlevs * sizeof(levinfo_t));

            memcpy(vars2[varID].levinfo+nlevs2, vars1[varID].levinfo,
                    (size_t)nlevs1 * sizeof(levinfo_t));
            }
        else
            cdiVlistCreateVarLevInfo(vlistptr1, varID);

        for ( int levID = 0; levID < nlevs1; levID++ )
            vars1[varID].levinfo[levID].mlevelID = nlevs2 + levID;
        }

    bool *lvar = (bool *) Calloc((size_t)nvars2, sizeof(bool));

    for ( varID = 0; varID < nvars2; varID++ )
        {
        if ( lvar[varID] == true ) continue;

        int zaxisID1 = vars1[varID].zaxisID;
        int zaxisID2 = vars2[varID].zaxisID;

        int nlevs1 = zaxisInqSize(zaxisID1);
        int nlevs2 = zaxisInqSize(zaxisID2);

        int nlevs = nlevs1 + nlevs2;

        int zaxisID = zaxisDuplicate(zaxisID2);
        zaxisResize(zaxisID, nlevs);

        if ( zaxisInqLevels(zaxisID1, NULL) )
            {
            double *levels = (double *) Malloc((size_t)nlevs1 * sizeof(double));

            zaxisInqLevels(zaxisID1, levels);

            for ( int levID = 0; levID < nlevs1; levID++ )
                zaxisDefLevel(zaxisID, nlevs2+levID, levels[levID]);

            Free(levels);
            }

        for ( int index = 0; index < vlistptr2->nzaxis; index++ )
            if ( vlistptr2->zaxisIDs[index] == zaxisID2 )
            vlistptr2->zaxisIDs[index] = zaxisID;

        for ( int varID2 = 0; varID2 < nvars2; varID2++ )
            if ( lvar[varID2] == false && vars2[varID2].zaxisID == zaxisID2 )
            {
                vars2[varID2].zaxisID = zaxisID;
                lvar[varID2] = true;
            }
        }

    Free(lvar);
    }
else
    {
    vlistCat(vlistID2, vlistID1);
    }
}


int vlistNvars(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
return vlistptr->nvars;
}


int vlistNrecs(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

int nrecs = 0;
for ( int varID = 0; varID < vlistptr->nvars; varID++ )
    nrecs +=  zaxisInqSize(vlistptr->vars[varID].zaxisID);

return nrecs;
}


int vlistNumber(int vlistID)
{
int number, number2;
vlist_t *vlistptr = vlist_to_pointer(vlistID);

int datatype = vlistptr->vars[0].datatype;
if (  datatype== CDI_DATATYPE_CPX32 || datatype == CDI_DATATYPE_CPX64 )
    number = CDI_COMP;
else
    number = CDI_REAL;

for ( int varID = 1; varID < vlistptr->nvars; varID++ )
    {
    datatype = vlistptr->vars[varID].datatype;
    if ( datatype == CDI_DATATYPE_CPX32 || datatype == CDI_DATATYPE_CPX64 )
        number2 = CDI_COMP;
    else
        number2 = CDI_REAL;

    if ( number2 != number )
        {
        number = CDI_BOTH;
        break;
        }
    }

return number;
}


int vlistNgrids(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

return vlistptr->ngrids;
}


int vlistNzaxis(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

return vlistptr->nzaxis;
}


int vlistNsubtypes(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

return vlistptr->nsubtypes;
}


void vlistDefNtsteps(int vlistID, int nts)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( vlistptr->ntsteps != nts )
    {
    vlistptr->ntsteps = nts;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistNtsteps(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

return (int)vlistptr->ntsteps;
}

static
void vlistPrintKernel(vlist_t *vlistptr, FILE *fp)
{
char paramstr[32];

fprintf ( fp, "#\n# vlistID %d\n#\n", vlistptr->self);

int nvars = vlistptr->nvars;

fprintf(fp, "nvars    : %d\n"
        "ngrids   : %d\n"
        "nzaxis   : %d\n"
        "nsubtypes: %d\n"
        "taxisID  : %d\n"
        "instID   : %d\n"
        "modelID  : %d\n"
        "tableID  : %d\n",
        nvars, vlistptr->ngrids, vlistptr->nzaxis, vlistptr->nsubtypes, vlistptr->taxisID,
        vlistptr->instID, vlistptr->modelID, vlistptr->tableID);

if ( nvars > 0 )
    {
    fprintf(fp, " varID param    gridID zaxisID stypeID tsteptype flag iorank"
            " name     longname         units\n");
    for ( int varID = 0; varID < nvars; varID++ )
        {
        int param = vlistptr->vars[varID].param;
        int gridID = vlistptr->vars[varID].gridID;
        int zaxisID = vlistptr->vars[varID].zaxisID;
        int subtypeID = vlistptr->vars[varID].subtypeID;
        int tsteptype = vlistptr->vars[varID].tsteptype;
        const char *name = vlistptr->vars[varID].name;
        const char *longname = vlistptr->vars[varID].longname;
        const char *units = vlistptr->vars[varID].units;
        int flag = vlistptr->vars[varID].flag;
        int iorank = vlistptr->vars[varID].iorank;

        cdiParamToString(param, paramstr, sizeof(paramstr));
        fprintf(fp, "%6d %-8s %6d  %6d  %6d  %6d  %5d %6d %-8s %s [%s]\n",
                varID, paramstr, gridID, zaxisID, subtypeID, tsteptype, flag, iorank,
                name?name:"", longname?longname:"", units?units:"");
        }

    fputs("\n"
            " varID  levID fvarID flevID mvarID mlevID  index  dtype  flag  level\n", fp);
    for ( int varID = 0; varID < nvars; varID++ )
        {
        int zaxisID = vlistptr->vars[varID].zaxisID;
        int nlevs = zaxisInqSize(zaxisID);
        int fvarID = vlistptr->vars[varID].fvarID;
        int mvarID = vlistptr->vars[varID].mvarID;
        int dtype    = vlistptr->vars[varID].datatype;
        for ( int levID = 0; levID < nlevs; levID++ )
            {
            levinfo_t li;
            if (vlistptr->vars[varID].levinfo)
                li = vlistptr->vars[varID].levinfo[levID];
            else
                li = DEFAULT_LEVINFO(levID);
            int flevID = li.flevelID;
            int mlevID = li.mlevelID;
            int index  = li.index;
            int flag   = li.flag;

            double level = zaxisInqLevels(zaxisID, NULL) ? zaxisInqLevel(zaxisID, levID) : levID+1;

            fprintf(fp, "%6d %6d %6d %6d %6d %6d %6d %6d %5d  %.9g\n",
                    varID, levID, fvarID, flevID, mvarID, mlevID, index,
                    dtype, flag, level);
            }
        }

    fputs("\n"
            " varID  size iorank\n", fp);
    for ( int varID = 0; varID < nvars; varID++ )
        fprintf(fp, "%3d %8zu %6d\n", varID,
                zaxisInqSize(vlistptr->vars[varID].zaxisID)
                * gridInqSize(vlistptr->vars[varID].gridID),
                vlistptr->vars[varID].iorank);
    }
}


void vlistPrint(int vlistID)
{
if ( vlistID == CDI_UNDEFID ) return;
vlist_t *vlistptr = vlist_to_pointer(vlistID);
vlistPrintKernel(vlistptr, stdout);
}


void vlistDefTaxis(int vlistID, int taxisID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( vlistptr->taxisID != taxisID )
    {

    vlistptr->taxisID = taxisID;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqTaxis(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

return vlistptr->taxisID;
}


void vlistDefTable(int vlistID, int tableID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( vlistptr->tableID != tableID )
    {
    vlistptr->tableID = tableID;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqTable(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

return vlistptr->tableID;
}


void vlistDefInstitut(int vlistID, int instID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( vlistptr->instID != instID )
    {
    vlistptr->instID = instID;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqInstitut(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

int instID = vlistptr->instID;

if ( instID == CDI_UNDEFID )
    {
    instID  = vlistInqVarInstitut(vlistID, 0);

    for ( int varID = 1; varID < vlistptr->nvars; varID++ )
        if ( instID != vlistInqVarInstitut(vlistID, varID) )
        {
            instID = CDI_UNDEFID;
            break;
    }
    vlistDefInstitut(vlistID, instID);
    }

return instID;
}


void vlistDefModel(int vlistID, int modelID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( vlistptr->modelID != modelID )
    {
    vlistptr->modelID = modelID;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqModel(int vlistID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

int modelID = vlistptr->modelID;

if ( modelID == CDI_UNDEFID )
    {
    modelID = vlistInqVarModel(vlistID, 0);

    for ( int varID = 1; varID < vlistptr->nvars; varID++ )
        if ( modelID != vlistInqVarModel(vlistID, varID) )
        {
            modelID = CDI_UNDEFID;
            break;
        }

    vlistDefModel(vlistID, modelID);
    }

return modelID;
}


size_t vlistGridsizeMax(int vlistID)
{
size_t gridsizemax = 0;
vlist_t *vlistptr = vlist_to_pointer(vlistID);

for ( int index = 0 ; index < vlistptr->ngrids ; index++ )
    {
    int gridID = vlistptr->gridIDs[index];
    size_t gridsize = gridInqSize(gridID);
    if ( gridsize > gridsizemax ) gridsizemax = gridsize;
    }

return gridsizemax;
}


int vlistGrid(int vlistID, int index)
{
int gridID = CDI_UNDEFID;
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( index < vlistptr->ngrids && index >= 0 )
    gridID = vlistptr->gridIDs[index];

return gridID;
}


int vlistGridIndex(int vlistID, int gridID)
{
int index;
vlist_t *vlistptr = vlist_to_pointer(vlistID);

for ( index = 0 ; index < vlistptr->ngrids ; index++ )
    if ( gridID == vlistptr->gridIDs[index] ) break;

if ( index == vlistptr->ngrids ) index = -1;

return index;
}


void vlistChangeGridIndex(int vlistID, int index, int gridID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

int gridIDold = vlistptr->gridIDs[index];
if (gridIDold != gridID)
    {
    vlistptr->gridIDs[index] = gridID;

    int nvars = vlistptr->nvars;
    for ( int varID = 0; varID < nvars; varID++ )
        if ( vlistptr->vars[varID].gridID == gridIDold )
        vlistptr->vars[varID].gridID = gridID;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistChangeGrid(int vlistID, int gridID1, int gridID2)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if (gridID1 != gridID2)
    {
    int ngrids = vlistptr->ngrids;
    for ( int index = 0; index < ngrids; index++ )
        {
        if ( vlistptr->gridIDs[index] == gridID1 )
            {
            vlistptr->gridIDs[index] = gridID2;
            break;
            }
        }
    int nvars = vlistptr->nvars;
    for ( int varID = 0; varID < nvars; varID++ )
        if ( vlistptr->vars[varID].gridID == gridID1 )
        vlistptr->vars[varID].gridID = gridID2;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistZaxis(int vlistID, int index)
{
int zaxisID = CDI_UNDEFID;
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( index < vlistptr->nzaxis && index >= 0 )
    zaxisID = vlistptr->zaxisIDs[index];

return zaxisID;
}


int vlistZaxisIndex(int vlistID, int zaxisID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

int index;
for ( index = 0 ; index < vlistptr->nzaxis ; index++ )
    if ( zaxisID == vlistptr->zaxisIDs[index] ) break;

if ( index == vlistptr->nzaxis ) index = -1;

return index;
}


void vlistChangeZaxisIndex(int vlistID, int index, int zaxisID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

int zaxisIDold = vlistptr->zaxisIDs[index];
if (zaxisIDold != zaxisID)
    {
    vlistptr->zaxisIDs[index] = zaxisID;

    int nlevs = zaxisInqSize(zaxisID),
        nlevsOld = zaxisInqSize(zaxisIDold);
    int nvars = vlistptr->nvars;
    for ( int varID = 0; varID < nvars; varID++ )
        if ( vlistptr->vars[varID].zaxisID == zaxisIDold )
        {
            vlistptr->vars[varID].zaxisID = zaxisID;
            if ( vlistptr->vars[varID].levinfo && nlevs != nlevsOld )
            {
                vlistptr->vars[varID].levinfo = (levinfo_t *) Realloc(vlistptr->vars[varID].levinfo, (size_t)nlevs * sizeof (levinfo_t));

                for ( int levID = 0; levID < nlevs; levID++ )
                vlistptr->vars[varID].levinfo[levID] = DEFAULT_LEVINFO(levID);
            }
        }
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistChangeZaxis(int vlistID, int zaxisID1, int zaxisID2)
{
int nlevs1 = zaxisInqSize(zaxisID1), nlevs2 = zaxisInqSize(zaxisID2);
vlist_t *vlistptr = vlist_to_pointer(vlistID);

int nzaxis = vlistptr->nzaxis;
for ( int index = 0; index < nzaxis; index++ )
    {
    if ( vlistptr->zaxisIDs[index] == zaxisID1 )
        {
        vlistptr->zaxisIDs[index] = zaxisID2;
        break;
        }
    }

int nvars = vlistptr->nvars;
for ( int varID = 0; varID < nvars; varID++ )
    if ( vlistptr->vars[varID].zaxisID == zaxisID1 )
    {
        vlistptr->vars[varID].zaxisID = zaxisID2;

        if ( vlistptr->vars[varID].levinfo && nlevs2 != nlevs1 )
        {
            vlistptr->vars[varID].levinfo
            = (levinfo_t *) Realloc(vlistptr->vars[varID].levinfo,
                                    (size_t)nlevs2 * sizeof(levinfo_t));

            for ( int levID = 0; levID < nlevs2; levID++ )
            vlistptr->vars[varID].levinfo[levID] = DEFAULT_LEVINFO(levID);
        }
    }
reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
}


int vlistSubtype(int vlistID, int index)
{
int subtypeID = CDI_UNDEFID;
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( index < vlistptr->nsubtypes && index >= 0 )
    subtypeID = vlistptr->subtypeIDs[index];

return subtypeID;
}


int vlistSubtypeIndex(int vlistID, int subtypeID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

int index;
for(index = vlistptr->nsubtypes; index--; )
    if ( subtypeID == vlistptr->subtypeIDs[index] ) break;

return index;
}


int vlistHasTime(int vlistID)
{
bool hastime = false;
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( !(CDI_reduce_dim && vlistptr->ntsteps == 1) )
    {
    for ( int varID = 0; varID <  vlistptr->nvars; varID++ )
        if ( vlistptr->vars[varID].timetype != TIME_CONSTANT )
        {
            hastime = true;
            break;
        }
    }

return (int)hastime;
}

enum {
vlist_nints=6,
};

static int vlistTxCode ( void )
{
return VLIST;
}


static
int  vlistGetSizeP ( void * vlistptr, void *context)
{
int txsize, varID;
vlist_t *p = (vlist_t*) vlistptr;
txsize = serializeGetSize(vlist_nints, CDI_DATATYPE_INT, context);
txsize += serializeGetSize(1, CDI_DATATYPE_LONG, context);
txsize += cdiAttsGetSize(p, CDI_GLOBAL, context);
for ( varID = 0; varID <  p->nvars; varID++ )
    txsize += vlistVarGetPackSize(p, varID, context);
return txsize;
}


static
void vlistPackP ( void * vlistptr, void * buf, int size, int *position,
                void *context )
{
int varID, tempbuf[vlist_nints];
vlist_t *p = (vlist_t*) vlistptr;
tempbuf[0] = p->self;
tempbuf[1] = p->nvars;
tempbuf[2] = p->taxisID;
tempbuf[3] = p->tableID;
tempbuf[4] = p->instID;
tempbuf[5] = p->modelID;
serializePack(tempbuf, vlist_nints, CDI_DATATYPE_INT, buf, size, position, context);
serializePack(&p->ntsteps, 1, CDI_DATATYPE_LONG, buf, size, position, context);

cdiAttsPack(p, CDI_GLOBAL, buf, size, position, context);
for ( varID = 0; varID < p->nvars; varID++ )
    {
    vlistVarPack(p, varID, (char *)buf, size, position, context);
    }
}

void vlistUnpack(char * buf, int size, int *position, int originNamespace,
                void *context, int force_id)
{
int tempbuf[vlist_nints];
serializeUnpack(buf, size, position, tempbuf, vlist_nints, CDI_DATATYPE_INT, context);
int nvars = tempbuf[1];
int targetID = namespaceAdaptKey(tempbuf[0], originNamespace);
vlist_t *p = vlist_new_entry(force_id?targetID:CDI_UNDEFID);
xassert(!force_id || p->self == targetID);
if (!force_id)
    targetID = p->self;
cdiVlistMakeInternal(p->self);
p->taxisID = namespaceAdaptKey(tempbuf[2], originNamespace);
p->tableID = tempbuf[3];
p->instID = namespaceAdaptKey(tempbuf[4], originNamespace);
p->modelID = namespaceAdaptKey(tempbuf[5], originNamespace);
serializeUnpack(buf, size, position, &p->ntsteps, 1, CDI_DATATYPE_LONG, context);
cdiAttsUnpack(targetID, CDI_GLOBAL, buf, size, position, context);
for (int varID = 0; varID < nvars; varID++ )
    vlistVarUnpack(targetID, buf, size, position, originNamespace, context);
reshSetStatus(targetID, &vlistOps,
                reshGetStatus(targetID, &vlistOps) & ~RESH_SYNC_BIT);
}


void vlist_check_contents(int vlistID)
{
int zaxisID;
int nzaxis = vlistNzaxis(vlistID);

for ( int index = 0; index < nzaxis; index++ )
    {
    zaxisID = vlistZaxis(vlistID, index);
    if ( zaxisInqType(zaxisID) == ZAXIS_GENERIC )
        cdiCheckZaxis(zaxisID);
    }
}



void resize_opt_grib_entries(var_t *var, int nentries)
{
if (var->opt_grib_kvpair_size >= nentries)
    {
    if ( CDI_Debug )
        Message("data structure has size %d, no resize to %d needed.", var->opt_grib_kvpair_size, nentries);
    return;
    }
else
    {
    if ( CDI_Debug )
        Message("resize data structure, %d -> %d", var->opt_grib_kvpair_size, nentries);

    int i, new_size;
    new_size = (2*var->opt_grib_kvpair_size) > nentries ? (2*var->opt_grib_kvpair_size) : nentries;
    opt_key_val_pair_t *tmp = (opt_key_val_pair_t *) Malloc((size_t)new_size * sizeof (opt_key_val_pair_t));
    for (i=0; i<var->opt_grib_kvpair_size; i++) {
        tmp[i] = var->opt_grib_kvpair[i];
    }
    for (i=var->opt_grib_kvpair_size; i<new_size; i++) {
        tmp[i].int_val =     0;
        tmp[i].dbl_val =     0;
        tmp[i].update  = false;
        tmp[i].keyword =  NULL;
    }
    var->opt_grib_kvpair_size = new_size;
    Free(var->opt_grib_kvpair);
    var->opt_grib_kvpair = tmp;
    }
}



enum  {KEY_INT = 1, KEY_FLOAT, KEY_BYTES};

static
cdi_keys_t *get_keysp(vlist_t *vlistptr, int varID)
{
cdi_keys_t *keysp = NULL;

if ( varID == CDI_GLOBAL )
    {
    keysp = &vlistptr->keys;
    }
else
    {
    if ( varID >= 0 && varID < vlistptr->nvars )
        keysp = &(vlistptr->vars[varID].keys);
    }

return keysp;
}

static
cdi_key_t *new_key(cdi_keys_t *keysp, int key)
{
xassert(keysp != NULL);

if ( keysp->nelems == keysp->nalloc ) return NULL;

cdi_key_t *keyp = &(keysp->value[keysp->nelems]);
keysp->nelems++;

keyp->key = key;
keyp->length = 0;
keyp->type = 0;
keyp->v.s = NULL;

return keyp;
}

static
cdi_key_t *find_key(cdi_keys_t *keysp, int key)
{
xassert(keysp != NULL);

if ( keysp->nelems == 0 ) return NULL;

cdi_key_t *keys = keysp->value;
for ( size_t keyid = 0; keyid < keysp->nelems; keyid++ )
    {
    cdi_key_t *keyp = keys + keyid;
    if ( keyp->key == key )
        return keyp;
    }

return NULL;
}

static
cdi_keys_t *cdi_get_keysp(int objID, int varID)
{
cdi_keys_t *keysp = NULL;


if ( reshGetTxCode(objID) == VLIST )
    {
    vlist_t *vlistptr = vlist_to_pointer(objID);
    keysp = get_keysp(vlistptr, varID);
    }

return keysp;
}


int vlist_key_compare(vlist_t *a, int varIDA, vlist_t *b, int varIDB, int keynum)
{
cdi_keys_t *keyspa = get_keysp(a, varIDA),
            *keyspb = get_keysp(b, varIDB);
if (keyspa == NULL && keyspb == NULL)
    return 0;
xassert(keynum >= 0 && keynum < (int)keyspa->nelems
        && keynum < (int)keyspb->nelems);
cdi_key_t *keypa = keyspa->value + keynum,
    *keypb = keyspb->value + keynum;

if ( keypa->key != keypb->key )
    return 1;

if ( keypa->v.i != keypb->v.i )
    return 1;

return 0;
}


void cdiDeleteVarKeys(cdi_keys_t *keysp)
{
for ( int keyid = 0; keyid < (int)keysp->nelems; keyid++ )
    {
    cdi_key_t *keyp = &(keysp->value[keyid]);
    if ( keyp->length )
        {
        free(keyp->v.s);
        keyp->v.s = NULL;
        keyp->length = 0;
        }
    }

keysp->nelems = 0;
}


int cdiDeleteKeys(int cdiID, int varID)
{
int status = CDI_NOERR;

cdi_keys_t *keysp = cdi_get_keysp(cdiID, varID);
xassert(keysp != NULL);

cdiDeleteVarKeys(keysp);

return status;
}


int cdiInqKeyLen(int cdiID, int varID, int key, int *length)
{
int status = -1;

cdi_keys_t *keysp = cdi_get_keysp(cdiID, varID);
xassert(keysp != NULL);

cdi_key_t *keyp = find_key(keysp, key);
if ( keyp != NULL )
    {
    *length = keyp->length;
    if ( *length == 0 ) *length = 1;
    status = CDI_NOERR;
    }

return status;
}


void cdiCopyVarKeys(cdi_keys_t *keysp, int cdiID2, int varID2)
{
for ( size_t keyid = 0; keyid < keysp->nelems; keyid++ )
    {
    cdi_key_t *keyp = &(keysp->value[keyid]);
    if ( keyp->type == KEY_INT )
        cdiDefKeyInt(cdiID2, varID2, keyp->key, keyp->v.i);
    else if ( keyp->type == KEY_BYTES )
        cdiDefKeyBytes(cdiID2, varID2, keyp->key, keyp->v.s, keyp->length);
    }
}


int cdiCopyKeys(int cdiID1, int varID1, int cdiID2, int varID2)
{
int status = CDI_NOERR;

cdi_keys_t *keysp = cdi_get_keysp(cdiID1, varID1);
xassert(keysp != NULL);

cdiCopyVarKeys(keysp, cdiID2, varID2);

return status;
}


void cdiDefVarKeyInt(cdi_keys_t *keysp, int key, int value)
{
cdi_key_t *keyp = find_key(keysp, key);
if ( keyp == NULL ) keyp = new_key(keysp, key);

if ( keyp != NULL )
    {

        {
        keyp->type = KEY_INT;
        keyp->v.i = value;
        }
    }
}


int cdiDefKeyInt(int cdiID, int varID, int key, int value)
{
int status = CDI_NOERR;

cdi_keys_t *keysp = cdi_get_keysp(cdiID, varID);
xassert(keysp != NULL);

cdiDefVarKeyInt(keysp, key, value);

return status;
}


int cdiInqKeyInt(int cdiID, int varID, int key, int *value)
{
int status = -1;

if ( varID != CDI_GLOBAL ) status = cdiInqKeyInt(cdiID, CDI_GLOBAL, key, value);

cdi_keys_t *keysp = cdi_get_keysp(cdiID, varID);
xassert(keysp != NULL);

cdi_key_t *keyp = find_key(keysp, key);
if ( keyp != NULL )
    {
    if ( keyp->type == KEY_INT )
        {
        *value = keyp->v.i;
        status = CDI_NOERR;
        }
    }

return status;
}


void cdiDefVarKeyBytes(cdi_keys_t *keysp, int key, const unsigned char *bytes, int length)
{
cdi_key_t *keyp = find_key(keysp, key);
if ( keyp == NULL ) keyp = new_key(keysp, key);

if ( keyp != NULL )
    {
    if ( keyp->length != 0 && keyp->length != length )
        {
        free(keyp->v.s);
        keyp->length = 0;
        }
    if ( keyp->length == 0 )
        {
        keyp->v.s = (unsigned char *) malloc(length);
        keyp->length = length;
        }

    memcpy(keyp->v.s, bytes, length);
    keyp->type = KEY_BYTES;
    }
}


int cdiDefKeyBytes(int cdiID, int varID, int key, const unsigned char *bytes, int length)
{
int status = CDI_NOERR;

cdi_keys_t *keysp = cdi_get_keysp(cdiID, varID);
xassert(keysp != NULL);

cdiDefVarKeyBytes(keysp, key, bytes, length);

return status;
}


int cdiInqKeyBytes(int cdiID, int varID, int key, unsigned char *bytes, int *length)
{
int status = -1;
xassert(bytes != NULL);
xassert(length != NULL);

if ( varID != CDI_GLOBAL ) status = cdiInqKeyBytes(cdiID, CDI_GLOBAL, key, bytes, length);

cdi_keys_t *keysp = cdi_get_keysp(cdiID, varID);
xassert(keysp != NULL);

cdi_key_t *keyp = find_key(keysp, key);
if ( keyp != NULL )
    {
    if ( keyp->type == KEY_BYTES )
        {
        if ( keyp->length < *length ) *length = keyp->length;
        memcpy(bytes, keyp->v.s, *length);
        status = CDI_NOERR;
        }
    }

return status;
}


int cdiDefKeyString(int cdiID, int varID, int key, const char *string)
{
xassert(string != NULL);

int length = strlen(string)+1;
int status = cdiDefKeyBytes(cdiID, varID, key, (const unsigned char *) string, length);

return status;
}


int cdiInqKeyString(int cdiID, int varID, int key, char *string, int *length)
{
xassert(string != NULL);
xassert(length != NULL);

string[0] = '\0';

int status = cdiInqKeyBytes(cdiID, varID, key, (unsigned char *) string, length);

return status;
}


#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>




static
cdi_atts_t *get_attsp(vlist_t *vlistptr, int varID)
{
cdi_atts_t *attsp = NULL;

if ( varID == CDI_GLOBAL )
    {
    attsp = &vlistptr->atts;
    }
else
    {
    if ( varID >= 0 && varID < vlistptr->nvars )
        attsp = &(vlistptr->vars[varID].atts);
    }

return attsp;
}

static
cdi_att_t *find_att(cdi_atts_t *attsp, const char *name)
{
xassert(attsp != NULL);

if ( attsp->nelems == 0 ) return NULL;

size_t slen = strlen(name);
if ( slen > CDI_MAX_NAME ) slen = CDI_MAX_NAME;

cdi_att_t *atts = attsp->value;
for ( size_t attid = 0; attid < attsp->nelems; attid++ )
    {
    cdi_att_t *attp = atts + attid;
    if ( attp->namesz == slen && memcmp(attp->name, name, slen) == 0 )
        return attp;
    }

return NULL;
}

static
cdi_att_t *new_att(cdi_atts_t *attsp, const char *name)
{
xassert(attsp != NULL);
xassert(name  != NULL);

if ( attsp->nelems == attsp->nalloc ) return NULL;

cdi_att_t *attp = &(attsp->value[attsp->nelems]);
attsp->nelems++;

size_t slen = strlen(name);
if ( slen > CDI_MAX_NAME ) slen = CDI_MAX_NAME;

attp->name = (char *) Malloc(slen+1);
memcpy(attp->name, name, slen+1);
attp->namesz = slen;
attp->xvalue = NULL;

return attp;
}

static
void fill_att(cdi_att_t *attp, int indtype, int exdtype, size_t nelems, size_t xsz, const void *xvalue)
{
xassert(attp != NULL);

attp->xsz = xsz;
attp->indtype = indtype;
attp->exdtype = exdtype;
attp->nelems  = nelems;

if ( xsz > 0 )
    {
    attp->xvalue = Realloc(attp->xvalue, xsz);
    memcpy(attp->xvalue, xvalue, xsz);
    }
}

static
cdi_atts_t *cdi_get_attsp(int objID, int varID)
{
cdi_atts_t *attsp = NULL;

if ( varID == CDI_GLOBAL && reshGetTxCode(objID) == GRID )
    {
    grid_t *gridptr = grid_to_pointer(objID);
    attsp = &gridptr->atts;
    }
else if ( varID == CDI_GLOBAL && reshGetTxCode(objID) == ZAXIS )
    {
    zaxis_t *zaxisptr = zaxis_to_pointer(objID);
    attsp = &zaxisptr->atts;
    }
else
    {
    vlist_t *vlistptr = vlist_to_pointer(objID);
    attsp = get_attsp(vlistptr, varID);
    }

return attsp;
}


int cdiInqNatts(int cdiID, int varID, int *nattsp)
{
int status = CDI_NOERR;

cdi_atts_t *attsp = cdi_get_attsp(cdiID, varID);
xassert(attsp != NULL);

*nattsp = (int)attsp->nelems;

return status;
}


int cdiInqAtt(int cdiID, int varID, int attnum, char *name, int *typep, int *lenp)
{
int status = CDI_NOERR;

xassert(name != NULL);

cdi_atts_t *attsp = cdi_get_attsp(cdiID, varID);
xassert(attsp != NULL);

cdi_att_t *attp = NULL;
if ( attnum >= 0 && attnum < (int)attsp->nelems )
    attp = &(attsp->value[attnum]);

if ( attp != NULL )
    {
    memcpy(name, attp->name, attp->namesz+1);
    *typep  = attp->exdtype;
    *lenp   = (int)attp->nelems;
    }
else
    {
    name[0] =  0;
    *typep  = -1;
    *lenp   =  0;
    status  = -1;
    }

return status;
}


int cdiDeleteAtts(int cdiID, int varID)
{
int status = CDI_NOERR;

cdi_atts_t *attsp = cdi_get_attsp(cdiID, varID);
xassert(attsp != NULL);

for ( int attid = 0; attid < (int)attsp->nelems; attid++ )
    {
    cdi_att_t *attp = &(attsp->value[attid]);
    if ( attp->name   ) Free(attp->name);
    if ( attp->xvalue ) Free(attp->xvalue);
    }

attsp->nelems = 0;

return status;
}


int cdiDelAtt(int cdiID, int varID, const char *name)
{
int status = CDI_NOERR;

UNUSED(cdiID);
UNUSED(varID);
UNUSED(name);

fprintf(stderr, "cdiDelAtt not implemented!\n");

return status;
}

static
int cdi_def_att(int indtype, int exdtype, int cdiID, int varID, const char *name, size_t len, size_t xsz, const void *xp)
{
int status = CDI_NOERR;

if ( len != 0 && xp == NULL )
    return CDI_EINVAL;

cdi_atts_t *attsp = cdi_get_attsp(cdiID, varID);
xassert(attsp != NULL);

cdi_att_t *attp = find_att(attsp, name);
if ( attp == NULL ) attp = new_att(attsp, name);

if ( attp != NULL ) fill_att(attp, indtype, exdtype, len, xsz, xp);

return status;
}

static
int cdi_inq_att(int indtype, int cdiID, int varID, const char *name, size_t mxsz, void *xp)
{
int status = CDI_NOERR;

if ( mxsz != 0 && xp == NULL )
    return CDI_EINVAL;

cdi_atts_t *attsp = cdi_get_attsp(cdiID, varID);
xassert(attsp != NULL);

cdi_att_t *attp = find_att(attsp, name);
if ( attp != NULL )
    {
    if ( attp->indtype == indtype )
        {
        size_t xsz = attp->xsz;
        if ( mxsz < xsz ) xsz = mxsz;
        if ( xsz > 0 )
            memcpy(xp, attp->xvalue, xsz);
        }
    else
        {
        Warning("Attribute %s has wrong data type!", name);
        status = -2;
        }
    }
else
    {

    status = -1;
    }

return status;
}


int cdiCopyAtts(int cdiID1, int varID1, int cdiID2, int varID2)
{
int status = CDI_NOERR;

cdi_atts_t *attsp1 = cdi_get_attsp(cdiID1, varID1);
xassert(attsp1 != NULL);

for ( size_t attid = 0; attid < attsp1->nelems; attid++ )
    {
    cdi_att_t *attp = &(attsp1->value[attid]);
    cdi_def_att(attp->indtype, attp->exdtype, cdiID2, varID2, attp->name, attp->nelems, attp->xsz, attp->xvalue);
    }

return status;
}


int cdiDefAttInt(int cdiID, int varID, const char *name, int type, int len, const int *ip)
{
return cdi_def_att(CDI_DATATYPE_INT, type, cdiID, varID, name, (size_t)len, (size_t)len * sizeof(int), ip);
}


int cdiDefAttFlt(int cdiID, int varID, const char *name, int type, int len, const double *dp)
{
return cdi_def_att(CDI_DATATYPE_FLT, type, cdiID, varID, name, (size_t)len, (size_t)len * sizeof(double), dp);
}


int cdiDefAttTxt(int cdiID, int varID, const char *name, int len, const char *tp)
{
return cdi_def_att(CDI_DATATYPE_TXT, CDI_DATATYPE_TXT, cdiID, varID, name, (size_t)len, (size_t)len, tp);
}


int cdiInqAttInt(int cdiID, int varID, const char *name, int mlen, int *ip)
{
return cdi_inq_att(CDI_DATATYPE_INT, cdiID, varID, name, (size_t)mlen * sizeof(int), ip);
}


int cdiInqAttFlt(int cdiID, int varID, const char *name, int mlen, double *dp)
{
return cdi_inq_att(CDI_DATATYPE_FLT, cdiID, varID, name, (size_t)mlen * sizeof(double), dp);
}


int cdiInqAttTxt(int cdiID, int varID, const char *name, int mlen, char *tp)
{
return cdi_inq_att(CDI_DATATYPE_TXT, cdiID, varID, name, (size_t)mlen * sizeof(char), tp);
}

enum {
cdi_att_nints = 4,
};

static inline
int cdiAttTypeLookup(cdi_att_t *attp)
{
int type;
switch (attp->indtype)
{
case CDI_DATATYPE_FLT:
    type = CDI_DATATYPE_FLT64;
    break;
case CDI_DATATYPE_INT:
case CDI_DATATYPE_TXT:
    type = attp->indtype;
    break;
default:
    xabort("Unknown datatype encountered in attribute %s: %d\n",
            attp->name, attp->indtype);
}
return type;
}


int cdi_att_compare(vlist_t *a, int varIDA, vlist_t *b, int varIDB, int attnum)
{
cdi_atts_t *attspa = get_attsp(a, varIDA),
    *attspb = get_attsp(b, varIDB);
if (attspa == NULL && attspb == NULL)
    return 0;
xassert(attnum >= 0 && attnum < (int)attspa->nelems
        && attnum < (int)attspb->nelems);
cdi_att_t *attpa = attspa->value + attnum,
    *attpb = attspb->value + attnum;
size_t len;
if ((len = attpa->namesz) != attpb->namesz)
    return 1;
int diff;
if ((diff = memcmp(attpa->name, attpb->name, len)))
    return 1;
if (attpa->indtype != attpb->indtype
    || attpa->exdtype != attpb->exdtype
    || attpa->nelems != attpb->nelems)
    return 1;
return memcmp(attpa->xvalue, attpb->xvalue, attpa->xsz);
}


static
int cdiAttGetSize(vlist_t *vlistptr, int varID, int attnum, void *context)
{
cdi_atts_t *attsp;
cdi_att_t *attp;

xassert(attsp = get_attsp(vlistptr, varID));
xassert(attnum >= 0 && attnum < (int)attsp->nelems);
attp = &(attsp->value[attnum]);
int txsize = serializeGetSize(cdi_att_nints, CDI_DATATYPE_INT, context)
    + serializeGetSize((int)attp->namesz, CDI_DATATYPE_TXT, context);
txsize += serializeGetSize((int)attp->nelems, cdiAttTypeLookup(attp), context);
return txsize;
}


int cdiAttsGetSize(void *vp, int varID, void *context)
{
vlist_t *p = (vlist_t*) vp;
cdi_atts_t *attsp = get_attsp(p, varID);
int txsize = serializeGetSize(1, CDI_DATATYPE_INT, context);
size_t numAtts = attsp->nelems;
for (size_t i = 0; i < numAtts; ++i)
    txsize += cdiAttGetSize(p, varID, (int)i, context);
return txsize;
}

static
void cdiAttPack(vlist_t *vlistptr, int varID, int attnum,
                void *buf, int size, int *position, void *context)
{
cdi_atts_t *attsp;
cdi_att_t *attp;
int tempbuf[cdi_att_nints];

xassert(attsp = get_attsp(vlistptr, varID));
xassert(attnum >= 0 && attnum < (int)attsp->nelems);
attp = &(attsp->value[attnum]);
tempbuf[0] = (int)attp->namesz;
tempbuf[1] = attp->exdtype;
tempbuf[2] = attp->indtype;
tempbuf[3] = (int)attp->nelems;
serializePack(tempbuf, cdi_att_nints, CDI_DATATYPE_INT, buf, size, position, context);
serializePack(attp->name, (int)attp->namesz, CDI_DATATYPE_TXT, buf, size, position, context);
serializePack(attp->xvalue, (int)attp->nelems, cdiAttTypeLookup(attp),
                buf, size, position, context);
}


void cdiAttsPack(void *vp, int varID, void *buf, int size, int *position, void *context)
{
vlist_t *p = (vlist_t*) vp;
cdi_atts_t *attsp = get_attsp(p, varID);
size_t numAtts = attsp->nelems;
int numAttsI = (int)numAtts;
xassert(numAtts <= INT_MAX);
serializePack(&numAttsI, 1, CDI_DATATYPE_INT, buf, size, position, context);
for (size_t i = 0; i < numAtts; ++i)
    cdiAttPack(p, varID, (int)i, buf, size, position, context);
}

static
void cdiAttUnpack(int cdiID, int varID, void *buf, int size, int *position, void *context)
{
int tempbuf[cdi_att_nints];

serializeUnpack(buf, size, position,
                tempbuf, cdi_att_nints, CDI_DATATYPE_INT, context);
char *attName = (char *) Malloc((size_t)tempbuf[0] + 1);
serializeUnpack(buf, size, position, attName, tempbuf[0], CDI_DATATYPE_TXT, context);
attName[tempbuf[0]] = '\0';
int attVDt;
size_t elemSize;
switch (tempbuf[2])
{
case CDI_DATATYPE_FLT:
    attVDt = CDI_DATATYPE_FLT64;
    elemSize = sizeof(double);
    break;
case CDI_DATATYPE_INT:
    attVDt = CDI_DATATYPE_INT;
    elemSize = sizeof(int);
    break;
case CDI_DATATYPE_TXT:
    attVDt = CDI_DATATYPE_TXT;
    elemSize = 1;
    break;
default:
    xabort("Unknown datatype encountered in attribute %s: %d\n",
        attName, tempbuf[2]);
}
void *attData = (void *) Malloc(elemSize * (size_t)tempbuf[3]);
serializeUnpack(buf, size, position, attData, tempbuf[3], attVDt, context);
cdi_def_att(tempbuf[2], tempbuf[1], cdiID, varID, attName,
            (size_t)tempbuf[3], (size_t)tempbuf[3] * elemSize, attData);
Free(attName);
Free(attData);
}


void cdiAttsUnpack(int cdiID, int varID, void *buf, int size, int *position, void *context)
{
int numAtts;
serializeUnpack(buf, size, position, &numAtts, 1, CDI_DATATYPE_INT, context);
for ( int i = 0; i < numAtts; ++i )
    cdiAttUnpack(cdiID, varID, buf, size, position, context);
}


#ifndef TABLEPAR_H
#define TABLEPAR_H

enum {
TABLE_DUP_NAME = 1 << 0,
TABLE_DUP_LONGNAME = 1 << 1,
TABLE_DUP_UNITS = 1 << 2,
};

typedef struct
{
int id;
int ltype;
int dupflags;
const char *name;
const char *longname;
const char *units;
}
param_type;


static void tableLink(int tableID, const param_type *pars, int npars);
int tableDef(int modelID, int tablegribID, const char *tablename);

int tableInqParCode(int tableID, char *name, int *code);

#endif

#ifdef HAVE_CONFIG_H
#endif



static
void vlistvarInitEntry(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistptr->vars[varID].fvarID        = varID;
vlistptr->vars[varID].mvarID        = varID;
vlistptr->vars[varID].flag          = 0;
vlistptr->vars[varID].param         = 0;
vlistptr->vars[varID].datatype      = CDI_UNDEFID;
vlistptr->vars[varID].timetype      = CDI_UNDEFID;
vlistptr->vars[varID].tsteptype     = TSTEP_INSTANT;
vlistptr->vars[varID].timave        = 0;
vlistptr->vars[varID].chunktype     = cdiChunkType;
vlistptr->vars[varID].xyz           = 321;
vlistptr->vars[varID].gridID        = CDI_UNDEFID;
vlistptr->vars[varID].zaxisID       = CDI_UNDEFID;
vlistptr->vars[varID].subtypeID     = CDI_UNDEFID;
vlistptr->vars[varID].instID        = CDI_UNDEFID;
vlistptr->vars[varID].modelID       = CDI_UNDEFID;
vlistptr->vars[varID].tableID       = CDI_UNDEFID;
vlistptr->vars[varID].missvalused   = false;
vlistptr->vars[varID].missval       = CDI_default_missval;
vlistptr->vars[varID].addoffset     = 0.0;
vlistptr->vars[varID].scalefactor   = 1.0;
vlistptr->vars[varID].name          = NULL;
vlistptr->vars[varID].longname      = NULL;
vlistptr->vars[varID].stdname       = NULL;
vlistptr->vars[varID].units         = NULL;
vlistptr->vars[varID].extra         = NULL;
vlistptr->vars[varID].levinfo       = NULL;
vlistptr->vars[varID].comptype      = CDI_COMPRESS_NONE;
vlistptr->vars[varID].complevel     = 1;
vlistptr->vars[varID].keys.nalloc   = MAX_KEYS;
vlistptr->vars[varID].keys.nelems   = 0;
for ( int i = 0; i < MAX_KEYS; ++i )
    vlistptr->vars[varID].keys.value[i].length = 0;
vlistptr->vars[varID].atts.nalloc   = MAX_ATTRIBUTES;
vlistptr->vars[varID].atts.nelems   = 0;
vlistptr->vars[varID].lvalidrange   = false;
vlistptr->vars[varID].validrange[0] = VALIDMISS;
vlistptr->vars[varID].validrange[1] = VALIDMISS;
vlistptr->vars[varID].iorank        = CDI_UNDEFID;
vlistptr->vars[varID].opt_grib_kvpair_size = 0;
vlistptr->vars[varID].opt_grib_kvpair      = NULL;
vlistptr->vars[varID].opt_grib_nentries    = 0;
}

static
int vlistvarNewEntry(int vlistID)
{
int varID = 0;
vlist_t *vlistptr = vlist_to_pointer(vlistID);
int vlistvarSize = vlistptr->varsAllocated;
var_t *vlistvar = vlistptr->vars;

if ( ! vlistvarSize )
    {
    vlistvarSize = 2;
    vlistvar = (var_t *) Malloc((size_t)vlistvarSize * sizeof (var_t));
    for ( int i = 0; i < vlistvarSize; i++ )
        vlistvar[i].isUsed = false;
    }
else
    {
    while (varID < vlistvarSize && vlistvar[varID].isUsed)
        ++varID;
    }

if ( varID == vlistvarSize )
    {
    vlistvar = (var_t *) Realloc(vlistvar, (size_t)(vlistvarSize *= 2) * sizeof(var_t));
    for ( int i = varID; i < vlistvarSize; i++ )
        vlistvar[i].isUsed = false;
    }

vlistptr->varsAllocated = vlistvarSize;
vlistptr->vars          = vlistvar;

vlistvarInitEntry(vlistID, varID);

vlistptr->vars[varID].isUsed = true;

return varID;
}

void vlistCheckVarID(const char *caller, int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( vlistptr == NULL )
    Errorc("vlist undefined!");

if ( varID < 0 || varID >= vlistptr->nvars )
    Errorc("varID %d undefined!", varID);

if ( ! vlistptr->vars[varID].isUsed )
    Errorc("varID %d undefined!", varID);
}


int vlistDefVarTiles(int vlistID, int gridID, int zaxisID, int timetype, int tilesetID)
{
if ( CDI_Debug )
    Message("gridID = %d  zaxisID = %d  timetype = %d", gridID, zaxisID, timetype);

int varID = vlistvarNewEntry(vlistID);

vlist_t *vlistptr = vlist_to_pointer(vlistID);
vlistptr->nvars++;
vlistptr->vars[varID].gridID    = gridID;
vlistptr->vars[varID].zaxisID   = zaxisID;
vlistptr->vars[varID].timetype = timetype;
vlistptr->vars[varID].subtypeID = tilesetID;

if ( timetype < 0 )
    {
    Message("Unexpected time type %d, set to TIME_VARYING!", timetype);
    vlistptr->vars[varID].timetype = TIME_VARYING;
    }

vlistAdd2GridIDs(vlistptr, gridID);
vlistAdd2ZaxisIDs(vlistptr, zaxisID);
vlistAdd2SubtypeIDs(vlistptr, tilesetID);

vlistptr->vars[varID].param = cdiEncodeParam(-(varID + 1), 255, 255);
reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);

return varID;
}


int vlistDefVar(int vlistID, int gridID, int zaxisID, int timetype)
{

return vlistDefVarTiles(vlistID, gridID, zaxisID, timetype, CDI_UNDEFID);
}

void
cdiVlistCreateVarLevInfo(vlist_t *vlistptr, int varID)
{
xassert(varID >= 0 && varID < vlistptr->nvars
        && vlistptr->vars[varID].levinfo == NULL);
int zaxisID = vlistptr->vars[varID].zaxisID;
size_t nlevs = (size_t)zaxisInqSize(zaxisID);

vlistptr->vars[varID].levinfo
    = (levinfo_t*) Malloc((size_t)nlevs * sizeof(levinfo_t));

for (size_t levID = 0; levID < nlevs; levID++ )
    vlistptr->vars[varID].levinfo[levID] = DEFAULT_LEVINFO((int)levID);
}


void vlistDefVarParam(int vlistID, int varID, int param)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if (vlistptr->vars[varID].param != param)
    {
    vlistptr->vars[varID].param = param;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistDefVarCode(int vlistID, int varID, int code)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

int param = vlistptr->vars[varID].param;
int pnum, pcat, pdis;
cdiDecodeParam(param, &pnum, &pcat, &pdis);
int newParam = cdiEncodeParam(code, pcat, pdis);
if (vlistptr->vars[varID].param != newParam)
    {
    vlistptr->vars[varID].param = newParam;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistInqVar(int vlistID, int varID, int *gridID, int *zaxisID, int *timetype)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

*gridID   = vlistptr->vars[varID].gridID;
*zaxisID  = vlistptr->vars[varID].zaxisID;
*timetype = vlistptr->vars[varID].timetype;

return;
}


int vlistInqVarGrid(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].gridID;
}


int vlistInqVarZaxis(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].zaxisID;
}



int vlistInqVarSubtype(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].subtypeID;
}



int vlistInqVarParam(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].param;
}


int vlistInqVarCode(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

int param = vlistptr->vars[varID].param;
int pdis, pcat, pnum;
cdiDecodeParam(param, &pnum, &pcat, &pdis);
int code = pnum;
if ( pdis != 255 ) code = -varID-1;

if ( code < 0 && vlistptr->vars[varID].tableID != -1 && vlistptr->vars[varID].name != NULL )
    {
    tableInqParCode(vlistptr->vars[varID].tableID, vlistptr->vars[varID].name, &code);
    }

return code;
}


const char *vlistInqVarNamePtr(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].name;
}


void vlistInqVarName(int vlistID, int varID, char *name)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( vlistptr->vars[varID].name == NULL )
    {
    int param = vlistptr->vars[varID].param;
    int pdis, pcat, pnum;
    cdiDecodeParam(param, &pnum, &pcat, &pdis);
    if ( pdis == 255 )
        {
        int code = pnum;
        int tableID = vlistptr->vars[varID].tableID;
        name[0] = 0;
        tableInqEntry(tableID, code, -1, name, NULL, NULL);
        if ( !name[0] ) sprintf(name, "var%d", code);
        }
    else
        {
        sprintf(name, "param%d.%d.%d", pnum, pcat, pdis);
        }
    }
else
    strcpy(name, vlistptr->vars[varID].name);

return;
}


char* vlistCopyVarName(int vlistId, int varId)
{
vlist_t* vlistptr = vlist_to_pointer(vlistId);
vlistCheckVarID(__func__, vlistId, varId);


{
    const char* name = vlistptr->vars[varId].name;
    if (name) return strdup(name);
}


int param = vlistptr->vars[varId].param;
int discipline, category, number;
cdiDecodeParam(param, &number, &category, &discipline);
char *result = NULL;
if (discipline == 255)
    {
    int tableId = vlistptr->vars[varId].tableID;
    char name[CDI_MAX_NAME]; name[0] = 0;
    tableInqEntry(tableId, number, -1, name, NULL, NULL);
    if ( name[0] )
        result = strdup(name);
    else
        {

        result = (char *) Malloc(3 + 3 * sizeof (int) * CHAR_BIT / 8 + 2);
        sprintf(result, "var%d", number);
        }
    }
else
    {
    result = (char *) Malloc(5 + 2 + 3 * (3 * sizeof (int) * CHAR_BIT + 1) + 1);
    sprintf(result, "param%d.%d.%d", number, category, discipline);
    }

return result;
}


void vlistInqVarLongname(int vlistID, int varID, char *longname)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

longname[0] = '\0';

if ( vlistptr->vars[varID].longname == NULL )
    {
    int param = vlistptr->vars[varID].param;
    int pdis, pcat, pnum;
    cdiDecodeParam(param, &pnum, &pcat, &pdis);
    if ( pdis == 255 )
        {
        int code = pnum;
        int tableID = vlistptr->vars[varID].tableID;
        tableInqEntry(tableID, code, -1, NULL, longname, NULL);
        }
    }
else
    strcpy(longname, vlistptr->vars[varID].longname);

return;
}


void vlistInqVarStdname(int vlistID, int varID, char *stdname)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( vlistptr->vars[varID].stdname == NULL )
    {
    stdname[0] = '\0';
    }
else
    strcpy(stdname, vlistptr->vars[varID].stdname);

return;
}


void vlistInqVarUnits(int vlistID, int varID, char *units)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

units[0] = '\0';

if ( vlistptr->vars[varID].units == NULL )
    {
    int param = vlistptr->vars[varID].param;
    int pdis, pcat, pnum;
    cdiDecodeParam(param, &pnum, &pcat, &pdis);
    if ( pdis == 255 )
        {
        int code = pnum;
        int tableID = vlistptr->vars[varID].tableID;
        tableInqEntry(tableID, code, -1, NULL, NULL, units);
        }
    }
else
    strcpy(units, vlistptr->vars[varID].units);

return;
}


int vlistInqVarID(int vlistID, int code)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

for ( int varID = 0; varID < vlistptr->nvars; varID++ )
    {
    int param = vlistptr->vars[varID].param;
    int pdis, pcat, pnum;
    cdiDecodeParam(param, &pnum, &pcat, &pdis);
    if ( pnum == code ) return varID;
    }

return CDI_UNDEFID;
}


size_t vlistInqVarSize(int vlistID, int varID)
{
vlistCheckVarID(__func__, vlistID, varID);

int zaxisID, gridID, timetype;
vlistInqVar(vlistID, varID, &gridID, &zaxisID, &timetype);

size_t nlevs = (size_t)zaxisInqSize(zaxisID);

size_t gridsize = gridInqSize(gridID);

size_t size = gridsize*nlevs;

return size;
}


int vlistInqVarDatatype(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].datatype;
}


int vlistInqVarNumber(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

int number = CDI_REAL;
if ( vlistptr->vars[varID].datatype == CDI_DATATYPE_CPX32 ||
    vlistptr->vars[varID].datatype == CDI_DATATYPE_CPX64 )
    number = CDI_COMP;

return number;
}


void vlistDefVarDatatype(int vlistID, int varID, int datatype)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if (vlistptr->vars[varID].datatype != datatype)
    {
    vlistptr->vars[varID].datatype = datatype;

    if ( !vlistptr->vars[varID].missvalused )
        switch (datatype)
        {
        case CDI_DATATYPE_INT8:   vlistptr->vars[varID].missval = -SCHAR_MAX; break;
        case CDI_DATATYPE_UINT8:  vlistptr->vars[varID].missval =  UCHAR_MAX; break;
        case CDI_DATATYPE_INT16:  vlistptr->vars[varID].missval = -SHRT_MAX;  break;
        case CDI_DATATYPE_UINT16: vlistptr->vars[varID].missval =  USHRT_MAX; break;
        case CDI_DATATYPE_INT32:  vlistptr->vars[varID].missval = -INT_MAX;   break;
        case CDI_DATATYPE_UINT32: vlistptr->vars[varID].missval =  UINT_MAX;  break;
        }
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistDefVarInstitut(int vlistID, int varID, int instID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
if (vlistptr->vars[varID].instID != instID)
    {
    vlistptr->vars[varID].instID = instID;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarInstitut(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
return vlistptr->vars[varID].instID;
}


void vlistDefVarModel(int vlistID, int varID, int modelID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
if (vlistptr->vars[varID].modelID != modelID)
    {
    vlistptr->vars[varID].modelID = modelID;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarModel(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
return vlistptr->vars[varID].modelID;
}


void vlistDefVarTable(int vlistID, int varID, int tableID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if (vlistptr->vars[varID].tableID != tableID)
    {
    vlistptr->vars[varID].tableID = tableID;
    int tablenum = tableInqNum(tableID);

    int param = vlistptr->vars[varID].param;

    int pnum, pcat, pdis;
    cdiDecodeParam(param, &pnum, &pcat, &pdis);
    vlistptr->vars[varID].param = cdiEncodeParam(pnum, tablenum, pdis);
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarTable(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
return vlistptr->vars[varID].tableID;
}


void vlistDefVarName(int vlistID, int varID, const char *name)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( name )
    {
    if ( vlistptr->vars[varID].name )
        {
        Free(vlistptr->vars[varID].name);
        vlistptr->vars[varID].name = NULL;
        }

    vlistptr->vars[varID].name = strdupx(name);
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistDefVarLongname(int vlistID, int varID, const char *longname)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( longname )
    {
    if ( vlistptr->vars[varID].longname )
        {
        Free(vlistptr->vars[varID].longname);
        vlistptr->vars[varID].longname = 0;
        }

    vlistptr->vars[varID].longname = strdupx(longname);
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistDefVarStdname(int vlistID, int varID, const char *stdname)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( stdname )
    {
    if ( vlistptr->vars[varID].stdname )
        {
        Free(vlistptr->vars[varID].stdname);
        vlistptr->vars[varID].stdname = 0;
        }

    vlistptr->vars[varID].stdname = strdupx(stdname);
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistDefVarUnits(int vlistID, int varID, const char *units)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( units )
    {
    if ( vlistptr->vars[varID].units )
        {
        Free(vlistptr->vars[varID].units);
        vlistptr->vars[varID].units = 0;
        }

    vlistptr->vars[varID].units = strdupx(units);
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


double vlistInqVarMissval(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].missval;
}


void vlistDefVarMissval(int vlistID, int varID, double missval)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

vlistptr->vars[varID].missval = missval;
vlistptr->vars[varID].missvalused = true;
}


void vlistDefVarExtra(int vlistID, int varID, const char *extra)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( extra )
    {
    if ( vlistptr->vars[varID].extra )
        {
        Free(vlistptr->vars[varID].extra);
        vlistptr->vars[varID].extra = NULL;
        }

    vlistptr->vars[varID].extra = strdupx(extra);
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistInqVarExtra(int vlistID, int varID, char *extra)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( vlistptr->vars[varID].extra == NULL )
    sprintf(extra, "-");
else
    strcpy(extra, vlistptr->vars[varID].extra);

return;
}


int vlistInqVarValidrange(int vlistID, int varID, double *validrange)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( validrange != NULL && vlistptr->vars[varID].lvalidrange )
    {
    validrange[0] = vlistptr->vars[varID].validrange[0];
    validrange[1] = vlistptr->vars[varID].validrange[1];
    }

return (int)vlistptr->vars[varID].lvalidrange;
}


void vlistDefVarValidrange(int vlistID, int varID, const double *validrange)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

vlistptr->vars[varID].validrange[0] = validrange[0];
vlistptr->vars[varID].validrange[1] = validrange[1];
vlistptr->vars[varID].lvalidrange = true;
reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
}


double vlistInqVarScalefactor(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].scalefactor;
}


double vlistInqVarAddoffset(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].addoffset;
}


void vlistDefVarScalefactor(int vlistID, int varID, double scalefactor)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( IS_NOT_EQUAL(vlistptr->vars[varID].scalefactor, scalefactor) )
    {
    vlistptr->vars[varID].scalefactor = scalefactor;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistDefVarAddoffset(int vlistID, int varID, double addoffset)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( IS_NOT_EQUAL(vlistptr->vars[varID].addoffset, addoffset))
    {
    vlistptr->vars[varID].addoffset = addoffset;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistDefVarTimetype(int vlistID, int varID, int timetype)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
if (vlistptr->vars[varID].timetype != timetype)
    {
    vlistptr->vars[varID].timetype = timetype;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarTimetype(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
return vlistptr->vars[varID].timetype;
}


void vlistDefVarTsteptype(int vlistID, int varID, int tsteptype)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
if (vlistptr->vars[varID].tsteptype != tsteptype)
    {
    vlistptr->vars[varID].tsteptype = tsteptype;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarTsteptype(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
return vlistptr->vars[varID].tsteptype;
}


void vlistDefVarTimave(int vlistID, int varID, int timave)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
if (vlistptr->vars[varID].timave != timave)
    {
    vlistptr->vars[varID].timave = timave;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarTimave(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
return vlistptr->vars[varID].timave;
}


void vlistDestroyVarName(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
if ( vlistptr->vars[varID].name )
    {
    Free(vlistptr->vars[varID].name);
    vlistptr->vars[varID].name = NULL;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistDestroyVarLongname(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( vlistptr->vars[varID].longname )
    {
    Free(vlistptr->vars[varID].longname);
    vlistptr->vars[varID].longname = NULL;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistDestroyVarStdname(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( vlistptr->vars[varID].stdname )
    {
    Free(vlistptr->vars[varID].stdname);
    vlistptr->vars[varID].stdname = NULL;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


void vlistDestroyVarUnits(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if ( vlistptr->vars[varID].units )
    {
    Free(vlistptr->vars[varID].units);
    vlistptr->vars[varID].units = NULL;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarMissvalUsed(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
return (int)vlistptr->vars[varID].missvalused;
}


void vlistDefFlag(int vlistID, int varID, int levID, int flag)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

levinfo_t li = DEFAULT_LEVINFO(levID);
if (vlistptr->vars[varID].levinfo)
    ;
else if (flag != li.flag)
    cdiVlistCreateVarLevInfo(vlistptr, varID);
else
    return;

vlistptr->vars[varID].levinfo[levID].flag = flag;

vlistptr->vars[varID].flag = 0;

int nlevs = zaxisInqSize(vlistptr->vars[varID].zaxisID);
for ( int levelID = 0; levelID < nlevs; levelID++ )
    {
    if ( vlistptr->vars[varID].levinfo[levelID].flag )
        {
        vlistptr->vars[varID].flag = 1;
        break;
        }
    }

reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
}


int vlistInqFlag(int vlistID, int varID, int levID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if (vlistptr->vars[varID].levinfo)
    return vlistptr->vars[varID].levinfo[levID].flag;
else
    {
    levinfo_t li = DEFAULT_LEVINFO(levID);
    return li.flag;
    }
}


int vlistFindVar(int vlistID, int fvarID)
{
int varID;
vlist_t *vlistptr = vlist_to_pointer(vlistID);

for ( varID = 0; varID < vlistptr->nvars; varID++ )
    {
    if ( vlistptr->vars[varID].fvarID == fvarID ) break;
    }

if ( varID == vlistptr->nvars )
    {
    varID = -1;
    Message("varID not found for fvarID %d in vlistID %d!", fvarID, vlistID);
    }

return varID;
}


int vlistFindLevel(int vlistID, int fvarID, int flevelID)
{
int levelID = -1;
vlist_t *vlistptr = vlist_to_pointer(vlistID);

int varID = vlistFindVar(vlistID, fvarID);

if ( varID != -1 )
    {
    int nlevs = zaxisInqSize(vlistptr->vars[varID].zaxisID);
    for ( levelID = 0; levelID < nlevs; levelID++ )
        {
        if ( vlistptr->vars[varID].levinfo[levelID].flevelID == flevelID ) break;
        }

    if ( levelID == nlevs )
        {
        levelID = -1;
        Message("levelID not found for fvarID %d and levelID %d in vlistID %d!",
                fvarID, flevelID, vlistID);
        }
    }

return levelID;
}


int vlistMergedVar(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);
return vlistptr->vars[varID].mvarID;
}


int vlistMergedLevel(int vlistID, int varID, int levelID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if (vlistptr->vars[varID].levinfo)
    return vlistptr->vars[varID].levinfo[levelID].mlevelID;
else
    {
    levinfo_t li = DEFAULT_LEVINFO(levelID);
    return li.mlevelID;
    }
}


void vlistDefIndex(int vlistID, int varID, int levelID, int index)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

levinfo_t li = DEFAULT_LEVINFO(levelID);
if (vlistptr->vars[varID].levinfo)
    ;
else if (index != li.index)
    cdiVlistCreateVarLevInfo(vlistptr, varID);
else
    return;
vlistptr->vars[varID].levinfo[levelID].index = index;
reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
}


int vlistInqIndex(int vlistID, int varID, int levelID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

if (vlistptr->vars[varID].levinfo)
    return vlistptr->vars[varID].levinfo[levelID].index;
else
    {
    levinfo_t li = DEFAULT_LEVINFO(levelID);
    return li.index;
    }
}


void vlistChangeVarZaxis(int vlistID, int varID, int zaxisID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

int nlevs1 = zaxisInqSize(vlistptr->vars[varID].zaxisID);
int nlevs2 = zaxisInqSize(zaxisID);

if ( nlevs1 != nlevs2 ) Error("Number of levels must not change!");

int nvars = vlistptr->nvars;
int found = 0;
int oldZaxisID = vlistptr->vars[varID].zaxisID;
for ( int i = 0; i < varID; ++i)
    found |= (vlistptr->vars[i].zaxisID == oldZaxisID);
for ( int i = varID + 1; i < nvars; ++i)
    found |= (vlistptr->vars[i].zaxisID == oldZaxisID);

if (found)
    {
    int nzaxis = vlistptr->nzaxis;
    for (int i = 0; i < nzaxis; ++i)
        if (vlistptr->zaxisIDs[i] == oldZaxisID )
        vlistptr->zaxisIDs[i] = zaxisID;
    }
else
    vlistAdd2ZaxisIDs(vlistptr, zaxisID);

vlistptr->vars[varID].zaxisID = zaxisID;
reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
}


void vlistChangeVarGrid(int vlistID, int varID, int gridID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

int nvars = vlistptr->nvars;
int index;
for ( index = 0; index < nvars; index++ )
    if ( index != varID )
    if ( vlistptr->vars[index].gridID == vlistptr->vars[varID].gridID ) break;

if ( index == nvars )
    {
    for ( index = 0; index < vlistptr->ngrids; index++ )
        if ( vlistptr->gridIDs[index] == vlistptr->vars[varID].gridID )
        vlistptr->gridIDs[index] = gridID;
    }
else
    vlistAdd2GridIDs(vlistptr, gridID);

vlistptr->vars[varID].gridID = gridID;
reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
}


void vlistDefVarCompType(int vlistID, int varID, int comptype)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if (vlistptr->vars[varID].comptype != comptype)
    {
    vlistptr->vars[varID].comptype = comptype;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarCompType(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].comptype;
}


void vlistDefVarCompLevel(int vlistID, int varID, int complevel)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if (vlistptr->vars[varID].complevel != complevel)
    {
    vlistptr->vars[varID].complevel = complevel;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarCompLevel(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].complevel;
}


void  vlistDefVarChunkType(int vlistID, int varID, int chunktype)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if (vlistptr->vars[varID].chunktype != chunktype)
    {
    vlistptr->vars[varID].chunktype = chunktype;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarChunkType(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].chunktype;
}

static
int vlistEncodeXyz(int (*dimorder)[3])
{
return (*dimorder)[0]*100 + (*dimorder)[1]*10 + (*dimorder)[2];
}


static
void vlistDecodeXyz(int xyz, int (*outDimorder)[3])
{
(*outDimorder)[0] = xyz/100, xyz -= (*outDimorder)[0]*100;
(*outDimorder)[1] = xyz/10, xyz -= (*outDimorder)[1]*10;
(*outDimorder)[2] = xyz;
}


void  vlistDefVarXYZ(int vlistID, int varID, int xyz)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

if ( xyz == 3 ) xyz = 321;


{
    int dimorder[3];
    vlistDecodeXyz(xyz, &dimorder);
    int dimx = 0, dimy = 0, dimz = 0;
    for ( int id = 0; id < 3; ++id )
    {
        switch ( dimorder[id] )
        {
            case 1: dimx++; break;
            case 2: dimy++; break;
            case 3: dimz++; break;
            default: dimorder[id] = 0; break;
        }
    }
    if ( dimz > 1 || dimy > 1 || dimx > 1 ) xyz = 321;
    else
    {
        if ( dimz == 0 ) for ( int id = 0; id < 3; ++id ) if ( dimorder[id] == 0 ) {dimorder[id] = 3; break;}
        if ( dimy == 0 ) for ( int id = 0; id < 3; ++id ) if ( dimorder[id] == 0 ) {dimorder[id] = 2; break;}
        if ( dimx == 0 ) for ( int id = 0; id < 3; ++id ) if ( dimorder[id] == 0 ) {dimorder[id] = 1; break;}
        xyz = vlistEncodeXyz(&dimorder);
    }
}

assert(xyz == 123 || xyz == 312 || xyz == 231 || xyz == 321 || xyz == 132 || xyz == 213);

vlistptr->vars[varID].xyz = xyz;
reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
}


void vlistInqVarDimorder(int vlistID, int varID, int (*outDimorder)[3])
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

vlistDecodeXyz(vlistptr->vars[varID].xyz, outDimorder);
}


int vlistInqVarXYZ(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].xyz;
}


void vlistDefVarIntKey(int vlistID, int varID, const char *name, int value)
{
#ifdef HAVE_LIBGRIB_API
vlist_t *vlistptr = vlist_to_pointer(vlistID);
if (vlistptr == NULL)  Error("Internal error!");
int idx;

if ( vlistptr->immutable )
    Error("vlistDefVarIntKey() was called on an immutable vlist object (vlistID = %d)\n"
        "Either call vlistDefVarIntKey() before passing the vlist object to streamDefVlist(),\n"
        "or use the stream-internal vlist by calling streamInqVlist().", vlistID);

for ( idx=0; idx<vlistptr->vars[varID].opt_grib_nentries; idx++)
    if ( (strcmp(name, vlistptr->vars[varID].opt_grib_kvpair[idx].keyword) == 0 ) &&
        (vlistptr->vars[varID].opt_grib_kvpair[idx].data_type == t_int) )  break;

if ( idx < vlistptr->vars[varID].opt_grib_nentries )
    {
    vlistptr->vars[varID].opt_grib_kvpair[idx].int_val = value;
    vlistptr->vars[varID].opt_grib_kvpair[idx].update  = true;
    }
else
    {
    resize_opt_grib_entries(&vlistptr->vars[varID], vlistptr->vars[varID].opt_grib_nentries+1);
    vlistptr->vars[varID].opt_grib_nentries += 1;
    idx = vlistptr->vars[varID].opt_grib_nentries -1;
    vlistptr->vars[varID].opt_grib_kvpair[idx].data_type   = t_int;
    vlistptr->vars[varID].opt_grib_kvpair[idx].int_val     = value;
    vlistptr->vars[varID].opt_grib_kvpair[idx].update      = true;
    if ( name )
        vlistptr->vars[varID].opt_grib_kvpair[idx].keyword = strdupx(name);
    else
        Error("Internal error, name undefined!");
    }

if ( CDI_Debug )
    {
    Message("define additional GRIB2 key \"%s\" (integer): %d", name, value);
    Message("total list of registered, additional GRIB2 keys (total: %d):",
            vlistptr->vars[varID].opt_grib_nentries);
    for ( idx=0; idx<vlistptr->vars[varID].opt_grib_nentries; idx++)
        if (vlistptr->vars[varID].opt_grib_kvpair[idx].data_type == t_int)
        Message("%s -> integer %d",
                vlistptr->vars[varID].opt_grib_kvpair[idx].keyword,
                vlistptr->vars[varID].opt_grib_kvpair[idx].int_val);
        else if (vlistptr->vars[varID].opt_grib_kvpair[idx].data_type == t_double)
        Message("%s -> double %d",
                vlistptr->vars[varID].opt_grib_kvpair[idx].keyword,
                vlistptr->vars[varID].opt_grib_kvpair[idx].dbl_val);
        else
        Message("%s -> unknown", vlistptr->vars[varID].opt_grib_kvpair[idx].keyword);
    }

reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
#else
(void)vlistID;
(void)varID;
(void)name;
(void)value;
#endif
}


void vlistDefVarDblKey(int vlistID, int varID, const char *name, double value)
{
#ifdef HAVE_LIBGRIB_API
vlist_t *vlistptr = vlist_to_pointer(vlistID);
if (vlistptr == NULL)  Error("Internal error!");
int idx;

if ( vlistptr->immutable )
    Error("vlistDefVarDblKey() was called on an immutable vlist object (vlistID = %d)\n"
        "Either call vlistDefVarIntKey() before passing the vlist object to streamDefVlist(),\n"
        "or use the stream-internal vlist by calling streamInqVlist().", vlistID);

for ( idx=0; idx<vlistptr->vars[varID].opt_grib_nentries; idx++)
    if ( (strcmp(name, vlistptr->vars[varID].opt_grib_kvpair[idx].keyword) == 0 ) &&
        (vlistptr->vars[varID].opt_grib_kvpair[idx].data_type == t_double) )  break;

if ( idx < vlistptr->vars[varID].opt_grib_nentries )
    {
    vlistptr->vars[varID].opt_grib_kvpair[idx].dbl_val = value;
    vlistptr->vars[varID].opt_grib_kvpair[idx].update  = true;
    }
else
    {
    resize_opt_grib_entries(&vlistptr->vars[varID], vlistptr->vars[varID].opt_grib_nentries+1);
    vlistptr->vars[varID].opt_grib_nentries += 1;
    idx = vlistptr->vars[varID].opt_grib_nentries - 1;
    vlistptr->vars[varID].opt_grib_kvpair[idx].data_type = t_double;
    vlistptr->vars[varID].opt_grib_kvpair[idx].dbl_val   = value;
    vlistptr->vars[varID].opt_grib_kvpair[idx].update    = true;
    if ( name )
        vlistptr->vars[varID].opt_grib_kvpair[idx].keyword = strdupx(name);
    else
        Error("Internal error, name undefined!");
    }

if ( CDI_Debug )
    {
    Message("define additional GRIB2 key \"%s\" (double): %d", name, value);
    Message("total list of registered, additional GRIB2 keys (total: %d):",
            vlistptr->vars[varID].opt_grib_nentries);
    for ( idx=0; idx<vlistptr->vars[varID].opt_grib_nentries; idx++)
        if (vlistptr->vars[varID].opt_grib_kvpair[idx].data_type == t_int)
        Message("%s -> integer %d",
                vlistptr->vars[varID].opt_grib_kvpair[idx].keyword,
                vlistptr->vars[varID].opt_grib_kvpair[idx].int_val);
        else if (vlistptr->vars[varID].opt_grib_kvpair[idx].data_type == t_double)
        Message("%s -> double %d",
                vlistptr->vars[varID].opt_grib_kvpair[idx].keyword,
                vlistptr->vars[varID].opt_grib_kvpair[idx].dbl_val);
        else
        Message("%s -> unknown", vlistptr->vars[varID].opt_grib_kvpair[idx].keyword);
    }

reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
#else
(void)vlistID;
(void)varID;
(void)name;
(void)value;
#endif
}



void cdiClearAdditionalKeys()
{
#ifdef HAVE_LIBGRIB_API
for (int i=0; i<cdiNAdditionalGRIBKeys; i++)  Free(cdiAdditionalGRIBKeys[i]);
cdiNAdditionalGRIBKeys = 0;
#endif
}


void cdiDefAdditionalKey(const char *name)
{
#ifdef HAVE_LIBGRIB_API
int idx = cdiNAdditionalGRIBKeys;
cdiNAdditionalGRIBKeys++;
if ( idx >= MAX_OPT_GRIB_ENTRIES ) Error("Too many additional keywords!");
if ( name )
    cdiAdditionalGRIBKeys[idx] = strdupx(name);
else
    Error("Internal error!");
#else
(void)name;
#endif
}


int vlistHasVarKey(int vlistID, int varID, const char* name)
{
#ifdef HAVE_LIBGRIB_API

vlist_t *vlistptr = vlist_to_pointer(vlistID);

for (int i=0; i<vlistptr->vars[varID].opt_grib_nentries; i++)
    {
    if ( strcmp(name, vlistptr->vars[varID].opt_grib_kvpair[i].keyword) == 0 )
        return 1;
    }
#else
(void)vlistID;
(void)varID;
(void)name;
#endif
return 0;
}


double vlistInqVarDblKey(int vlistID, int varID, const char* name)
{
double value = 0;
#ifdef HAVE_LIBGRIB_API

vlist_t *vlistptr = vlist_to_pointer(vlistID);

for (int i=0; i<vlistptr->vars[varID].opt_grib_nentries; i++)
    {
    int isub = subtypeInqActiveIndex(vlistptr->vars[varID].subtypeID);
    if ( (strcmp(name, vlistptr->vars[varID].opt_grib_kvpair[i].keyword) == 0 ) &&
        (vlistptr->vars[varID].opt_grib_kvpair[i].data_type == t_double)       &&
        (vlistptr->vars[varID].opt_grib_kvpair[i].subtype_index == isub) )
        return vlistptr->vars[varID].opt_grib_kvpair[i].dbl_val;
    }
#else
(void)vlistID;
(void)varID;
(void)name;
#endif
return value;
}



int vlistInqVarIntKey(int vlistID, int varID, const char* name)
{
long value = 0;
#ifdef HAVE_LIBGRIB_API

vlist_t *vlistptr = vlist_to_pointer(vlistID);

for (int i=0; i<vlistptr->vars[varID].opt_grib_nentries; i++)
    {
    int isub = subtypeInqActiveIndex(vlistptr->vars[varID].subtypeID);
    if ( (strcmp(name, vlistptr->vars[varID].opt_grib_kvpair[i].keyword) == 0 ) &&
        (vlistptr->vars[varID].opt_grib_kvpair[i].data_type == t_int)          &&
        (vlistptr->vars[varID].opt_grib_kvpair[i].subtype_index == isub) )
        return vlistptr->vars[varID].opt_grib_kvpair[i].int_val;
    }

#else
(void)vlistID;
(void)varID;
(void)name;
#endif
return (int) value;
}


void vlistDefVarIOrank(int vlistID, int varID, int iorank)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID ( __func__, vlistID, varID );

if (vlistptr->vars[varID].iorank != iorank)
    {
    vlistptr->vars[varID].iorank = iorank;
    reshSetStatus(vlistID, &vlistOps, RESH_DESYNC_IN_USE);
    }
}


int vlistInqVarIOrank(int vlistID, int varID)
{
vlist_t *vlistptr = vlist_to_pointer(vlistID);

vlistCheckVarID(__func__, vlistID, varID);

return vlistptr->vars[varID].iorank;
}


int vlistVarCompare(vlist_t *a, int varIDA, vlist_t *b, int varIDB)
{
xassert(a && b
        && varIDA >= 0 && varIDA < a->nvars
        && varIDB >= 0 && varIDB < b->nvars);
var_t *pva = a->vars + varIDA, *pvb = b->vars + varIDB;
#define FCMP(f) ((pva->f) != (pvb->f))
#define FCMPFLT(f) (IS_NOT_EQUAL((pva->f), (pvb->f)))
#define FCMPSTR(fs) ((pva->fs) != (pvb->fs) && strcmp((pva->fs), (pvb->fs)))
#define FCMP2(f) (namespaceResHDecode(pva->f).idx       \
                != namespaceResHDecode(pvb->f).idx)
int diff = FCMP(fvarID) | FCMP(mvarID) | FCMP(flag) | FCMP(param)
    | FCMP(datatype) | FCMP(timetype) | FCMP(tsteptype) | FCMP(timave)
    | FCMP(chunktype) | FCMP(xyz) | FCMP2(gridID) | FCMP2(zaxisID)
    | FCMP2(instID) | FCMP2(modelID) | FCMP2(tableID) | FCMP(missvalused)
    | FCMPFLT(missval) | FCMPFLT(addoffset) | FCMPFLT(scalefactor) | FCMPSTR(name)
    | FCMPSTR(longname) | FCMPSTR(stdname) | FCMPSTR(units) | FCMPSTR(extra)
    | FCMP(comptype) | FCMP(complevel) | FCMP(lvalidrange)
    | FCMPFLT(validrange[0]) | FCMPFLT(validrange[1]);
#undef FCMP
#undef FCMPFLT
#undef FCMPSTR
#undef FCMP2
if ((diff |= ((pva->levinfo == NULL) ^ (pvb->levinfo == NULL))))
    return 1;
if (pva->levinfo)
    {
    int zaxisID = pva->zaxisID;
    size_t nlevs = (size_t)zaxisInqSize(zaxisID);
    diff |= (memcmp(pva->levinfo, pvb->levinfo, sizeof (levinfo_t) * nlevs)
            != 0);
    if (diff)
        return 1;
    }

size_t natts = a->vars[varIDA].atts.nelems;
if (natts != b->vars[varIDB].atts.nelems)
    return 1;
for (size_t attID = 0; attID < natts; ++attID)
    diff |= cdi_att_compare(a, varIDA, b, varIDB, (int)attID);

size_t nkeys = a->vars[varIDA].keys.nelems;
if (nkeys != b->vars[varIDB].keys.nelems)
    return 1;
for (size_t keyID = 0; keyID < nkeys; ++keyID)
    diff |= vlist_key_compare(a, varIDA, b, varIDB, (int)keyID);

return diff;
}


enum {
VLISTVAR_PACK_INT_IDX_FLAG,
VLISTVAR_PACK_INT_IDX_GRIDID,
VLISTVAR_PACK_INT_IDX_ZAXISID,
VLISTVAR_PACK_INT_IDX_TIMETYPE,
VLISTVAR_PACK_INT_IDX_NAMESZ,
VLISTVAR_PACK_INT_IDX_LONGNAMESZ,
VLISTVAR_PACK_INT_IDX_STDNAMESZ,
VLISTVAR_PACK_INT_IDX_UNITSSZ,
VLISTVAR_PACK_INT_IDX_DATATYPE,
VLISTVAR_PACK_INT_IDX_PARAM,
VLISTVAR_PACK_INT_IDX_INSTID,
VLISTVAR_PACK_INT_IDX_MODELID,
VLISTVAR_PACK_INT_IDX_TABLEID,
VLISTVAR_PACK_INT_IDX_TIMAVE,
VLISTVAR_PACK_INT_IDX_MISSVALUSED,
VLISTVAR_PACK_INT_IDX_COMPTYPE,
VLISTVAR_PACK_INT_IDX_COMPLEVEL,
VLISTVAR_PACK_INT_IDX_NLEVS,
VLISTVAR_PACK_INT_IDX_IORANK,
VLISTVAR_PACK_INT_IDX_EXTRALEN,
vlistvarNint
};

enum {
vlistvar_ndbls = 3,
};

int vlistVarGetPackSize(vlist_t *p, int varID, void *context)
{
var_t *var = p->vars + varID;
int varsize = serializeGetSize(vlistvarNint, CDI_DATATYPE_INT, context)
    + serializeGetSize(vlistvar_ndbls, CDI_DATATYPE_FLT64, context);
if (var->name)
    varsize += serializeGetSize((int)strlen(var->name), CDI_DATATYPE_TXT, context);
if (var->longname)
    varsize += serializeGetSize((int)strlen(var->longname), CDI_DATATYPE_TXT, context);
if (var->stdname)
    varsize += serializeGetSize((int)strlen(var->stdname), CDI_DATATYPE_TXT, context);
if (var->units)
    varsize += serializeGetSize((int)strlen(var->units), CDI_DATATYPE_TXT, context);
if (var->extra)
    varsize += serializeGetSize((int)strlen(var->extra), CDI_DATATYPE_TXT, context);
varsize += serializeGetSize(4 * zaxisInqSize(var->zaxisID),
                            CDI_DATATYPE_INT, context);
varsize += cdiAttsGetSize(p, varID, context);
return varsize;
}

void vlistVarPack(vlist_t *p, int varID, char * buf, int size, int *position,
                void *context)
{
double dtempbuf[vlistvar_ndbls];
var_t *var = p->vars + varID;
int tempbuf[vlistvarNint], namesz, longnamesz, stdnamesz, unitssz, extralen;

tempbuf[VLISTVAR_PACK_INT_IDX_FLAG] = var->flag;
tempbuf[VLISTVAR_PACK_INT_IDX_GRIDID] = var->gridID;
tempbuf[VLISTVAR_PACK_INT_IDX_ZAXISID] = var->zaxisID;
tempbuf[VLISTVAR_PACK_INT_IDX_TIMETYPE] = var->timetype;
tempbuf[VLISTVAR_PACK_INT_IDX_NAMESZ] = namesz = var->name?(int)strlen(var->name):0;
tempbuf[VLISTVAR_PACK_INT_IDX_LONGNAMESZ] = longnamesz = var->longname?(int)strlen(var->longname):0;
tempbuf[VLISTVAR_PACK_INT_IDX_STDNAMESZ] = stdnamesz = var->stdname?(int)strlen(var->stdname):0;
tempbuf[VLISTVAR_PACK_INT_IDX_UNITSSZ] = unitssz = var->units?(int)strlen(var->units):0;
tempbuf[VLISTVAR_PACK_INT_IDX_DATATYPE] = var->datatype;
tempbuf[VLISTVAR_PACK_INT_IDX_PARAM] = var->param;
tempbuf[VLISTVAR_PACK_INT_IDX_INSTID] = var->instID;
tempbuf[VLISTVAR_PACK_INT_IDX_MODELID] = var->modelID;
tempbuf[VLISTVAR_PACK_INT_IDX_TABLEID] = var->tableID;
tempbuf[VLISTVAR_PACK_INT_IDX_TIMAVE] = var->timave;
tempbuf[VLISTVAR_PACK_INT_IDX_MISSVALUSED] = (int)var->missvalused;
tempbuf[VLISTVAR_PACK_INT_IDX_COMPTYPE] = var->comptype;
tempbuf[VLISTVAR_PACK_INT_IDX_COMPLEVEL] = var->complevel;
int nlevs = var->levinfo ? zaxisInqSize(var->zaxisID) : 0;
tempbuf[VLISTVAR_PACK_INT_IDX_NLEVS] = nlevs;
tempbuf[VLISTVAR_PACK_INT_IDX_IORANK] = var->iorank;
tempbuf[VLISTVAR_PACK_INT_IDX_EXTRALEN] = extralen = var->extra?(int)strlen(var->extra):0;
dtempbuf[0] = var->missval;
dtempbuf[1] = var->scalefactor;
dtempbuf[2] = var->addoffset;
serializePack(tempbuf, vlistvarNint, CDI_DATATYPE_INT, buf, size, position, context);
serializePack(dtempbuf, vlistvar_ndbls, CDI_DATATYPE_FLT64,buf, size, position, context);
if (namesz)
    serializePack(var->name, namesz, CDI_DATATYPE_TXT, buf, size, position, context);
if (longnamesz)
    serializePack(var->longname, longnamesz, CDI_DATATYPE_TXT, buf, size, position, context);
if (stdnamesz)
    serializePack(var->stdname, stdnamesz, CDI_DATATYPE_TXT, buf, size, position, context);
if (unitssz)
    serializePack(var->units, unitssz, CDI_DATATYPE_TXT, buf, size, position, context);
if (extralen)
    serializePack(var->extra, extralen, CDI_DATATYPE_TXT, buf, size, position, context);
if (nlevs)
    {
    int *levbuf = (int*) malloc(nlevs*sizeof(int));
    for (int levID = 0; levID < nlevs; ++levID) levbuf[levID] = var->levinfo[levID].flag;
    serializePack(levbuf, nlevs, CDI_DATATYPE_INT, buf, size, position, context);
    for (int levID = 0; levID < nlevs; ++levID) levbuf[levID] = var->levinfo[levID].index;
    serializePack(levbuf, nlevs, CDI_DATATYPE_INT, buf, size, position, context);
    for (int levID = 0; levID < nlevs; ++levID) levbuf[levID] = var->levinfo[levID].mlevelID;
    serializePack(levbuf, nlevs, CDI_DATATYPE_INT, buf, size, position, context);
    for (int levID = 0; levID < nlevs; ++levID) levbuf[levID] = var->levinfo[levID].flevelID;
    free(levbuf);
    }
cdiAttsPack(p, varID, buf, size, position, context);
}

static inline int
imax(int a, int b)
{
return a>=b?a:b;
}


void vlistVarUnpack(int vlistID, char * buf, int size, int *position,
                    int originNamespace, void *context)
{
double dtempbuf[vlistvar_ndbls];
int tempbuf[vlistvarNint];
char *varname = NULL;
vlist_t *vlistptr = vlist_to_pointer(vlistID);
serializeUnpack(buf, size, position, tempbuf, vlistvarNint, CDI_DATATYPE_INT, context);
serializeUnpack(buf, size, position, dtempbuf, vlistvar_ndbls, CDI_DATATYPE_FLT64, context);





int newvar = vlistDefVar ( vlistID,
                        namespaceAdaptKey ( tempbuf[VLISTVAR_PACK_INT_IDX_GRIDID], originNamespace ),
                        namespaceAdaptKey ( tempbuf[VLISTVAR_PACK_INT_IDX_ZAXISID], originNamespace ),
                        tempbuf[VLISTVAR_PACK_INT_IDX_TIMETYPE]);
if (tempbuf[VLISTVAR_PACK_INT_IDX_NAMESZ] || tempbuf[VLISTVAR_PACK_INT_IDX_LONGNAMESZ] ||
    tempbuf[VLISTVAR_PACK_INT_IDX_STDNAMESZ] || tempbuf[VLISTVAR_PACK_INT_IDX_UNITSSZ] ||
    tempbuf[VLISTVAR_PACK_INT_IDX_EXTRALEN])
    varname = (char *)Malloc((size_t)imax(imax(imax(imax(tempbuf[VLISTVAR_PACK_INT_IDX_NAMESZ],
                                                        tempbuf[VLISTVAR_PACK_INT_IDX_LONGNAMESZ]),
                                                    tempbuf[VLISTVAR_PACK_INT_IDX_STDNAMESZ]),
                                            tempbuf[VLISTVAR_PACK_INT_IDX_UNITSSZ]), tempbuf[VLISTVAR_PACK_INT_IDX_EXTRALEN]) + 1);
if (tempbuf[VLISTVAR_PACK_INT_IDX_NAMESZ])
{
    serializeUnpack(buf, size, position,
                    varname, tempbuf[VLISTVAR_PACK_INT_IDX_NAMESZ], CDI_DATATYPE_TXT, context);
    varname[tempbuf[VLISTVAR_PACK_INT_IDX_NAMESZ]] = '\0';
    vlistDefVarName(vlistID, newvar, varname);
}
if (tempbuf[VLISTVAR_PACK_INT_IDX_LONGNAMESZ])
{
    serializeUnpack(buf, size, position,
                    varname, tempbuf[VLISTVAR_PACK_INT_IDX_LONGNAMESZ], CDI_DATATYPE_TXT, context);
    varname[tempbuf[VLISTVAR_PACK_INT_IDX_LONGNAMESZ]] = '\0';
    vlistDefVarLongname(vlistID, newvar, varname);
}
if (tempbuf[VLISTVAR_PACK_INT_IDX_STDNAMESZ])
{
    serializeUnpack(buf, size, position,
                    varname, tempbuf[VLISTVAR_PACK_INT_IDX_STDNAMESZ], CDI_DATATYPE_TXT, context);
    varname[tempbuf[VLISTVAR_PACK_INT_IDX_STDNAMESZ]] = '\0';
    vlistDefVarStdname(vlistID, newvar, varname);
}
if (tempbuf[VLISTVAR_PACK_INT_IDX_UNITSSZ])
{
    serializeUnpack(buf, size, position,
                    varname, tempbuf[VLISTVAR_PACK_INT_IDX_UNITSSZ], CDI_DATATYPE_TXT, context);
    varname[tempbuf[VLISTVAR_PACK_INT_IDX_UNITSSZ]] = '\0';
    vlistDefVarUnits(vlistID, newvar, varname);
}
if (tempbuf[VLISTVAR_PACK_INT_IDX_EXTRALEN])
    {
    serializeUnpack(buf, size, position,
                    varname, tempbuf[VLISTVAR_PACK_INT_IDX_EXTRALEN], CDI_DATATYPE_TXT, context);
    varname[tempbuf[VLISTVAR_PACK_INT_IDX_EXTRALEN]] = '\0';
    vlistDefVarExtra(vlistID, newvar, varname);
    }
Free(varname);
vlistDefVarDatatype(vlistID, newvar, tempbuf[VLISTVAR_PACK_INT_IDX_DATATYPE]);
vlistDefVarInstitut ( vlistID, newvar,
                        namespaceAdaptKey ( tempbuf[VLISTVAR_PACK_INT_IDX_INSTID], originNamespace ));
vlistDefVarModel ( vlistID, newvar,
                    namespaceAdaptKey ( tempbuf[VLISTVAR_PACK_INT_IDX_MODELID], originNamespace ));
vlistDefVarTable(vlistID, newvar, tempbuf[VLISTVAR_PACK_INT_IDX_TABLEID]);

vlistDefVarParam(vlistID, newvar, tempbuf[VLISTVAR_PACK_INT_IDX_PARAM]);
vlistDefVarTimave(vlistID, newvar, tempbuf[VLISTVAR_PACK_INT_IDX_TIMAVE]);
if (tempbuf[VLISTVAR_PACK_INT_IDX_MISSVALUSED])
    vlistDefVarMissval(vlistID, newvar, dtempbuf[0]);
vlistDefVarScalefactor(vlistID, newvar, dtempbuf[1]);
vlistDefVarAddoffset(vlistID, newvar, dtempbuf[2]);
vlistDefVarCompType(vlistID, newvar, tempbuf[VLISTVAR_PACK_INT_IDX_COMPTYPE]);
vlistDefVarCompLevel(vlistID, newvar, tempbuf[VLISTVAR_PACK_INT_IDX_COMPLEVEL]);
int nlevs = tempbuf[VLISTVAR_PACK_INT_IDX_NLEVS];
if (nlevs)
    {
    var_t *var = vlistptr->vars + newvar;
    int i, flagSetLev = 0;
    cdiVlistCreateVarLevInfo(vlistptr, newvar);

    int *levbuf = (int*) malloc(nlevs*sizeof(int));
    serializeUnpack(buf, size, position, levbuf, nlevs, CDI_DATATYPE_INT, context);
    for (i = 0; i < nlevs; ++i) vlistDefFlag(vlistID, newvar, i, levbuf[i]);
    for (i = 0; i < nlevs; ++i) if (levbuf[i] == tempbuf[0]) flagSetLev = i;
    vlistDefFlag(vlistID, newvar, flagSetLev, levbuf[flagSetLev]);
    serializeUnpack(buf, size, position, levbuf, nlevs, CDI_DATATYPE_INT, context);
    for (i = 0; i < nlevs; ++i) vlistDefIndex(vlistID, newvar, i, levbuf[i]);
    serializeUnpack(buf, size, position, levbuf, nlevs, CDI_DATATYPE_INT, context);
    for (i = 0; i < nlevs; ++i) var->levinfo[i].mlevelID = levbuf[i];
    serializeUnpack(buf, size, position, levbuf, nlevs, CDI_DATATYPE_INT, context);
    for (i = 0; i < nlevs; ++i) var->levinfo[i].flevelID = levbuf[i];
    free(levbuf);
    }
vlistDefVarIOrank(vlistID, newvar, tempbuf[VLISTVAR_PACK_INT_IDX_IORANK]);
cdiAttsUnpack(vlistID, newvar, buf, size, position, context);
}

#include <stddef.h>
#include <string.h>
#include <ctype.h>

#define MAX_TABLE  256
#define MAX_PARS   1024

typedef struct
{
bool   used;
int    npars;
int    modelID;
int    number;
char  *name;
param_type *pars;
}
paramtab_type;

static paramtab_type parTable[MAX_TABLE];
static int  parTableSize = MAX_TABLE;
static int  parTableNum  = 0;
static int  ParTableInit = 0;

static char *tablePath = NULL;

static void tableDefModelID(int tableID, int modelID);
static void tableDefNum(int tableID, int tablenum);

static
void tableDefEntry(int tableID, int id, int ltype, const char *name,
                const char *longname, const char *units)
{
if ( tableID >= 0 && tableID < MAX_TABLE && parTable[tableID].used) { } else
    Error("Invalid table ID %d", tableID);

int item = parTable[tableID].npars++;
parTable[tableID].pars[item].id       = id;
parTable[tableID].pars[item].ltype    = ltype;
parTable[tableID].pars[item].dupflags = 0;
parTable[tableID].pars[item].name     = NULL;
parTable[tableID].pars[item].longname = NULL;
parTable[tableID].pars[item].units    = NULL;

if ( name && name[0] )
    {
    parTable[tableID].pars[item].name     = strdupx(name);
    parTable[tableID].pars[item].dupflags |= TABLE_DUP_NAME;
    }
if ( longname && longname[0] )
    {
    parTable[tableID].pars[item].longname = strdupx(longname);
    parTable[tableID].pars[item].dupflags |= TABLE_DUP_LONGNAME;
    }
if ( units && units[0] )
    {
    parTable[tableID].pars[item].units    = strdupx(units);
    parTable[tableID].pars[item].dupflags |= TABLE_DUP_UNITS;
    }
}

static void tableLink(int tableID, const param_type *pars, int npars)
{
for ( int item = 0; item < npars; item++ )
    {
    parTable[tableID].pars[item].id       = pars[item].id;
    parTable[tableID].pars[item].ltype    = pars[item].ltype;
    parTable[tableID].pars[item].dupflags = 0;
    parTable[tableID].pars[item].name     = pars[item].name;
    parTable[tableID].pars[item].longname = pars[item].longname;
    parTable[tableID].pars[item].units    = pars[item].units;
    }

parTable[tableID].npars = npars;
}

static void parTableInitEntry(int tableID)
{
parTable[tableID].used    = false;
parTable[tableID].pars    = NULL;
parTable[tableID].npars   = 0;
parTable[tableID].modelID = CDI_UNDEFID;
parTable[tableID].number  = CDI_UNDEFID;
parTable[tableID].name    = NULL;
}

static void tableGetPath(void)
{
char *path = getenv("TABLEPATH");

if ( path ) tablePath = strdupx(path);

}

static void parTableFinalize(void)
{
for (int tableID = 0; tableID < MAX_TABLE; ++tableID)
    if (parTable[tableID].used)
    {
        int npars = parTable[tableID].npars;
        for (int item = 0; item < npars; ++item)
        {
            if (parTable[tableID].pars[item].dupflags & TABLE_DUP_NAME)
            Free((void *)parTable[tableID].pars[item].name);
            if (parTable[tableID].pars[item].dupflags & TABLE_DUP_LONGNAME)
            Free((void *)parTable[tableID].pars[item].longname);
            if (parTable[tableID].pars[item].dupflags & TABLE_DUP_UNITS)
            Free((void *)parTable[tableID].pars[item].units);
        }
        Free(parTable[tableID].pars);
        Free(parTable[tableID].name);
    }
}

static void parTableInit(void)
{
ParTableInit = 1;

atexit(parTableFinalize);
if ( cdiPartabIntern )
    tableDefault();

tableGetPath();
}

static int tableNewEntry()
{
int tableID = 0;
static int init = 0;

if ( ! init )
    {
    for ( tableID = 0; tableID < parTableSize; tableID++ )
        parTableInitEntry(tableID);
    init = 1;
    }


for ( tableID = 0; tableID < parTableSize; tableID++ )
    {
    if ( ! parTable[tableID].used ) break;
    }

if ( tableID == parTableSize )
    Error("no more entries!");

parTable[tableID].used = true;
parTableNum++;

return tableID;
}

static int
decodeForm1(char *pline, char *name, char *longname, char *units)
{
char *pstart, *pend;


strtol(pline, &pline, 10);
while ( isspace((int) *pline) ) pline++;

pstart = pline;
while ( ! (isspace((int) *pline) || *pline == 0) ) pline++;
size_t len = (size_t)(pline - pstart);
if ( len > 0 )
    {
    memcpy(name, pstart, len);
    name[len] = 0;
    }
else
    return 0;

if ( pline[0] == 0 ) return 0;



strtod(pline, &pline);

strtod(pline, &pline);

while ( isspace((int) *pline) ) pline++;

len = strlen(pline);
if ( len > 0 )
    {
    pstart = pline;
    pend = strrchr(pline, '[');
    if ( pend == pstart )
        len = 0;
    else
        {
        if ( pend )
            pend--;
        else
            pend = pstart + len;
        while ( isspace((int) *pend) ) pend--;
        len = (size_t)(pend - pstart + 1);
        }
    if ( len > 0 )
        {
        memcpy(longname, pstart, len);
        longname[len] = 0;
        }
    pstart = strrchr(pline, '[');
    if ( pstart )
        {
        pstart++;
        while ( isspace((int) *pstart) ) pstart++;
        pend = strchr(pstart, ']');
        if ( ! pend ) return 0;
        pend--;
        while ( isspace((int) *pend) ) pend--;
        len = (size_t)(pend - pstart + 1);
        if ( len > 0 )
            {
            memcpy(units, pstart, len);
            units[len] = 0;
            }
        }
    }

return 0;
}

static int
decodeForm2(char *pline, char *name, char *longname, char *units)
{

char *pend;

pline = strchr(pline, '|');
pline++;

while ( isspace((int) *pline) ) pline++;
if (*pline != '|')
    {
    pend = strchr(pline, '|');
    if ( ! pend )
        {
        pend = pline;
        while ( ! isspace((int) *pend) ) pend++;
        size_t len = (size_t)(pend - pline);
        if ( len > 0 )
            {
            memcpy(name, pline, len);
            name[len] = 0;
            }
        return 0;
        }
    else
        {
        pend--;
        while ( isspace((int) *pend) ) pend--;
        size_t len = (size_t)(pend - pline + 1);
        if ( len > 0 )
            {
            memcpy(name, pline, len);
            name[len] = 0;
            }
        }
    }
else
    name[0] = '\0';

pline = strchr(pline, '|');
pline++;
while ( isspace((int) *pline) ) pline++;
pend = strchr(pline, '|');
if ( !pend ) pend = strchr(pline, 0);
pend--;
while ( isspace((int) *pend) ) pend--;
{
    size_t len = (size_t)(pend - pline + 1);
    if ( len > 0 )
    {
        memcpy(longname, pline, len);
        longname[len] = 0;
    }
}

pline = strchr(pline, '|');
if ( pline )
    {
    pline++;
    while ( isspace((int) *pline) ) pline++;
    pend = strchr(pline, '|');
    if ( !pend ) pend = strchr(pline, 0);
    pend--;
    while ( isspace((int) *pend) ) pend--;
    ptrdiff_t len = pend - pline + 1;
    if ( len < 0 ) len = 0;
    memcpy(units, pline, (size_t)len);
    units[len] = 0;
    }

return 0;
}


int tableRead(const char *tablefile)
{
char line[1024], *pline;
int lnr = 0;
char name[256], longname[256], units[256];
int err;
int tableID = CDI_UNDEFID;

FILE *tablefp = fopen(tablefile, "r");
if ( tablefp == NULL ) return tableID;

char *tablename = (char* )strrchr(tablefile, '/');
if ( tablename == 0 ) tablename = (char *) tablefile;
else                  tablename++;

tableID = tableDef(-1, 0, tablename);

while ( fgets(line, 1023, tablefp) )
    {
    size_t len = strlen(line);
    if ( line[len-1] == '\n' ) line[len-1] = '\0';
    lnr++;
    int id      = CDI_UNDEFID;
    int ltype   = CDI_UNDEFID;
    name[0]     = 0;
    longname[0] = 0;
    units[0]    = 0;
    if ( line[0] == '#' ) continue;
    pline = line;

    len = strlen(pline);
    if ( len < 4 ) continue;
    while ( isspace((int) *pline) ) pline++;
    id = atoi(pline);

    if ( id == 0 ) continue;

    while ( isdigit((int) *pline) ) pline++;

    if ( *pline == ';' || *pline == ':' )
        {
        pline++;
        ltype = atoi(pline);
        while ( isdigit((int) *pline) ) pline++;

        if ( *pline == ';' || *pline == ':' )
            {
            pline++;
            while ( isdigit((int) *pline) ) pline++;
            }
        }

    while ( isdigit((int) *pline) ) pline++;

    if ( strchr(pline, '|') )
        err = decodeForm2(pline, name, longname, units);
    else
        err = decodeForm1(pline, name, longname, units);

    if ( err ) continue;

    if ( name[0] == 0 ) sprintf(name, "var%d", id);

    tableDefEntry(tableID, id, ltype, name, longname, units);
    }

return tableID;
}


static int tableFromEnv(int modelID, int tablenum)
{
char tablename[256] = {'\0'};
size_t tablenameLen = 0;
int instID;

const char *name2Use;
{
    const char *modelName, *instName;
    if ( (modelName = modelInqNamePtr(modelID)) )
    name2Use = modelName;
    else if ( (instID = modelInqInstitut(modelID)) != CDI_UNDEFID
            && (instName = institutInqNamePtr(instID)) )
    name2Use = instName;
    else
    return CDI_UNDEFID;
}
tablenameLen = strlen(name2Use);
memcpy(tablename, name2Use, tablenameLen);
if ( tablenum )
    tablenameLen
    += (size_t)(sprintf(tablename+tablenameLen, "_%03d", tablenum));
size_t lenp = 0, lenf = tablenameLen;
if ( tablePath )
    lenp = strlen(tablePath);


char *tablefile = (char *) Malloc(lenp+lenf+3);
if ( tablePath )
    {
    strcpy(tablefile, tablePath);
    strcat(tablefile, "/");
    }
else
    tablefile[0] = '\0';
strcat(tablefile, tablename);


int tableID = tableRead(tablefile);
if ( tableID != CDI_UNDEFID )
    {
    tableDefModelID(tableID, modelID);
    tableDefNum(tableID, tablenum);
    }

Free(tablefile);

return tableID;
}

int tableInq(int modelID, int tablenum, const char *tablename)
{
int tableID = CDI_UNDEFID;
int modelID2 = CDI_UNDEFID;
char tablefile[256] = {'\0'};

if ( ! ParTableInit ) parTableInit();

if ( tablename )
    {
    strcpy(tablefile, tablename);


    for ( tableID = 0; tableID < MAX_TABLE; tableID++ )
        {
        if ( parTable[tableID].used && parTable[tableID].name )
            {

            size_t len = strlen(tablename);
            if ( memcmp(parTable[tableID].name, tablename, len) == 0 ) break;
            }
        }
    if ( tableID == MAX_TABLE ) tableID = CDI_UNDEFID;
    if ( CDI_Debug )
        Message("tableID = %d tablename = %s", tableID, tablename);
    }
else
    {
    for ( tableID = 0; tableID < MAX_TABLE; tableID++ )
        {
        if ( parTable[tableID].used )
            {
            if ( parTable[tableID].modelID == modelID &&
                parTable[tableID].number  == tablenum ) break;
            }
        }

    if ( tableID == MAX_TABLE ) tableID = CDI_UNDEFID;

    if ( tableID == CDI_UNDEFID )
        {
        if ( modelID != CDI_UNDEFID )
            {
            const char *modelName;
            if ( (modelName = modelInqNamePtr(modelID)) )
                {
                strcpy(tablefile, modelName);
                size_t len = strlen(tablefile);
                for ( size_t i = 0; i < len; i++)
                    if ( tablefile[i] == '.' ) tablefile[i] = '\0';
                modelID2 = modelInq(-1, 0, tablefile);
                }
            }
        if ( modelID2 != CDI_UNDEFID )
            for ( tableID = 0; tableID < MAX_TABLE; tableID++ )
            {
                if ( parTable[tableID].used )
                {
                    if ( parTable[tableID].modelID == modelID2 &&
                        parTable[tableID].number  == tablenum ) break;
                }
            }
        }

    if ( tableID == MAX_TABLE ) tableID = CDI_UNDEFID;

    if ( tableID == CDI_UNDEFID && modelID != CDI_UNDEFID )
        tableID = tableFromEnv(modelID, tablenum);

    if ( CDI_Debug )
        if ( tablename )
        Message("tableID = %d tablename = %s", tableID, tablename);
    }

return tableID;
}

int tableDef(int modelID, int tablenum, const char *tablename)
{
int tableID = CDI_UNDEFID;

if ( ! ParTableInit ) parTableInit();

if ( tableID == CDI_UNDEFID )
    {
    tableID = tableNewEntry();

    parTable[tableID].modelID = modelID;
    parTable[tableID].number  = tablenum;
    if ( tablename )
        parTable[tableID].name = strdupx(tablename);

    parTable[tableID].pars = (param_type *) Malloc(MAX_PARS * sizeof(param_type));
    }

return tableID;
}

static
void tableDefModelID(int tableID, int modelID)
{
parTable[tableID].modelID = modelID;
}

static
void tableDefNum(int tableID, int tablenum)
{
parTable[tableID].number  = tablenum;
}


int tableInqNum(int tableID)
{
int number = 0;

if ( tableID >= 0 && tableID < MAX_TABLE )
    number = parTable[tableID].number;

return number;
}


int tableInqModel(int tableID)
{
int modelID = -1;

if ( tableID >= 0 && tableID < MAX_TABLE )
    modelID = parTable[tableID].modelID;

return modelID;
}


static void partabCheckID(int item)
{
if ( item < 0 || item >= parTableSize )
    Error("item %d undefined!", item);

if ( ! parTable[item].name )
    Error("item %d name undefined!", item);
}


const char *tableInqNamePtr(int tableID)
{
const char *tablename = NULL;

if ( CDI_Debug )
    Message("tableID = %d", tableID);

if ( ! ParTableInit ) parTableInit();

if ( tableID >= 0 && tableID < parTableSize )
    if ( parTable[tableID].name )
    tablename = parTable[tableID].name;

return tablename;
}


void tableWrite(const char *ptfile, int tableID)
{
size_t maxname = 4, maxlname = 10, maxunits = 2;
int instID = CDI_UNDEFID;
int center = 0, subcenter = 0;
const char *instnameptr = NULL, *modelnameptr = NULL;

if ( CDI_Debug )
    Message("write parameter table %d to %s", tableID, ptfile);

if ( tableID == CDI_UNDEFID )
    {
    Warning("parameter table ID undefined");
    return;
    }

partabCheckID(tableID);

FILE *ptfp = fopen(ptfile, "w");

int npars = parTable[tableID].npars;

for ( int item = 0; item < npars; item++)
    {
    if ( parTable[tableID].pars[item].name )
        {
        size_t lenname = strlen(parTable[tableID].pars[item].name);
        if ( lenname  > maxname )  maxname  = lenname;
        }

    if ( parTable[tableID].pars[item].longname )
        {
        size_t lenlname = strlen(parTable[tableID].pars[item].longname);
        if ( lenlname > maxlname ) maxlname = lenlname;
        }

    if ( parTable[tableID].pars[item].units )
        {
        size_t lenunits = strlen(parTable[tableID].pars[item].units);
        if ( lenunits > maxunits ) maxunits = lenunits;
        }
    }

int tablenum = tableInqNum(tableID);
int modelID = parTable[tableID].modelID;
if ( modelID != CDI_UNDEFID )
    {
    modelnameptr = modelInqNamePtr(modelID);
    instID = modelInqInstitut(modelID);
    }
if ( instID != CDI_UNDEFID )
    {
    center = institutInqCenter(instID);
    subcenter = institutInqSubcenter(instID);
    instnameptr = institutInqNamePtr(instID);
    }

fprintf(ptfp, "# Parameter table\n");
fprintf(ptfp, "#\n");
if ( tablenum )
    fprintf(ptfp, "# TABLE_ID=%d\n", tablenum);
fprintf(ptfp, "# TABLE_NAME=%s\n", parTable[tableID].name);
if ( modelnameptr )
    fprintf(ptfp, "# TABLE_MODEL=%s\n", modelnameptr);
if ( instnameptr )
    fprintf(ptfp, "# TABLE_INSTITUT=%s\n", instnameptr);
if ( center )
    fprintf(ptfp, "# TABLE_CENTER=%d\n", center);
if ( subcenter )
    fprintf(ptfp, "# TABLE_SUBCENTER=%d\n", subcenter);
fprintf(ptfp, "#\n");
fprintf(ptfp, "#\n");
fprintf(ptfp, "# id       = parameter ID\n");
fprintf(ptfp, "# name     = variable name\n");
fprintf(ptfp, "# title    = long name (description)\n");
fprintf(ptfp, "# units    = variable units\n");
fprintf(ptfp, "#\n");
fprintf(ptfp, "# The format of each record is:\n");
fprintf(ptfp, "#\n");
fprintf(ptfp, "# id | %-*s | %-*s | %-*s\n",
        (int)maxname,  "name",
        (int)maxlname, "title",
        (int)maxunits, "units");

for ( int item = 0; item < npars; item++)
    {
    const char *name = parTable[tableID].pars[item].name,
        *longname = parTable[tableID].pars[item].longname,
        *units = parTable[tableID].pars[item].units;
    if ( name == NULL ) name = " ";
    if ( longname == NULL ) longname = " ";
    if ( units == NULL ) units = " ";
    fprintf(ptfp, "%4d | %-*s | %-*s | %-*s\n",
            parTable[tableID].pars[item].id,
            (int)maxname, name,
            (int)maxlname, longname,
            (int)maxunits, units);
    }

fclose(ptfp);
}


void tableFWriteC(FILE *ptfp, int tableID)
{
const char chelp[] = "";
size_t maxname = 0, maxlname = 0, maxunits = 0;
char tablename[256];


if ( tableID == CDI_UNDEFID )
    {
    Warning("parameter table ID undefined");
    return;
    }

partabCheckID(tableID);

int npars = parTable[tableID].npars;

for ( int item = 0; item < npars; item++)
    {
    if ( parTable[tableID].pars[item].name )
        {
        size_t lenname = strlen(parTable[tableID].pars[item].name);
        if ( lenname  > maxname )  maxname  = lenname;
        }

    if ( parTable[tableID].pars[item].longname )
        {
        size_t lenlname = strlen(parTable[tableID].pars[item].longname);
        if ( lenlname > maxlname ) maxlname = lenlname;
        }

    if ( parTable[tableID].pars[item].units )
        {
        size_t lenunits = strlen(parTable[tableID].pars[item].units);
        if ( lenunits > maxunits ) maxunits = lenunits;
        }
    }

strncpy(tablename, parTable[tableID].name, sizeof (tablename));
tablename[sizeof (tablename) - 1] = '\0';
{
    size_t len = strlen(tablename);
    for (size_t i = 0; i < len; i++ )
    if ( tablename[i] == '.' ) tablename[i] = '_';
}
fprintf(ptfp, "static const param_type %s[] = {\n", tablename);

for ( int item = 0; item < npars; item++ )
    {
    size_t len = strlen(parTable[tableID].pars[item].name),
        llen = parTable[tableID].pars[item].longname
        ? strlen(parTable[tableID].pars[item].longname) : 0,
        ulen = parTable[tableID].pars[item].units
        ? strlen(parTable[tableID].pars[item].units) : 0;
    fprintf(ptfp, "  {%4d, -1, 0, \"%s\", %-*s%c%s%s, %-*s%c%s%s %-*s},\n",
            parTable[tableID].pars[item].id,
            parTable[tableID].pars[item].name, (int)(maxname-len), chelp,
            llen?'"':' ',
            llen?parTable[tableID].pars[item].longname:"NULL",
            llen?"\"":"",
            (int)(maxlname-(llen?llen:3)), chelp,
            ulen?'"':' ',
            ulen?parTable[tableID].pars[item].units:"NULL",
            ulen?"\"":"",
            (int)(maxunits-(ulen?ulen:3)), chelp);
    }

fprintf(ptfp, "};\n\n");
}


void tableInqEntry(int tableID, int id, int ltype, char *name, char *longname, char *units)
{
if ( ((tableID >= 0) & (tableID < MAX_TABLE)) | (tableID == CDI_UNDEFID) ) { } else
    Error("Invalid table ID %d", tableID);

if ( tableID != CDI_UNDEFID )
    {
    int npars = parTable[tableID].npars;
    for ( int item = 0; item < npars; item++ )
        {
        if ( parTable[tableID].pars[item].id == id &&
            (parTable[tableID].pars[item].ltype == -1 || ltype == -1 ||
                parTable[tableID].pars[item].ltype == ltype) )
            {
            if ( name && parTable[tableID].pars[item].name )
                strcpy(name, parTable[tableID].pars[item].name);
            if ( longname && parTable[tableID].pars[item].longname )
                strcpy(longname, parTable[tableID].pars[item].longname);
            if ( units && parTable[tableID].pars[item].units )
                strcpy(units, parTable[tableID].pars[item].units);

            break;
            }
        }
    }
}


int tableInqParCode(int tableID, char *varname, int *code)
{
int err = 1;

if ( tableID != CDI_UNDEFID && varname != NULL )
    {
    int npars = parTable[tableID].npars;
    for ( int item = 0; item < npars; item++ )
        {
        if ( parTable[tableID].pars[item].name
            && strcmp(parTable[tableID].pars[item].name, varname) == 0 )
            {
            *code = parTable[tableID].pars[item].id;
            err = 0;
            break;
            }
        }
    }

return err;
}


int tableInqNumber(void)
{
if ( ! ParTableInit ) parTableInit();

return parTableNum;
}

#include <string.h>
#include <math.h>
#include <float.h>



#define  LevelUp    1
#define  LevelDown  2


static const struct {
unsigned char positive;
const char *name;
const char *longname;
const char *stdname;
const char *units;
}
ZaxistypeEntry[] = {
{   0, "sfc",               "surface",                "",               ""},
{   0, "lev",               "generic",                "",               ""},
{   2, "lev",               "hybrid",                 "",               "level"},
{   2, "lev",               "hybrid_half",            "",               "level"},
{   2, "plev",              "pressure",               "air_pressure",   "Pa"},
{   1, "height",            "height",                 "height",         "m"},
{   2, "depth",             "depth_below_sea",        "depth",          "m"},
{   2, "depth",             "depth_below_land",       "",               "cm"},
{   0, "lev",               "isentropic",             "",               "K"},
{   0, "lev",               "trajectory",             "",               ""},
{   1, "alt",               "altitude",               "",               "m"},
{   0, "lev",               "sigma",                  "",               "level"},
{   0, "lev",               "meansea",                "",               "level"},
{   0, "toa",               "top_of_atmosphere",      "",               ""},
{   0, "seabottom",         "sea_bottom",             "",               ""},
{   0, "atmosphere",        "atmosphere",             "",               ""},
{   0, "cloudbase",         "cloud_base",             "",               ""},
{   0, "cloudtop",          "cloud_top",              "",               ""},
{   0, "isotherm0",         "isotherm_zero",          "",               ""},
{   0, "snow",              "snow",                   "",               ""},
{   0, "lakebottom",        "lake_bottom",            "",               ""},
{   0, "sedimentbottom",    "sediment_bottom",        "",               ""},
{   0, "sedimentbottomta",  "sediment_bottom_ta",     "",               ""},
{   0, "sedimentbottomtw",  "sediment_bottom_tw",     "",               ""},
{   0, "mixlayer",          "mix_layer",              "",               ""},
{   0, "height",            "generalized_height",     "height",         ""},
{   0, "character",         "area_type",              "",               ""},
};

enum {
CDI_NumZaxistype = sizeof(ZaxistypeEntry) / sizeof(ZaxistypeEntry[0]),
};


static int    zaxisCompareP    (zaxis_t *z1, zaxis_t *z2);
static void   zaxisDestroyP    ( void * zaxisptr );
static void   zaxisPrintP      ( void * zaxisptr, FILE * fp );
static int    zaxisGetPackSize ( void * zaxisptr, void *context);
static void   zaxisPack        ( void * zaxisptr, void * buffer, int size, int *pos, void *context);
static int    zaxisTxCode      ( void );

static const resOps zaxisOps = {
(int (*)(void *, void *))zaxisCompareP,
zaxisDestroyP,
zaxisPrintP,
zaxisGetPackSize,
zaxisPack,
zaxisTxCode
};

const resOps *getZaxisOps(void)
{
return &zaxisOps;
}

static int  ZAXIS_Debug = 0;

void zaxisGetTypeDescription(int zaxisType, int *outPositive, const char **outName, const char **outLongName, const char **outStdName, const char **outUnit)
{
if ( zaxisType < 0 || zaxisType >= CDI_NumZaxistype )
    {
    if (outPositive) *outPositive = 0;
    if (outName) *outName = NULL;
    if (outLongName) *outLongName = NULL;
    if (outStdName) *outStdName = NULL;
    if (outUnit) *outUnit = NULL;
    }
else
    {
    if (outPositive) *outPositive = ZaxistypeEntry[zaxisType].positive;
    if (outName) *outName = ZaxistypeEntry[zaxisType].name;
    if (outLongName && zaxisType != ZAXIS_GENERIC) *outLongName = ZaxistypeEntry[zaxisType].longname;
    if (outStdName) *outStdName = ZaxistypeEntry[zaxisType].stdname;
    if (outUnit) *outUnit = ZaxistypeEntry[zaxisType].units;
    }
}


zaxis_t *zaxis_to_pointer(int id)
{
return (zaxis_t *)reshGetVal(id, &zaxisOps);
}

static
void zaxis_init(zaxis_t *zaxisptr)
{
zaxisptr->self          = CDI_UNDEFID;
zaxisptr->name[0]       = 0;
zaxisptr->longname[0]   = 0;
zaxisptr->stdname[0]    = 0;
zaxisptr->dimname[0]    = 0;
zaxisptr->vdimname[0]   = 0;
zaxisptr->units[0]      = 0;
zaxisptr->psname[0]     = 0;
zaxisptr->p0name[0]     = 0;
zaxisptr->p0value.defined = false;
zaxisptr->vals          = NULL;
zaxisptr->cvals         = NULL;
zaxisptr->clength       = 0;
zaxisptr->ubounds       = NULL;
zaxisptr->lbounds       = NULL;
zaxisptr->weights       = NULL;
zaxisptr->type          = CDI_UNDEFID;
zaxisptr->ltype         = 0;
zaxisptr->ltype2        = -1;
zaxisptr->positive      = 0;
zaxisptr->scalar        = 0;
zaxisptr->direction     = 0;
zaxisptr->datatype      = CDI_DATATYPE_FLT64;
zaxisptr->size          = 0;
zaxisptr->vctsize       = 0;
zaxisptr->vct           = NULL;
zaxisptr->number        = 0;
zaxisptr->nhlev         = 0;
memset(zaxisptr->uuid, 0, CDI_UUID_SIZE);
zaxisptr->atts.nalloc   = MAX_ATTRIBUTES;
zaxisptr->atts.nelems   = 0;
}

static
zaxis_t *zaxisNewEntry(int id)
{
zaxis_t *zaxisptr = (zaxis_t *) Malloc(sizeof(zaxis_t));
zaxis_init(zaxisptr);

if ( id == CDI_UNDEFID )
    zaxisptr->self = reshPut(zaxisptr, &zaxisOps);
else
    {
    zaxisptr->self = id;
    reshReplace(id, zaxisptr, &zaxisOps);
    }

return zaxisptr;
}

static
void zaxisInit(void)
{
static bool zaxisInitialized = false;
if ( zaxisInitialized ) return;
zaxisInitialized = true;

const char *env = getenv("ZAXIS_DEBUG");
if ( env ) ZAXIS_Debug = atoi(env);
}

static
void zaxis_copy(zaxis_t *zaxisptr2, zaxis_t *zaxisptr1)
{
int zaxisID2 = zaxisptr2->self;
memcpy(zaxisptr2, zaxisptr1, sizeof(zaxis_t));
zaxisptr2->self = zaxisID2;
}


unsigned cdiZaxisCount(void)
{
return reshCountType(&zaxisOps);
}

static
int zaxisCreate_(int zaxistype, int size, int id)
{
zaxis_t *zaxisptr = zaxisNewEntry(id);

xassert(size >= 0);
zaxisptr->type = zaxistype;
zaxisptr->size = size;

if ( zaxistype >= CDI_NumZaxistype || zaxistype < 0 )
    Error("Internal problem! zaxistype > CDI_MaxZaxistype");

int zaxisID = zaxisptr->self;
zaxisDefName(zaxisID, ZaxistypeEntry[zaxistype].name);
if ( zaxistype != ZAXIS_GENERIC ) zaxisDefLongname(zaxisID, ZaxistypeEntry[zaxistype].longname);
zaxisDefUnits(zaxisID, ZaxistypeEntry[zaxistype].units);

if ( *ZaxistypeEntry[zaxistype].stdname )
    strcpy(zaxisptr->stdname, ZaxistypeEntry[zaxistype].stdname);

zaxisptr->positive = ZaxistypeEntry[zaxistype].positive;

return zaxisID;
}


int zaxisCreate(int zaxistype, int size)
{
if ( CDI_Debug ) Message("zaxistype: %d size: %d ", zaxistype, size);

zaxisInit();

return zaxisCreate_(zaxistype, size, CDI_UNDEFID);
}

static
void zaxisDestroyKernel( zaxis_t * zaxisptr )
{
xassert ( zaxisptr );

int id = zaxisptr->self;

if ( zaxisptr->vals )    Free( zaxisptr->vals );
if ( zaxisptr->cvals )
    {
    for ( int i=0; i<zaxisptr->size; i++)
        Free(zaxisptr->cvals[i]);
    Free( zaxisptr->cvals );
    }
if ( zaxisptr->lbounds ) Free( zaxisptr->lbounds );
if ( zaxisptr->ubounds ) Free( zaxisptr->ubounds );
if ( zaxisptr->weights ) Free( zaxisptr->weights );
if ( zaxisptr->vct )     Free( zaxisptr->vct );

Free( zaxisptr );

reshRemove ( id, &zaxisOps );
}


void zaxisDestroy(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
zaxisDestroyKernel(zaxisptr);
}


static
void zaxisDestroyP(void *zaxisptr)
{
zaxisDestroyKernel((zaxis_t *) zaxisptr);
}


const char *zaxisNamePtr(int zaxistype)
{
const char *name = (zaxistype >= 0 && zaxistype < CDI_NumZaxistype)
    ? ZaxistypeEntry[zaxistype].longname
    : ZaxistypeEntry[ZAXIS_GENERIC].longname;
return name;
}


void zaxisName(int zaxistype, char *zaxisname)
{
strcpy(zaxisname, zaxisNamePtr(zaxistype));
}

static inline
void zaxisSetString(char *zaxisstrname, const char *name, size_t len)
{
if ( len > CDI_MAX_NAME ) len = CDI_MAX_NAME;
strncpy(zaxisstrname, name, len);
zaxisstrname[len-1] = 0;
}

static inline
void zaxisGetString(char *name, const char *zaxisstrname, size_t len)
{
size_t slen = strlen(zaxisstrname)+1;
if ( slen > len ) slen = len;
if ( slen > CDI_MAX_NAME ) slen = CDI_MAX_NAME;
strncpy(name, zaxisstrname, slen);
name[slen-1] = 0;
}

static
void *zaxis_key_to_ptr(zaxis_t *zaxisptr, int key)
{
void *keyptr = NULL;

switch (key)
    {
    case CDI_KEY_NAME:      keyptr = (void*)zaxisptr->name; break;
    case CDI_KEY_LONGNAME:  keyptr = (void*)zaxisptr->longname; break;
    case CDI_KEY_UNITS:     keyptr = (void*)zaxisptr->units; break;
    case CDI_KEY_DIMNAME:   keyptr = (void*)zaxisptr->dimname; break;
    case CDI_KEY_VDIMNAME:  keyptr = (void*)zaxisptr->vdimname; break;
    case CDI_KEY_PSNAME:    keyptr = (void*)zaxisptr->psname; break;
    case CDI_KEY_P0NAME:    keyptr = (void*)zaxisptr->p0name; break;
    case CDI_KEY_P0VALUE:   keyptr = (void*)&zaxisptr->p0value; break;
    }

return keyptr;
}


int cdiZaxisDefKeyStr(int zaxisID, int key, int size, const char *mesg)
{
if ( size < 1 || mesg == NULL ) return -1;

zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

char *keyptr = (char*)zaxis_key_to_ptr(zaxisptr, key);
if ( keyptr == NULL)
    {
    Warning("CDI zaxis string key %d not supported!", key);
    return -1;
    }

zaxisSetString(keyptr, mesg, (size_t)size);
reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);

return 0;
}


int cdiZaxisInqKeyStr(int zaxisID, int key, int size, char *mesg)
{
if ( size < 1 || mesg == NULL ) return -1;

zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
const char *keyptr = (const char*)zaxis_key_to_ptr(zaxisptr, key);
if ( keyptr == NULL)
    {
    Warning("CDI zaxis string key %d not supported!", key);
    return -1;
    }

zaxisGetString(mesg, keyptr, (size_t)size);

return 0;
}



int cdiZaxisDefKeyFlt(int zaxisID, int key, double value)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

zkey_double_t *keyptr = (zkey_double_t*)zaxis_key_to_ptr(zaxisptr, key);
if ( keyptr == NULL)
    {
    Warning("CDI zaxis double key %d not supported!", key);
    return -1;
    }

keyptr->value = value;
keyptr->defined = true;

reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);

return 0;
}


int cdiZaxisInqKeyFlt(int zaxisID, int key, double *value)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
zkey_double_t *keyptr = (zkey_double_t*)zaxis_key_to_ptr(zaxisptr, key);
if ( keyptr == NULL)
    {
    Warning("CDI zaxis double key %d not supported!", key);
    return -1;
    }

if ( !keyptr->defined ) return 1;

*value = keyptr->value;

return 0;
}


void zaxisDefName(int zaxisID, const char *name)
{
(void)cdiZaxisDefKeyStr(zaxisID, CDI_KEY_NAME, CDI_MAX_NAME, name);
}


void zaxisDefLongname(int zaxisID, const char *longname)
{
(void)cdiZaxisDefKeyStr(zaxisID, CDI_KEY_LONGNAME, CDI_MAX_NAME, longname);
}


void zaxisDefUnits(int zaxisID, const char *units)
{
(void)cdiZaxisDefKeyStr(zaxisID, CDI_KEY_UNITS, CDI_MAX_NAME, units);
}


void zaxisInqName(int zaxisID, char *name)
{
(void)cdiZaxisInqKeyStr(zaxisID, CDI_KEY_NAME, CDI_MAX_NAME, name);
}

const char *zaxisInqNamePtr(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->name;
}


void zaxisInqLongname(int zaxisID, char *longname)
{
(void)cdiZaxisInqKeyStr(zaxisID, CDI_KEY_LONGNAME, CDI_MAX_NAME, longname);
}


void zaxisInqUnits(int zaxisID, char *units)
{
(void)cdiZaxisInqKeyStr(zaxisID, CDI_KEY_UNITS, CDI_MAX_NAME, units);
}


void zaxisInqStdname(int zaxisID, char *stdname)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
strcpy(stdname, zaxisptr->stdname);
}


void zaxisDefDatatype(int zaxisID, int datatype)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->datatype != datatype )
    {
    zaxisptr->datatype = datatype;
    reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
    }
}


int zaxisInqDatatype(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->datatype;
}


void zaxisDefPositive(int zaxisID, int positive)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->positive != (unsigned)positive )
    {
    zaxisptr->positive = (unsigned)positive;
    reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
    }
}


int zaxisInqPositive(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return (int)zaxisptr->positive;
}


void zaxisDefScalar(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

zaxisptr->scalar = 1;
reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
}

int zaxisInqScalar(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->scalar;
}


void zaxisDefLtype(int zaxisID, int ltype)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if (zaxisptr->ltype != ltype)
    {
    zaxisptr->ltype = ltype;
    reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
    }
}


int zaxisInqLtype(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->ltype;
}


void zaxisDefLtype2(int zaxisID, int ltype2)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->ltype2 != ltype2 )
    {
    zaxisptr->ltype2 = ltype2;
    reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
    }
}


int zaxisInqLtype2(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->ltype2;
}


void zaxisDefLevels(int zaxisID, const double *levels)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
size_t size = (size_t)zaxisptr->size;

if ( levels )
    {
    if ( zaxisptr->vals == NULL )
        zaxisptr->vals = (double*) Malloc(size*sizeof(double));

    double *vals = zaxisptr->vals;

    for ( size_t ilev = 0; ilev < size; ++ilev )
        vals[ilev] = levels[ilev];

    reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
    }
}


void zaxisDefCvals(int zaxisID, const char **cvals, int clen)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
int size = zaxisptr->size;

if ( cvals && clen )
    {
    zaxisptr->clength = clen;
    zaxisptr->cvals = (char**) Malloc((size_t)size*sizeof(char *));

    for ( int ilev = 0; ilev < size; ++ilev )
        {
        zaxisptr->cvals[ilev] = (char*) Malloc((size_t)clen*sizeof(char));
        memcpy(zaxisptr->cvals[ilev], cvals[ilev], (size_t)clen*sizeof(char));
        }
    reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
    }
}


void zaxisDefLevel(int zaxisID, int levelID, double level)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
int size = zaxisptr->size;

if ( zaxisptr->vals == NULL )
    zaxisptr->vals = (double*) Malloc((size_t)size*sizeof(double));

if ( levelID >= 0 && levelID < size )
    zaxisptr->vals[levelID] = level;

reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
}


void zaxisDefNlevRef(int zaxisID, int nhlev)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
if (zaxisptr->nhlev != nhlev)
    {
    zaxisptr->nhlev = nhlev;
    reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
    }
}


int zaxisInqNlevRef(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->nhlev;
}


void zaxisDefNumber(int zaxisID, int number)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
if (zaxisptr->number != number)
    {
    zaxisptr->number = number;
    reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
    }
}


int zaxisInqNumber(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->number;
}


void zaxisDefUUID(int zaxisID, const unsigned char uuid[CDI_UUID_SIZE])
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
memcpy(zaxisptr->uuid, uuid, CDI_UUID_SIZE);
reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
}


void zaxisInqUUID(int zaxisID, unsigned char uuid[CDI_UUID_SIZE])
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
memcpy(uuid, zaxisptr->uuid, CDI_UUID_SIZE);
}


double zaxisInqLevel(int zaxisID, int levelID)
{
double level = 0;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->vals && levelID >= 0 && levelID < zaxisptr->size )
    level = zaxisptr->vals[levelID];

return level;
}


double zaxisInqLbound(int zaxisID, int levelID)
{
double level = 0;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->lbounds && levelID >= 0 && levelID < zaxisptr->size )
    level = zaxisptr->lbounds[levelID];

return level;
}


double zaxisInqUbound(int zaxisID, int levelID)
{
double level = 0;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->ubounds && levelID >= 0 && levelID < zaxisptr->size )
    level = zaxisptr->ubounds[levelID];

return level;
}


const double *zaxisInqLevelsPtr(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->vals;
}


char **zaxisInqCValsPtr(int zaxisID)
{
char **cvals = NULL;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
cvals = zaxisptr->cvals;
return cvals;
}


int zaxisInqLevels(int zaxisID, double *levels)
{
int size = 0;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->vals )
    {
    size = zaxisptr->size;

    if ( levels )
        for ( int i = 0; i < size; i++ )
        levels[i] = zaxisptr->vals[i];
    }

return size;
}

int zaxisInqCLen(int zaxisID)
{
int size = 0;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->cvals && zaxisptr->clength)
    size = zaxisptr->clength;
return size;
}

int zaxisInqCVals(int zaxisID, char ***clevels)
{
int size = 0;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->cvals )
    {
    size = zaxisptr->size;
    size_t clen = zaxisptr->clength;
    if ( size && clen )
        {
        (*clevels) = (char**) Malloc(size*sizeof(char*));
        for ( int i = 0; i < size; i++ )
            {
            (*clevels)[i] = (char*) Malloc(clen*sizeof(char));
            memcpy((*clevels)[i], zaxisptr->cvals[i], clen*sizeof(char));
            }
        }
    }

return size;
}


int zaxisInqLbounds(int zaxisID, double *lbounds)
{
int size = 0;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->lbounds )
    {
    size = zaxisptr->size;

    if ( lbounds )
        for ( int i = 0; i < size; i++ )
        lbounds[i] = zaxisptr->lbounds[i];
    }

return size;
}


int zaxisInqUbounds(int zaxisID, double *ubounds)
{
int size = 0;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->ubounds )
    {
    size = zaxisptr->size;

    if ( ubounds )
        for ( int i = 0; i < size; i++ )
        ubounds[i] = zaxisptr->ubounds[i];
    }

return size;
}


int zaxisInqWeights(int zaxisID, double *weights)
{
int size = 0;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->weights )
    {
    size = zaxisptr->size;

    if ( weights )
        for ( int i = 0; i < size; i++ )
        weights[i] = zaxisptr->weights[i];
    }

return size;
}


int zaxisInqLevelID(int zaxisID, double level)
{
int levelID = CDI_UNDEFID;
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->vals )
    {
    int size = zaxisptr->size;

    for ( int i = 0; i < size; i++ )
        if ( fabs(level-zaxisptr->vals[i]) < DBL_EPSILON )
        {
            levelID = i;
            break;
        }
    }

return levelID;
}


int zaxisInqType(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->type;
}


int zaxisInqSize(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->size;
}


void cdiCheckZaxis(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisInqType(zaxisID) == ZAXIS_GENERIC && zaxisptr->vals )
    {
    int size = zaxisptr->size;
    if ( size > 1 )
        {

        if ( ! zaxisptr->direction )
            {
            int ups = 0, downs = 0;
            for ( int i = 1; i < size; i++ )
                {
                ups += (zaxisptr->vals[i] > zaxisptr->vals[i-1]);
                downs += (zaxisptr->vals[i] < zaxisptr->vals[i-1]);
                }
            if ( ups == size-1 )
                {
                zaxisptr->direction = LevelUp;
                }
            else if ( downs == size-1 )
                {
                zaxisptr->direction = LevelDown;
                }
            else
                {
                Warning("Direction undefined for zaxisID %d", zaxisID);
                }
            }
        }
    }
}


void zaxisDefVct(int zaxisID, int size, const double *vct)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

if ( zaxisptr->vct == 0 || zaxisptr->vctsize != size )
    {
    zaxisptr->vctsize = size;
    zaxisptr->vct = (double *) Realloc(zaxisptr->vct, (size_t)size*sizeof(double));
    }

memcpy(zaxisptr->vct, vct, (size_t)size*sizeof(double));
reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
}


void zaxisInqVct(int zaxisID, double *vct)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
memcpy(vct, zaxisptr->vct, (size_t)zaxisptr->vctsize * sizeof (double));
}


int zaxisInqVctSize(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->vctsize;
}


const double *zaxisInqVctPtr(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
return zaxisptr->vct;
}


void zaxisDefLbounds(int zaxisID, const double *lbounds)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

size_t size = (size_t)zaxisptr->size;

if ( CDI_Debug )
    if ( zaxisptr->lbounds != NULL )
    Warning("Lower bounds already defined for zaxisID = %d", zaxisID);

if ( zaxisptr->lbounds == NULL )
    zaxisptr->lbounds = (double *) Malloc(size*sizeof(double));

memcpy(zaxisptr->lbounds, lbounds, size*sizeof(double));
reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
}


void zaxisDefUbounds(int zaxisID, const double *ubounds)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

size_t size = (size_t)zaxisptr->size;

if ( CDI_Debug )
    if ( zaxisptr->ubounds != NULL )
    Warning("Upper bounds already defined for zaxisID = %d", zaxisID);

if ( zaxisptr->ubounds == NULL )
    zaxisptr->ubounds = (double *) Malloc(size*sizeof(double));

memcpy(zaxisptr->ubounds, ubounds, size*sizeof(double));
reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
}


void zaxisDefWeights(int zaxisID, const double *weights)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

size_t size = (size_t)zaxisptr->size;

if ( CDI_Debug )
    if ( zaxisptr->weights != NULL )
    Warning("Weights already defined for zaxisID = %d", zaxisID);

if ( zaxisptr->weights == NULL )
    zaxisptr->weights = (double *) Malloc(size*sizeof(double));

memcpy(zaxisptr->weights, weights, size*sizeof(double));
reshSetStatus(zaxisID, &zaxisOps, RESH_DESYNC_IN_USE);
}


void zaxisChangeType(int zaxisID, int zaxistype)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
zaxisptr->type = zaxistype;
}


void zaxisResize(int zaxisID, int size)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

xassert(size >= 0);

zaxisptr->size = size;

if ( zaxisptr->vals )
    zaxisptr->vals = (double *) Realloc(zaxisptr->vals, (size_t)size*sizeof(double));
}


int zaxisDuplicate(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);

int zaxistype = zaxisInqType(zaxisID);
int zaxissize = zaxisInqSize(zaxisID);

int zaxisIDnew = zaxisCreate(zaxistype, zaxissize);
zaxis_t *zaxisptrnew = zaxis_to_pointer(zaxisIDnew);

zaxis_copy(zaxisptrnew, zaxisptr);

strcpy(zaxisptrnew->name, zaxisptr->name);
strcpy(zaxisptrnew->longname, zaxisptr->longname);
strcpy(zaxisptrnew->units, zaxisptr->units);

if ( zaxisptr->vals )
    {
    size_t size = (size_t)zaxissize;
    zaxisptrnew->vals = (double *) Malloc(size * sizeof (double));
    memcpy(zaxisptrnew->vals, zaxisptr->vals, size * sizeof (double));
    }

if ( zaxisptr->lbounds )
    {
    size_t size = (size_t)zaxissize;
    zaxisptrnew->lbounds = (double *) Malloc(size * sizeof (double));
    memcpy(zaxisptrnew->lbounds, zaxisptr->lbounds, size * sizeof(double));
    }

if ( zaxisptr->ubounds )
    {
    size_t size = (size_t)zaxissize;
    zaxisptrnew->ubounds = (double *) Malloc(size * sizeof (double));
    memcpy(zaxisptrnew->ubounds, zaxisptr->ubounds, size * sizeof (double));
    }

if ( zaxisptr->vct )
    {
    size_t size = (size_t)zaxisptr->vctsize;
    if ( size )
        {
        zaxisptrnew->vctsize = (int)size;
        zaxisptrnew->vct = (double *) Malloc(size * sizeof (double));
        memcpy(zaxisptrnew->vct, zaxisptr->vct, size * sizeof (double));
        }
    }

return zaxisIDnew;
}

static
void zaxisPrintKernel(zaxis_t *zaxisptr, FILE *fp)
{
xassert(zaxisptr);

int zaxisID = zaxisptr->self;
int type    = zaxisptr->type;
int nlevels = zaxisptr->size;
int datatype = zaxisptr->datatype;

int dig = (datatype == CDI_DATATYPE_FLT64) ? 15 : 7;

int nbyte;
int nbyte0 = 0;
fprintf(fp, "zaxistype = %s\n", zaxisNamePtr(type));
fprintf(fp, "size      = %d\n", nlevels);
if ( nlevels == 1 )
    {
    bool zscalar = (bool)zaxisptr->scalar;
    if ( zscalar ) fprintf(fp, "scalar    = true\n");
    }
if ( zaxisptr->name[0]     ) fprintf(fp, "name      = %s\n", zaxisptr->name);
if ( zaxisptr->longname[0] ) fprintf(fp, "longname  = %s\n", zaxisptr->longname);
if ( zaxisptr->units[0]    ) fprintf(fp, "units     = %s\n", zaxisptr->units);

if ( zaxisptr->vals )
    {
    nbyte0 = fprintf(fp, "levels    = ");
    nbyte = nbyte0;
    for ( int levelID = 0; levelID < nlevels; levelID++ )
        {
        if ( nbyte > 80 )
            {
            fprintf(fp, "\n");
            fprintf(fp, "%*s", nbyte0, "");
            nbyte = nbyte0;
            }
        nbyte += fprintf(fp, "%.*g ", dig, zaxisptr->vals[levelID]);
        }
    fprintf(fp, "\n");
    }

if ( zaxisptr->cvals )
    {
    dig = datatype;
    nbyte0 = fprintf(fp, "types     = ");
    nbyte = nbyte0;
    for ( int levelID = 0; levelID < nlevels; levelID++ )
        {
        fprintf(fp, "\n");
        fprintf(fp, "%*s", nbyte0, "");
        nbyte = nbyte0;
        nbyte += fprintf(fp, "%.*s [%d]", dig, zaxisptr->cvals[levelID], levelID+1);
        }
    fprintf(fp, "\n");
    }

if ( zaxisptr->lbounds && zaxisptr->ubounds )
    {
    nbyte0 = fprintf(fp, "lbounds   = ");
    nbyte = nbyte0;
    for ( int levelID = 0; levelID < nlevels; levelID++ )
        {
        if ( nbyte > 80 )
            {
            fprintf(fp, "\n");
            fprintf(fp, "%*s", nbyte0, "");
            nbyte = nbyte0;
            }
        nbyte += fprintf(fp, "%.*g ", dig, zaxisptr->lbounds[levelID]);
        }
    fprintf(fp, "\n");

    nbyte0 = fprintf(fp, "ubounds   = ");
    nbyte = nbyte0;
    for ( int levelID = 0; levelID < nlevels; levelID++ )
        {
        if ( nbyte > 80 )
            {
            fprintf(fp, "\n");
            fprintf(fp, "%*s", nbyte0, "");
            nbyte = nbyte0;
            }
        nbyte += fprintf(fp, "%.*g ", dig, zaxisptr->ubounds[levelID]);
        }
    fprintf(fp, "\n");
    }

if ( type == ZAXIS_HYBRID || type == ZAXIS_HYBRID_HALF )
    {
    int vctsize = zaxisptr->vctsize;
    const double *vct = zaxisptr->vct;
    fprintf(fp, "vctsize   = %d\n", vctsize);
    if ( vctsize )
        {
        nbyte0 = fprintf(fp, "vct       = ");
        nbyte = nbyte0;
        for ( int i = 0; i < vctsize; i++ )
            {
            if ( nbyte > 70 || i == vctsize/2 )
                {
                fprintf(fp, "\n%*s", nbyte0, "");
                nbyte = nbyte0;
                }
            nbyte += fprintf(fp, "%.15g ", vct[i]);
            }
        fprintf(fp, "\n");

        }
    }

if ( type == ZAXIS_REFERENCE )
    {
    unsigned char uuid[CDI_UUID_SIZE];
    zaxisInqUUID(zaxisID, uuid);
    if ( *uuid )
        {
        const unsigned char *d = uuid;
        fprintf(fp, "uuid      = %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
                d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
                d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
        }
    }
}


void zaxisPrint(int zaxisID)
{
zaxis_t *zaxisptr = zaxis_to_pointer(zaxisID);
zaxisPrintKernel(zaxisptr, stdout);
}


static
void zaxisPrintP(void * voidptr, FILE * fp)
{
zaxis_t *zaxisptr = ( zaxis_t * ) voidptr;

xassert ( zaxisptr );

zaxisPrintKernel(zaxisptr, fp);
}


static
int zaxisCompareP(zaxis_t *z1, zaxis_t *z2)
{
enum { differ = 1 };
int diff = 0;
xassert(z1 && z2);

diff |= (z1->type != z2->type)
    | (z1->ltype != z2->ltype)
    | (z1->direction != z2->direction)
    | (z1->datatype != z2->datatype)
    | (z1->size != z2->size)
    | (z1->vctsize != z2->vctsize)
    | (z1->positive != z2->positive);

if ( diff ) return differ;

int size = z1->size;
int anyPresent = 0;
int present = (z1->vals != NULL);
diff |= (present ^ (z2->vals != NULL));
anyPresent |= present;
if (!diff && present)
    {
    const double *p = z1->vals, *q = z2->vals;
    for ( int i = 0; i < size; i++ )
        diff |= IS_NOT_EQUAL(p[i], q[i]);
    }

present = (z1->lbounds != NULL);
diff |= (present ^ (z2->lbounds != NULL));
anyPresent |= present;
if (!diff && present)
    {
    const double *p = z1->lbounds, *q = z2->lbounds;
    for ( int i = 0; i < size; i++ )
        diff |= IS_NOT_EQUAL(p[i], q[i]);
    }

present = (z1->ubounds != NULL);
diff |= (present ^ (z2->ubounds != NULL));
anyPresent |= present;
if (!diff && present)
    {
    const double *p = z1->ubounds, *q = z2->ubounds;
    for ( int i = 0; i < size; ++i )
        diff |= IS_NOT_EQUAL(p[i], q[i]);
    }

present = (z1->weights != NULL);
diff |= (present ^ (z2->weights != NULL));
anyPresent |= present;
if (!diff && present)
    {
    const double *p = z1->weights, *q = z2->weights;
    for ( int i = 0; i < size; ++i )
        diff |= IS_NOT_EQUAL(p[i], q[i]);
    }

present = (z1->vct != NULL);
diff |= (present ^ (z2->vct != NULL));
if (!diff && present)
    {
    int vctsize = z1->vctsize;
    xassert(vctsize);
    const double *p = z1->vct, *q = z2->vct;
    for ( int i = 0; i < vctsize; ++i )
        diff |= IS_NOT_EQUAL(p[i], q[i]);
    }

if (anyPresent)
    xassert(size);

diff |= strcmp(z1->name, z2->name)
    | strcmp(z1->longname, z2->longname)
    | strcmp(z1->stdname, z2->stdname)
    | strcmp(z1->units, z2->units)
    | memcmp(z1->uuid, z2->uuid, CDI_UUID_SIZE);
return diff != 0;
}


static
int zaxisTxCode(void)
{
return ZAXIS;
}

enum { zaxisNint     = 8,
    vals     = 1 << 0,
    lbounds  = 1 << 1,
    ubounds  = 1 << 2,
    weights  = 1 << 3,
    vct      = 1 << 4,
    zaxisHasUUIDFlag = 1 << 5,
};

#define ZAXIS_STR_SERIALIZE { zaxisP->name, zaxisP->longname, \
                            zaxisP->stdname, zaxisP->units }

static
int zaxisGetMemberMask( zaxis_t * zaxisP )
{
int memberMask = 0;

if ( zaxisP->vals )      memberMask |= vals;
if ( zaxisP->lbounds )   memberMask |= lbounds;
if ( zaxisP->ubounds )   memberMask |= ubounds;
if ( zaxisP->weights )   memberMask |= weights;
if ( zaxisP->vct )       memberMask |= vct;
if (!cdiUUIDIsNull(zaxisP->uuid)) memberMask |= zaxisHasUUIDFlag;
return memberMask;
}

static int
zaxisGetPackSize(void * voidP, void *context)
{
zaxis_t * zaxisP = ( zaxis_t * ) voidP;
int packBufferSize = serializeGetSize(zaxisNint, CDI_DATATYPE_INT, context)
    + serializeGetSize(1, CDI_DATATYPE_UINT32, context);

if (zaxisP->vals || zaxisP->lbounds || zaxisP->ubounds || zaxisP->weights)
    xassert(zaxisP->size);

if ( zaxisP->vals )
    packBufferSize += serializeGetSize(zaxisP->size, CDI_DATATYPE_FLT64, context)
    + serializeGetSize(1, CDI_DATATYPE_UINT32, context);

if ( zaxisP->lbounds )
    packBufferSize += serializeGetSize(zaxisP->size, CDI_DATATYPE_FLT64, context)
    + serializeGetSize(1, CDI_DATATYPE_UINT32, context);

if ( zaxisP->ubounds )
    packBufferSize += serializeGetSize(zaxisP->size, CDI_DATATYPE_FLT64, context)
    + serializeGetSize(1, CDI_DATATYPE_UINT32, context);

if ( zaxisP->weights )
    packBufferSize += serializeGetSize(zaxisP->size, CDI_DATATYPE_FLT64, context)
    + serializeGetSize(1, CDI_DATATYPE_UINT32, context);

if ( zaxisP->vct )
    {
    xassert ( zaxisP->vctsize );
    packBufferSize += serializeGetSize(zaxisP->vctsize, CDI_DATATYPE_FLT64, context)
        + serializeGetSize(1, CDI_DATATYPE_UINT32, context);
    }

{
    const char *strTab[] = ZAXIS_STR_SERIALIZE;
    size_t numStr = sizeof (strTab) / sizeof (strTab[0]);
    packBufferSize
    += serializeStrTabGetPackSize(strTab, (int)numStr, context);
}

packBufferSize += serializeGetSize(1, CDI_DATATYPE_UCHAR, context);

if (!cdiUUIDIsNull(zaxisP->uuid))
    packBufferSize += serializeGetSize(CDI_UUID_SIZE, CDI_DATATYPE_UCHAR, context);

return packBufferSize;
}


void
zaxisUnpack(char * unpackBuffer, int unpackBufferSize,
            int * unpackBufferPos, int originNamespace, void *context,
            int force_id)
{
int intBuffer[zaxisNint], memberMask;
uint32_t d;

serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                intBuffer, zaxisNint, CDI_DATATYPE_INT, context);
serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                &d, 1, CDI_DATATYPE_UINT32, context);

xassert(cdiCheckSum(CDI_DATATYPE_INT, zaxisNint, intBuffer) == d);

zaxisInit();

zaxis_t *zaxisP
    = zaxisNewEntry(force_id ? namespaceAdaptKey(intBuffer[0], originNamespace)
                    : CDI_UNDEFID);

zaxisP->datatype  = intBuffer[1];
zaxisP->type      = intBuffer[2];
zaxisP->ltype     = intBuffer[3];
zaxisP->size      = intBuffer[4];
zaxisP->direction = intBuffer[5];
zaxisP->vctsize   = intBuffer[6];
memberMask        = intBuffer[7];

if (memberMask & vals)
    {
    int size = zaxisP->size;
    xassert(size >= 0);

    zaxisP->vals = (double *) Malloc((size_t)size * sizeof (double));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    zaxisP->vals, size, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_FLT, size, zaxisP->vals) == d);
    }

if (memberMask & lbounds)
    {
    int size = zaxisP->size;
    xassert(size >= 0);

    zaxisP->lbounds = (double *) Malloc((size_t)size * sizeof (double));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    zaxisP->lbounds, size, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_FLT, size, zaxisP->lbounds) == d);
    }

if (memberMask & ubounds)
    {
    int size = zaxisP->size;
    xassert(size >= 0);

    zaxisP->ubounds = (double *) Malloc((size_t)size * sizeof (double));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    zaxisP->ubounds, size, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_FLT, size, zaxisP->ubounds) == d);
    }

if (memberMask & weights)
    {
    int size = zaxisP->size;
    xassert(size >= 0);

    zaxisP->weights = (double *) Malloc((size_t)size * sizeof (double));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    zaxisP->weights, size, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_FLT, size, zaxisP->weights) == d);
    }

if ( memberMask & vct )
    {
    int size = zaxisP->vctsize;
    xassert(size >= 0);

    zaxisP->vct = (double *) Malloc((size_t)size * sizeof (double));
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    zaxisP->vct, size, CDI_DATATYPE_FLT64, context);
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    &d, 1, CDI_DATATYPE_UINT32, context);
    xassert(cdiCheckSum(CDI_DATATYPE_FLT64, size, zaxisP->vct) == d);
    }

{
    char *strTab[] = ZAXIS_STR_SERIALIZE;
    int numStr = sizeof (strTab) / sizeof (strTab[0]);
    serializeStrTabUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                        strTab, numStr, context);
}

serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                &zaxisP->positive, 1, CDI_DATATYPE_UINT, context);

if (memberMask & zaxisHasUUIDFlag)
    serializeUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos,
                    zaxisP->uuid, CDI_UUID_SIZE, CDI_DATATYPE_UCHAR, context);

reshSetStatus(zaxisP->self, &zaxisOps,
                reshGetStatus(zaxisP->self, &zaxisOps) & ~RESH_SYNC_BIT);
}

static void
zaxisPack(void * voidP, void * packBuffer, int packBufferSize,
        int * packBufferPos, void *context)
{
zaxis_t   * zaxisP = ( zaxis_t * ) voidP;
int intBuffer[zaxisNint];
int memberMask;
uint32_t d;

intBuffer[0]  = zaxisP->self;
intBuffer[1]  = zaxisP->datatype;
intBuffer[2]  = zaxisP->type;
intBuffer[3]  = zaxisP->ltype;
intBuffer[4]  = zaxisP->size;
intBuffer[5]  = zaxisP->direction;
intBuffer[6]  = zaxisP->vctsize;
intBuffer[7]  = memberMask = zaxisGetMemberMask ( zaxisP );

serializePack(intBuffer, zaxisNint, CDI_DATATYPE_INT,
                packBuffer, packBufferSize, packBufferPos, context);
d = cdiCheckSum(CDI_DATATYPE_INT, zaxisNint, intBuffer);
serializePack(&d, 1, CDI_DATATYPE_UINT32,
                packBuffer, packBufferSize, packBufferPos, context);


if ( memberMask & vals )
    {
    xassert(zaxisP->size);
    serializePack(zaxisP->vals, zaxisP->size, CDI_DATATYPE_FLT64,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT, zaxisP->size, zaxisP->vals );
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & lbounds)
    {
    xassert(zaxisP->size);
    serializePack(zaxisP->lbounds, zaxisP->size, CDI_DATATYPE_FLT64,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT, zaxisP->size, zaxisP->lbounds);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & ubounds)
    {
    xassert(zaxisP->size);

    serializePack(zaxisP->ubounds, zaxisP->size, CDI_DATATYPE_FLT64,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT, zaxisP->size, zaxisP->ubounds);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & weights)
    {
    xassert(zaxisP->size);

    serializePack(zaxisP->weights, zaxisP->size, CDI_DATATYPE_FLT64,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT, zaxisP->size, zaxisP->weights);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

if (memberMask & vct)
    {
    xassert(zaxisP->vctsize);

    serializePack(zaxisP->vct, zaxisP->vctsize, CDI_DATATYPE_FLT64,
                    packBuffer, packBufferSize, packBufferPos, context);
    d = cdiCheckSum(CDI_DATATYPE_FLT64, zaxisP->vctsize, zaxisP->vct);
    serializePack(&d, 1, CDI_DATATYPE_UINT32,
                    packBuffer, packBufferSize, packBufferPos, context);
    }

{
    const char *strTab[] = ZAXIS_STR_SERIALIZE;
    int numStr = sizeof (strTab) / sizeof (strTab[0]);
    serializeStrTabPack(strTab, numStr,
                        packBuffer, packBufferSize, packBufferPos, context);
}

serializePack(&zaxisP->positive, 1, CDI_DATATYPE_UINT,
                packBuffer, packBufferSize, packBufferPos, context);

if (memberMask & zaxisHasUUIDFlag)
    serializePack(zaxisP->uuid, CDI_UUID_SIZE, CDI_DATATYPE_UCHAR,
                packBuffer, packBufferSize, packBufferPos, context);

}


void cdiZaxisGetIndexList(unsigned nzaxis, int *zaxisResHs)
{
reshGetResHListOfType(nzaxis, zaxisResHs, &zaxisOps);
}

#undef ZAXIS_STR_SERIALIZE


static const char cdi_libvers[] = "1.9.6rc1";
const char *cdiLibraryVersion(void)
{
return cdi_libvers;
}
