/* Do not edit this file. It is produced from the corresponding .m4 source */
/*
 *  Copyright (C) 2003, Northwestern University and Argonne National Laboratory
 *  See COPYRIGHT notice in top-level directory.
 */
/* $Id: test_read.m4 2861 2017-02-09 19:38:02Z wkliao $ */


#if defined (_WIN32) || defined (_WIN64)
#include <io.h>
#endif

#include <sys/types.h> /* open() */
#include <sys/stat.h>  /* open() */
#include <fcntl.h>     /* open() */
#ifdef _MSC_VER
#include <io.h>
#else
#include <unistd.h>    /* unlink(), write() */
#endif
#include <errno.h>     /* errno, strerror() */

#include "tests.h"






/*
 * Test  nc_strerror.
 *    Try on a bad error status.
 *    Test for each defined error status.
 */
int
test_nc_strerror(void)
{
    int i;
    const char *message, *expected_msg;
    int nok=0;

    static const struct {
        int status;
        const char *msg;
    } ncerrs[] = {
        {NC_NOERR, "No error"},
        {NC_EBADID, "NetCDF: Not a valid ID"},
        {NC_ENFILE, "NetCDF: Too many files open"},
        {NC_EEXIST, "NetCDF: File exists && NC_NOCLOBBER"},
        {NC_EINVAL, "NetCDF: Invalid argument"},
        {NC_EPERM, "NetCDF: Write to read only"},
        {NC_ENOTINDEFINE, "NetCDF: Operation not allowed in data mode"},
        {NC_EINDEFINE, "NetCDF: Operation not allowed in define mode"},
        {NC_EINVALCOORDS, "NetCDF: Index exceeds dimension bound"},
        {NC_EMAXDIMS, "NetCDF: NC_MAX_DIMS exceeded"},  /* not enforced after 4.5.0 */
        {NC_ENAMEINUSE, "NetCDF: String match to name in use"},
        {NC_ENOTATT, "NetCDF: Attribute not found"},
        {NC_EMAXATTS, "NetCDF: NC_MAX_ATTRS exceeded"}, /* not enforced after 4.5.0 */
        {NC_EBADTYPE, "NetCDF: Not a valid data type or _FillValue type mismatch"},
        {NC_EBADDIM, "NetCDF: Invalid dimension ID or name"},
        {NC_EUNLIMPOS, "NetCDF: NC_UNLIMITED in the wrong index"},
        {NC_EMAXVARS, "NetCDF: NC_MAX_VARS exceeded"}, /* not enforced after 4.5.0 */
        {NC_ENOTVAR, "NetCDF: Variable not found"},
        {NC_EGLOBAL, "NetCDF: Action prohibited on NC_GLOBAL varid"},
        {NC_ENOTNC, "NetCDF: Unknown file format"},
        {NC_ESTS, "NetCDF: In Fortran, string too short"},
        {NC_EMAXNAME, "NetCDF: NC_MAX_NAME exceeded"},
        {NC_EUNLIMIT, "NetCDF: NC_UNLIMITED size already in use"},
        {NC_ENORECVARS, "NetCDF: nc_rec op when there are no record vars"},
        {NC_ECHAR, "NetCDF: Attempt to convert between text & numbers"},
        {NC_EEDGE, "NetCDF: Start+count exceeds dimension bound"},
        {NC_ESTRIDE, "NetCDF: Illegal stride"},
        {NC_EBADNAME, "NetCDF: Name contains illegal characters"},
        {NC_ERANGE, "NetCDF: Numeric conversion not representable"},
        {NC_ENOMEM, "NetCDF: Memory allocation (malloc) failure"},
        {NC_EVARSIZE, "NetCDF: One or more variable sizes violate format constraints"},
        {NC_EDIMSIZE, "NetCDF: Invalid dimension size"}
    };

    /* Try on a bad error status */
    message =  nc_strerror(-666);/* should fail */
    expected_msg = "Unknown Error";
    IF (strncmp(message, expected_msg, strlen(expected_msg)) != 0)
        error(" nc_strerror on bad error status returned: %s", message);
    ELSE_NOK

    /* Try on each legitimate error status */
    for (i=0; i<LEN_OF(ncerrs); i++) {
        const char *message =  nc_strerror(ncerrs[i].status);
        IF (strcmp(message, ncerrs[i].msg) != 0)
            error(" nc_strerror(%d) should return %s, not %s",
                  ncerrs[i].status, ncerrs[i].msg, message);
        ELSE_NOK
    }
    return nok;
}


/*
 * Test  nc_open.
 * If in read-only section of tests,
 *    Try to open a non-existent netCDF file, check error return.
 *    Open a file that is not a netCDF file, check error return.
 *    Open a netCDF file with a bad mode argument, check error return.
 *    Open a netCDF file with NC_NOWRITE, read-only mode, try to write, check error.
 *    Try to open a netcdf twice, check whether returned netcdf ids different.
 * If in writable section of tests,
 *    Open a netCDF file with NC_WRITE mode, write something, close it.
 * On exit, any open netCDF files are closed.
 */
#define NOT_NC_FILE "dummy_not_nc_file"
int
test_nc_open(void)
{
    int err, ncid, ncid2, nok=0;
#if 0
    int fd;
    ssize_t w_len;
#endif

    /* Try to open a nonexistent file */
    err = file_open("tooth-fairy.nc", NC_NOWRITE, &ncid); /* should fail */

    /* on some systems (such as Lustre), opening an nonexisting file will
     * actually create the file. In this case, we print the error messages on
     * screen and move on to the next test, instead of aborting the entire test.
     * The created file will be of zero-length and PnetCDF should complain it
     * is not an NC file, i.e. NC_ENOTNC.
     */
    IF (err == NC_NOERR) {
        error("opening a nonexistent file expects to fail, but got NC_NOERR\n");
    }

#ifndef USE_PARALLEL
    IF (! NC_ISSYSERR(err))
        error("nc_open of nonexistent file should have returned system error");
#endif

    /* Open a file that is not a netCDF file.  But need a portable
     * test that also works for cross-compiles ... */
    /* err = nc_open("nc_test.o", NC_NOWRITE, &ncid);/\* should fail *\/ */
    /* IF (err != NC_ENOTNC) */
    /*  error("nc_open of non-netCDF file: status = %d", err); */

#if 0
    /* create a not-nc file */
    fd = open(NOT_NC_FILE, O_CREAT|O_WRONLY, 0600);
    IF (fd == -1) {
        error("Error: creating a non-CDF file (%s)", strerror(errno));
    }
    else {
        w_len = write(fd, "0123456789abcdefghijklmnopqrstuvwxyz", 36);
        assert(w_len >= 0);
        close(fd);
    }

    /* Open a file that is not a netCDF file. */
    err = file_open(NOT_NC_FILE, NC_NOWRITE, &ncid); /* should fail */
    IF (err != NC_ENOTNC)
        error("expecting NC_ENOTNC or NC_EFILE but got %s", nc_err_code_name(err));
    ELSE_NOK

    /* delete the not-nc file */
    unlink(NOT_NC_FILE);
#endif

    /* Open a netCDF file in read-only mode, check that write fails */
    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    ELSE_NOK
    err =  nc_redef(ncid);        /* should fail */
    IF (err != NC_EPERM)
        error("expecting NC_EPERM but got %s", nc_err_code_name(err));
    /* Opened OK, see if can open again and get a different netCDF ID */
    err = file_open(testfile, NC_NOWRITE, &ncid2);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    else {
         nc_close(ncid2);
        nok++;
    }
    IF (ncid2 == ncid)
        error("netCDF IDs for first and second open calls should differ");

    
    {   /* tests using netCDF scratch file */
        err = file_create(scratch, NC_NOCLOBBER, &ncid2);
        IF (err != NC_NOERR)
            error("create: %s",  nc_strerror(err));
        else
             nc_close(ncid2);
        err = file_open(scratch, NC_WRITE, &ncid2);
        IF (err != NC_NOERR)
            error("open: %s",  nc_strerror(err));
        else {
             nc_close(ncid2);
            nok++;
        }
        err = nc_delete(scratch);
        IF (err != NC_NOERR)
            error("remove of %s failed", scratch);
    }

    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


/*
 * Test  nc_close.
 *    Try to close a netCDF file twice, check whether second close fails.
 *    Try on bad handle, check error return.
 *    Try in define mode and data mode.
 */
int
test_nc_close(void)
{
    int ncid, nok=0;
    int err = file_open(testfile, NC_NOWRITE, &ncid);

    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    /* Close a netCDF file twice, second time should fail */
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close failed: %s",  nc_strerror(err));
    ELSE_NOK
    err =  nc_close(ncid);
    IF (err != NC_EBADID)
        error("expecting NC_EBADID but got %s", nc_err_code_name(err));
    ELSE_NOK

    /* Try with a bad netCDF ID */
    err =  nc_close(BAD_ID);/* should fail */
    IF (err != NC_EBADID)
        error("expecting NC_EBADID but got %s", nc_err_code_name(err));
    ELSE_NOK

    /* Close in data mode */
    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close in data mode failed: %s",  nc_strerror(err));
    ELSE_NOK

    
    {   /* tests using netCDF scratch file */
        err = file_create(scratch, NC_NOCLOBBER, &ncid);
        IF (err != NC_NOERR)
            error("create: %s",  nc_strerror(err));
        err =  nc_close(ncid);
        IF (err != NC_NOERR)
            error("close in define mode: %s",  nc_strerror(err));
        ELSE_NOK
        err = nc_delete(scratch);
        IF (err != NC_NOERR)
            error("remove of %s failed", scratch);
    }
    return nok;
}


/*
 * Test  nc_inq
 *    Try on bad handle, check error return.
 *    Try in data mode, check returned values.
 *    Try asking for subsets of info.
 * If in writable section of tests,
 *    Try in define mode, after adding an unlimited dimension, variable.
 * On exit, any open netCDF files are closed.
 */
int
test_nc_inq(void)
{
    int ncid;
    int ndims;                        /* number of dimensions */
    int nvars;                        /* number of variables */
    int ngatts;                       /* number of global attributes */
    int recdim;                       /* id of unlimited dimension */
    int err;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    /* Try on bad handle */
    err =  nc_inq(BAD_ID, 0, 0, 0, 0);
    IF (err != NC_EBADID)
        error("expecting NC_EBADID but got %s", nc_err_code_name(err));
    ELSE_NOK

    err =  nc_inq(ncid, &ndims, &nvars, &ngatts, &recdim);
    IF (err != NC_NOERR)
        error("inq: %s",  nc_strerror(err));
    else IF (ndims != NDIMS)
        error("inq: wrong number of dimensions returned, %d", ndims);
    else IF (nvars != numVars)
        error("inq: wrong number of variables returned, %d", nvars);
    else IF (ngatts != numGatts)
        error("inq: wrong number of global atts returned, %d", ngatts);
    else IF (recdim != RECDIM)
        error("inq: wrong record dimension ID returned, %d", recdim);
    ELSE_NOK

    /* Inguire for no info (useless, but should still work) */
    err =  nc_inq(ncid, 0, 0, 0, 0);
    IF (err != NC_NOERR)
        error("inq for no info failed: %s",  nc_strerror(err));
    ELSE_NOK

    /* Inguire for subsets of info */
    ngatts = numGatts - 1;        /* wipe out previous correct value */
    err =  nc_inq(ncid, 0, 0, &ngatts, 0);
    IF (err != NC_NOERR)
        error("inq for one item failed: %s",  nc_strerror(err));
    else IF (ngatts != numGatts)
        error("inq subset: wrong number of global atts returned, %d", ngatts);
    ELSE_NOK
    ndims = NDIMS - 1;
    nvars = numVars - 1;
    err =  nc_inq(ncid, &ndims, &nvars, 0, 0);
    IF (err != NC_NOERR)
        error("inq for two items failed: %s",  nc_strerror(err));
    else IF (ndims != NDIMS)
        error("inq subset: wrong number of dimensions returned, %d", ndims);
    else IF (nvars != numVars)
        error("inq subset: wrong number of variables returned, %d", nvars);
    ELSE_NOK

    
    {   /* tests using netCDF scratch file */
        int ncid2;              /* for scratch netCDF dataset */

        err = file_create(scratch, NC_NOCLOBBER, &ncid2);
        IF (err != NC_NOERR) {
            error("create: %s",  nc_strerror(err));
        } else {                /* add dim, var, gatt, check inq */
            int ndims0;
            int nvars0;
            int ngatts0;
            int recdim0;
            err =  nc_enddef(ncid2); /* enter data mode */
            err =  nc_inq(ncid2, &ndims0, &nvars0, &ngatts0, &recdim0);
            IF (err != NC_NOERR)
                error("inq: %s",  nc_strerror(err));
            ELSE_NOK
            err =  nc_redef(ncid2); /* enter define mode */
            /* Check that inquire still works in define mode */
            err =  nc_inq(ncid2, &ndims, &nvars, &ngatts, &recdim);
            IF (err != NC_NOERR)
                error("inq in define mode: %s",  nc_strerror(err));
            else IF (ndims != ndims0)
                error("inq in define mode: ndims wrong, %d", ndims);
            else IF (nvars != nvars0)
                error("inq in define mode: nvars wrong, %d", nvars);
            else IF (ngatts != ngatts0)
                error("inq in define mode: ngatts wrong, %d", ngatts);
            else IF (recdim != recdim0)
                error("inq in define mode: recdim wrong, %d", recdim);
            ELSE_NOK

            {
                int did, vid;
                /* Add dim, var, global att */
                err =  nc_def_dim(ncid2, "inqd", 1L, &did);
                IF (err != NC_NOERR)
                    error("def_dim: %s",  nc_strerror(err));
                err =  nc_def_var(ncid2, "inqv", NC_FLOAT, 0, 0, &vid);
                IF (err != NC_NOERR)
                    error("def_var: %s",  nc_strerror(err));
            }
            err =  nc_put_att_text(ncid2, NC_GLOBAL, "inqa", 1+strlen("stuff"),
                                   "stuff");
            IF (err != NC_NOERR)
                error("put_att_text: %s",  nc_strerror(err));

            /* Make sure  nc_inq sees the additions while in define mode */
            err =  nc_inq(ncid2, &ndims, &nvars, &ngatts, &recdim);
            IF (err != NC_NOERR)
                error("inq in define mode: %s",  nc_strerror(err));
            else IF (ndims != ndims0 + 1)
                error("inq in define mode: ndims wrong, %d", ndims);
            else IF (nvars != nvars0 + 1)
                error("inq in define mode: nvars wrong, %d", nvars);
            else IF (ngatts != ngatts0 + 1)
                error("inq in define mode: ngatts wrong, %d", ngatts);
            ELSE_NOK
            err =  nc_enddef(ncid2);
            IF (err != NC_NOERR)
                error("enddef: %s",  nc_strerror(err));

            /* Make sure  nc_inq stills sees additions in data mode */
            err =  nc_inq(ncid2, &ndims, &nvars, &ngatts, &recdim);
            IF (err != NC_NOERR)
                error("inq failed in data mode: %s",  nc_strerror(err));
            else IF (ndims != ndims0 + 1)
                error("inq in define mode: ndims wrong, %d", ndims);
            else IF (nvars != nvars0 + 1)
                error("inq in define mode: nvars wrong, %d", nvars);
            else IF (ngatts != ngatts0 + 1)
                error("inq in define mode: ngatts wrong, %d", ngatts);
            ELSE_NOK
             nc_close(ncid2);
            err = nc_delete(scratch);
            IF (err != NC_NOERR)
                error("remove of %s failed", scratch);
        }
    }

    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_natts(void)
{
    int ncid;
    int ngatts;                        /* number of global attributes */
    int err, nok=0;

    err =  nc_inq_natts(BAD_ID, &ngatts);
    IF (err != NC_EBADID)
        error("expecting NC_EBADID but got %s", nc_err_code_name(err));
    ELSE_NOK
    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    err =  nc_inq_natts(ncid, &ngatts);
    IF (err != NC_NOERR)
        error("inq_natts: %s",  nc_strerror(err));
    else IF (ngatts != numGatts)
        error("inq_natts: wrong number of global atts returned, %d", ngatts);
    ELSE_NOK
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_ndims(void)
{
    int ncid;
    int ndims;
    int err;
    int nok=0;

    err =  nc_inq_ndims(BAD_ID, &ndims);
    IF (err != NC_EBADID)
        error("expecting NC_EBADID but got %s", nc_err_code_name(err));
    ELSE_NOK
    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    err =  nc_inq_ndims(ncid, &ndims);
    IF (err != NC_NOERR)
        error("inq_ndims: %s",  nc_strerror(err));
    else IF (ndims != NDIMS)
        error("inq_ndims: wrong number returned, %d", ndims);
    ELSE_NOK
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_nvars(void)
{
    int ncid;
    int nvars;
    int err;
    int nok=0;

    err =  nc_inq_nvars(BAD_ID, &nvars);
    IF (err != NC_EBADID)
        error("expecting NC_EBADID but got %s", nc_err_code_name(err));
    ELSE_NOK
    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    err =  nc_inq_nvars(ncid, &nvars);
    IF (err != NC_NOERR)
        error("inq_nvars: %s",  nc_strerror(err));
    else IF (nvars != numVars)
        error("inq_nvars: wrong number returned, %d", nvars);
    ELSE_NOK
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_unlimdim(void)
{
    int ncid;
    int unlimdim;
    int err;
    int nok=0;

    err =  nc_inq_unlimdim(BAD_ID, &unlimdim);
    IF (err != NC_EBADID)
        error("expecting NC_EBADID but got %s", nc_err_code_name(err));
    ELSE_NOK
    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    err =  nc_inq_unlimdim(ncid, &unlimdim);
    IF (err != NC_NOERR)
        error("inq_unlimdim: %s",  nc_strerror(err));
    else IF (unlimdim != RECDIM)
        error("inq_unlimdim: wrong number returned, %d", unlimdim);
    ELSE_NOK
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_dimid(void)
{
    int ncid;
    int dimid;
    int i;
    int err;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    err =  nc_inq_dimid(ncid, "noSuch", &dimid);
    IF (err != NC_EBADDIM)
        error("expecting NC_EBADDIM but got %s", nc_err_code_name(err));
    ELSE_NOK
    for (i = 0; i < NDIMS; i++) {
        err =  nc_inq_dimid(BAD_ID, dim_name[i], &dimid);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_dimid(ncid, dim_name[i], &dimid);
        IF (err != NC_NOERR)
            error("inq_dimid: %s",  nc_strerror(err));
        else IF (dimid != i)
            error("expected %d, got %d", i, dimid);
        ELSE_NOK
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_dim(void)
{
    int ncid;
    int i;
    int err;
    char name[NC_MAX_NAME];
    size_t length;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    for (i = 0; i < NDIMS; i++) {
        err =  nc_inq_dim(BAD_ID, i, name, &length);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_dim(ncid, BAD_DIMID, name, &length);
        IF (err != NC_EBADDIM)
            error("expecting NC_EBADDIM but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_dim(ncid, i, 0, 0);
        IF (err != NC_NOERR)
            error("inq_dim: %s",  nc_strerror(err));
        ELSE_NOK
        err =  nc_inq_dim(ncid, i, name, &length);
        IF (err != NC_NOERR)
            error("inq_dim: %s",  nc_strerror(err));
        else IF (strcmp(dim_name[i],name))
            error("name expected: %s, got: %s",dim_name[i],name);
        else IF (dim_len[i] != length)
            error("size expected: %d, got: %d",dim_len[i],length);
        ELSE_NOK
        err =  nc_inq_dim(ncid, i, name, 0);
        IF (err != NC_NOERR)
            error("inq_dim: %s",  nc_strerror(err));
        else IF (strcmp(dim_name[i],name))
            error("name expected: %s, got: %s",dim_name[i],name);
        ELSE_NOK
        err =  nc_inq_dim(ncid, i, 0, &length);
        IF (err != NC_NOERR)
            error("inq_dim: %s",  nc_strerror(err));
        else IF (dim_len[i] != length)
            error("size expected: %d, got: %d",dim_len[i],length);
        ELSE_NOK
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_dimlen(void)
{
    int ncid;
    int i;
    int err;
    size_t  length;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    for (i = 0; i < NDIMS; i++) {
        err =  nc_inq_dimlen(BAD_ID, i, &length);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_dimlen(ncid, BAD_DIMID, &length);
        IF (err != NC_EBADDIM)
            error("expecting NC_EBADDIM but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_dimlen(ncid, i, &length);
        IF (err != NC_NOERR)
            error("inq_dimlen: %s",  nc_strerror(err));
        else IF (dim_len[i] != length)
            error("size expected: %d, got: %d",dim_len[i],length);
        ELSE_NOK
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_dimname(void)
{
    int ncid;
    int i;
    int err;
    char name[NC_MAX_NAME];
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    for (i = 0; i < NDIMS; i++) {
        err =  nc_inq_dimname(BAD_ID, i, name);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_dimname(ncid, BAD_DIMID, name);
        IF (err != NC_EBADDIM)
            error("expecting NC_EBADDIM but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_dimname(ncid, i, name);
        IF (err != NC_NOERR)
            error("inq_dimname: %s",  nc_strerror(err));
        else IF (strcmp(dim_name[i],name))
            error("name expected: %s, got: %s",dim_name[i],name);
        ELSE_NOK
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_varid(void)
{
    int ncid;
    int varid;
    int i;
    int err;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    err =  nc_inq_varid(ncid, "noSuch", &varid);
    IF (err != NC_ENOTVAR)
        error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
    ELSE_NOK

    for (i = 0; i < numVars; i++) {
        err =  nc_inq_varid(BAD_ID, var_name[i], &varid);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_varid(ncid, var_name[i], &varid);
        IF (err != NC_NOERR)
            error("inq_varid: %s",  nc_strerror(err));
        else IF (varid != i)
            error("expected %d, got %d", i, varid);
        ELSE_NOK
    }

    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_var(void)
{
    int ncid;
    int i;
    int err;
    char name[NC_MAX_NAME];
    nc_type datatype;
    int ndims;
    int dimids[MAX_RANK];
    int natts;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    for (i = 0; i < numVars; i++) {
        err =  nc_inq_var(BAD_ID, i, name, &datatype, &ndims, dimids, &natts);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_var(ncid,BAD_VARID,name,&datatype,&ndims,dimids,&natts);
        IF (err != NC_ENOTVAR)
            error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_var(ncid, i, 0, 0, 0, 0, 0);
        IF (err != NC_NOERR)
            error("inq_var: %s",  nc_strerror(err));
        ELSE_NOK
        err =  nc_inq_var(ncid, i, name, &datatype, &ndims, dimids, &natts);
        IF (err != NC_NOERR)
            error("inq_var: %s",  nc_strerror(err));
        else IF (strcmp(var_name[i],name))
            error("name expected: %s, got: %s",var_name[i],name);
        else IF (var_type[i] != datatype)
            error("type expected: %d, got: %d",var_type[i],datatype);
        else IF (var_rank[i] != ndims)
            error("ndims expected: %d, got: %d",var_rank[i],ndims);
        else IF (!int_vec_eq(var_dimid[i],dimids,ndims))
            error("unexpected dimid");
        else IF (var_natts[i] != natts)
            error("natts expected: %d, got: %d",var_natts[i],natts);
        ELSE_NOK
        err =  nc_inq_var(ncid, i, name, 0, 0, 0, 0);
        IF (err != NC_NOERR)
            error("inq_var: %s",  nc_strerror(err));
        else IF (strcmp(var_name[i],name))
            error("name expected: %s, got: %s",var_name[i],name);
        ELSE_NOK
        err =  nc_inq_var(ncid, i, 0, &datatype, 0, 0, 0);
        IF (err != NC_NOERR)
            error("inq_var: %s",  nc_strerror(err));
        else IF (var_type[i] != datatype)
            error("type expected: %d, got: %d",var_type[i],datatype);
        ELSE_NOK
        err =  nc_inq_var(ncid, i, 0, 0, &ndims, 0, 0);
        IF (err != NC_NOERR)
            error("inq_var: %s",  nc_strerror(err));
        else IF (var_rank[i] != ndims)
            error("ndims expected: %d, got: %d",var_rank[i],ndims);
        ELSE_NOK
        err =  nc_inq_var(ncid, i, 0, 0, 0, dimids, 0);
        IF (err != NC_NOERR)
            error("inq_var: %s",  nc_strerror(err));
        else IF (!int_vec_eq(var_dimid[i],dimids,ndims))
            error("unexpected dimid");
        ELSE_NOK
        err =  nc_inq_var(ncid, i, 0, 0, 0, 0, &natts);
        IF (err != NC_NOERR)
            error("inq_var: %s",  nc_strerror(err));
        else IF (var_natts[i] != natts)
            error("natts expected: %d, got: %d",var_natts[i],natts);
        ELSE_NOK
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_vardimid(void)
{
    int ncid;
    int i;
    int err;
    int dimids[MAX_RANK];
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    for (i = 0; i < numVars; i++) {
        err =  nc_inq_vardimid(BAD_ID, i, dimids);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_vardimid(ncid, BAD_VARID, dimids);
        IF (err != NC_ENOTVAR)
            error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_vardimid(ncid, i, dimids);
        IF (err != NC_NOERR)
            error("inq_vardimid: %s",  nc_strerror(err));
        else IF (!int_vec_eq(var_dimid[i], dimids, var_rank[i]))
            error("unexpected dimid");
        ELSE_NOK
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_varname(void)
{
    int ncid;
    int i;
    int err;
    char name[NC_MAX_NAME];
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    for (i = 0; i < numVars; i++) {
        err =  nc_inq_varname(BAD_ID, i, name);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        nok++;
        err =  nc_inq_varname(ncid, BAD_VARID, name);
        IF (err != NC_ENOTVAR)
            error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_varname(ncid, i, name);
        IF (err != NC_NOERR)
            error("inq_varname: %s",  nc_strerror(err));
        else IF (strcmp(var_name[i],name))
            error("name expected: %s, got: %s",var_name[i],name);
        ELSE_NOK
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_varnatts(void)
{
    int ncid;
    int i;
    int err;
    int natts;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    for (i = -1; i < numVars; i++) {
        err =  nc_inq_varnatts(BAD_ID, i, &natts);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_varnatts(ncid, BAD_VARID, &natts);
        IF (err != NC_ENOTVAR)
            error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_varnatts(ncid, VARID(i), &natts);
        IF (err != NC_NOERR)
            error("inq_varnatts: %s",  nc_strerror(err));
        else IF (NATTS(i) != natts)
            error("natts expected: %d, got: %d",NATTS(i),natts);
        ELSE_NOK
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_varndims(void)
{
    int ncid;
    int i;
    int err;
    int ndims;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    for (i = 0; i < numVars; i++) {
        err =  nc_inq_varndims(BAD_ID, i, &ndims);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_varndims(ncid, BAD_VARID, &ndims);
        IF (err != NC_ENOTVAR)
            error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_varndims(ncid, i, &ndims);
        IF (err != NC_NOERR)
            error("inq_varndims: %s",  nc_strerror(err));
        else IF (var_rank[i] != ndims)
            error("ndims expected: %d, got: %d",var_rank[i],ndims);
        ELSE_NOK
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_vartype(void)
{
    int ncid;
    int i;
    int err;
    nc_type datatype;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));
    for (i = 0; i < numVars; i++) {
        err =  nc_inq_vartype(BAD_ID, i, &datatype);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_vartype(ncid, BAD_VARID, &datatype);
        IF (err != NC_ENOTVAR)
            error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_vartype(ncid, i, &datatype);
        IF (err != NC_NOERR)
            error("inq_vartype: %s",  nc_strerror(err));
        else IF (var_type[i] != datatype)
            error("type expected: %d, got: %d", var_type[i], datatype);
        ELSE_NOK
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


/*
 * Test nc_get_var1(,,,)
 */
int
test_nc_get_var1(void)
{
    int ncid;
    int i;
    int err;
    double expect;
    int nok = 0;                /* count of valid comparisons */
    double buf[1];              /* (void *) buffer */
    double value[1];
    size_t j, index[MAX_RANK];
    

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR) error("open: %s",  nc_strerror(err));

    /* check if can detect a bad file ID */
    err = nc_get_var1(BAD_ID,0,NULL,NULL);
    IF (err != NC_EBADID) error("expecting NC_EBADID but got %s",nc_err_code_name(err));

    /* check if can detect a bad variable ID */
    err = nc_get_var1(ncid,BAD_VARID,NULL,NULL);
    IF (err != NC_ENOTVAR) error("expecting NC_ENOTVAR but got %s",nc_err_code_name(err));

    for (i = 0; i < numVars; i++) {
        assert(var_rank[i] <= MAX_RANK);
        assert(var_nels[i] <= MAX_NELS);

        /* check if can detect a bad file ID */
        err = nc_get_var1(BAD_ID,i,NULL,value);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s",nc_err_code_name(err));
        ELSE_NOK

        


        /* test NC_EINVALCOORDS */
        for (j = 0; j < var_rank[i]; j++) {
            index[j] = var_shape[i][j];
            err = nc_get_var1(ncid,i,index,buf);
            IF (err != NC_EINVALCOORDS)
                error("expecting NC_EINVALCOORDS but got %s",nc_err_code_name(err));
            ELSE_NOK
            index[j] = 0;
        }
        err = nc_get_var1(ncid,i,index,value);
        IF (err != NC_NOERR)
            error("expecting NC_NOERR but got %s",nc_err_code_name(err));
        ELSE_NOK

        /* check if the contents are supposed to be */
        for (j = 0; j < var_nels[i]; j++) {
            err = toMixedBase(j, var_rank[i], var_shape[i], index);
            IF (err != 0) error("error in toMixedBase");
            expect = hash( var_type[i], var_rank[i], index );
            err = nc_get_var1(ncid,i,index,buf);
            IF (err != NC_NOERR)
                error("expecting NC_NOERR but got %s",nc_err_code_name(err));
            ELSE_NOK
            err = nc2dbl( var_type[i], buf, &value[0]);
            IF (err)
                error("error in nc2dbl");
            if (inRange(expect,var_type[i])) {
                IF (!equal2(value[0],expect,var_type[i]))
                    error("expected: %G, got: %G", expect, value[0]);
                ELSE_NOK
            }
        }
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


/*
 * Test nc_get_vara(,,,,)
 * Choose a random point dividing each dim into 2 parts
 * Get 2^rank (nslabs) slabs so defined
 * Each get overwrites buffer, so check after each get.
 */
int
test_nc_get_vara(void)
{
    int ncid, d, i, k, err, nslabs;
    int nok = 0;      /* count of valid comparisons */
    size_t j, nels;
    size_t start[MAX_RANK];
    size_t edge[MAX_RANK];
    size_t index[MAX_RANK];
    size_t mid[MAX_RANK];
    
    double buf[MAX_NELS];        /* (void *) buffer */
    double expect;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    /* check if can detect a bad file ID */
    err = nc_get_vara(BAD_ID,0,NULL,NULL,NULL);
    IF (err != NC_EBADID) error("expecting NC_EBADID but got %s",nc_err_code_name(err));

    /* check if can detect a bad variable ID */
    err = nc_get_vara(ncid,BAD_VARID,NULL,NULL,NULL);
    IF (err != NC_ENOTVAR) error("expecting NC_ENOTVAR but got %s",nc_err_code_name(err));

    for (i = 0; i < numVars; i++) {
        assert(var_rank[i] <= MAX_RANK);
        assert(var_nels[i] <= MAX_NELS);

        /* check if can detect a bad file ID */
        err = nc_get_vara(BAD_ID,i,NULL,NULL,buf);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s",nc_err_code_name(err));
        ELSE_NOK

        

        for (j = 0; j < var_rank[i]; j++) {
            start[j] = 0;
            edge[j] = 1;
        }


        /* test NC_EINVALCOORDS, first when edge[*] > 0 */
        for (j = 0; j < var_rank[i]; j++) {
            index[j] = var_shape[i][j];
            err = nc_get_vara(ncid,i,index,edge,buf);
            IF (err != NC_EINVALCOORDS)
                error("expecting NC_EINVALCOORDS but got %s",nc_err_code_name(err));
            ELSE_NOK
            index[j] = 0;
            edge[j] = var_shape[i][j] + 1;  /* edge error check */
            err = nc_get_vara(ncid,i,start,edge,buf);
            IF (err != NC_EEDGE)
                error("expecting NC_EEDGE but got %s",nc_err_code_name(err));
            ELSE_NOK
            edge[j] = 1;
        }

        /* Check non-scalars for correct error returned even when there is
         * nothing to get (edge[j]==0) */
        for (j = 0; j < var_rank[i]; j++) edge[j] = 0;

        for (j = 0; j < var_rank[i]; j++) {
            if (var_dimid[i][j] == RECDIM) continue; /* skip record dim */
            start[j] = var_shape[i][j];
            err = nc_get_vara(ncid,i,start,edge,buf);
#ifdef RELAX_COORD_BOUND
            IF (err != NC_NOERR) /* allowed when edge[j]==0 */
                error("expecting NC_NOERR but got %s",nc_err_code_name(err));
#else
            IF (err != NC_EINVALCOORDS) /* not allowed even when edge[j]==0 */
                error("expecting NC_EINVALCOORDS but got %s",nc_err_code_name(err));
#endif
            ELSE_NOK
            start[j] = var_shape[i][j]+1;  /* out of boundary check */
            err = nc_get_vara(ncid,i,start,edge,buf);
            IF (err != NC_EINVALCOORDS)
                error("expecting NC_EINVALCOORDS but got %s",nc_err_code_name(err));
            ELSE_NOK
            start[j] = 0;
        }
        for (j = 0; j < var_rank[i]; j++) edge[j] = 1;

        err = nc_get_vara(ncid,i,start,edge,buf);
        IF (err != NC_NOERR)
            error("expecting NC_NOERR but got %s",nc_err_code_name(err));
        ELSE_NOK

        /* Choose a random point dividing each dim into 2 parts */
        /* get 2^rank (nslabs) slabs so defined */
        nslabs = 1;
        for (j = 0; j < var_rank[i]; j++) {
            mid[j] = roll( var_shape[i][j] );
            nslabs *= 2;
        }
        /* bits of k determine whether to get lower or upper part of dim */
        for (k = 0; k < nslabs; k++) {
            nels = 1;
            for (j = 0; j < var_rank[i]; j++) {
                if ((k >> j) & 1) {
                    start[j] = 0;
                    edge[j] = mid[j];
                }else{
                    start[j] = mid[j];
                    edge[j] = var_shape[i][j] - mid[j];
                }
                nels *= edge[j];
            }
            err = nc_get_vara(ncid,i,start,edge,buf);
            IF (err != NC_NOERR)
                error("expecting NC_NOERR but got %s",nc_err_code_name(err));
            ELSE_NOK

            for (j = 0; j < nels; j++) {
                double got;
                char *p = (char *) buf;
                p += j * (size_t)nctypelen(var_type[i]);
                err = nc2dbl( var_type[i], p, & got );
                IF (err) error("error in nc2dbl");
                err = toMixedBase(j, var_rank[i], edge, index);
                IF (err != 0) error("error in toMixedBase");
                for (d = 0; d < var_rank[i]; d++)
                    index[d] += start[d];
                expect = hash(var_type[i], var_rank[i], index);
                if (inRange(expect,var_type[i])) {
                    IF (!equal2(got,expect,var_type[i])) {
                        error("buf read not that expected");
                        if (verbose) {
                            error("\n");
                            error("varid: %d, ", i);
                            error("var_name: %s, ", var_name[i]);
                            error("element number: %d ", j);
                            error("expect: %g", expect);
                            error("got: %g", got);
                        }
                    }
                }
            }
        }
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


/*
 * Test nc_get_vars(,,,,,)
 * Choose a random point dividing each dim into 2 parts
 * Get 2^rank (nslabs) slabs so defined
 * Each get overwrites buffer, so check after each get.
 */
int
test_nc_get_vars(void)
{
    int ncid;
    int d;
    int i;
    int k;
    int err;
    int nslabs;
    ptrdiff_t nstarts;   /* number of different starts */
    int nok = 0;        /* total count of valid comparisons */
    int n;              /* count of valid comparisons within var */
    size_t j, m, nels;
    size_t start[MAX_RANK];
    size_t edge[MAX_RANK];
    size_t index[MAX_RANK];
    size_t index2[MAX_RANK];
    size_t mid[MAX_RANK];
    size_t count[MAX_RANK];
    size_t sstride[MAX_RANK];
    ptrdiff_t stride[MAX_RANK];
    
    double buf[MAX_NELS];     /* (void *) buffer */
    char *p;                  /* (void *) pointer */
    double expect;
    double got;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    /* check if can detect a bad file ID */
    err = nc_get_vars(BAD_ID,0,NULL,NULL,NULL,NULL);
    IF (err != NC_EBADID) error("expecting NC_EBADID but got %s",nc_err_code_name(err));

    /* check if can detect a bad variable ID */
    err = nc_get_vars(ncid,BAD_VARID,NULL,NULL,NULL,NULL);
    IF (err != NC_ENOTVAR) error("expecting NC_ENOTVAR but got %s",nc_err_code_name(err));

    for (i = 0; i < numVars; i++) {
        assert(var_rank[i] <= MAX_RANK);
        assert(var_nels[i] <= MAX_NELS);

        /* check if can detect a bad file ID */
        err = nc_get_vars(BAD_ID,i,NULL,NULL,NULL,buf);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s",nc_err_code_name(err));
        ELSE_NOK

        

        for (j = 0; j < var_rank[i]; j++) {
            start[j] = 0;
            edge[j] = 1;
            stride[j] = 1;
        }


        /* test NC_EINVALCOORDS, first when edge[*] > 0 */
        for (j = 0; j < var_rank[i]; j++) {
            start[j] = var_shape[i][j];
            err = nc_get_vars(ncid,i,start,edge,stride,buf);
            IF (err != NC_EINVALCOORDS)
                error("expecting NC_EINVALCOORDS but got %s",nc_err_code_name(err));
            ELSE_NOK
            start[j] = 0;
            edge[j] = var_shape[i][j] + 1;
            err = nc_get_vars(ncid,i,start,edge,stride,buf);
            IF (err != NC_EEDGE)
                error("expecting NC_EEDGE but got %s",nc_err_code_name(err));
            ELSE_NOK
            edge[j] = 1;
            stride[j] = 0;
            err = nc_get_vars(ncid,i,start,edge,stride,buf);
            IF (err != NC_ESTRIDE)
                error("expecting NC_ESTRIDE but got %s",nc_err_code_name(err));
            ELSE_NOK
            stride[j] = 1;
        }
        /* Check non-scalars for correct error returned even when there is
         * nothing to get (edge[j]==0) */
        for (j = 0; j < var_rank[i]; j++) edge[j] = 0;

        for (j = 0; j < var_rank[i]; j++) {
            if (var_dimid[i][j] == RECDIM) continue; /* skip record dim */
            start[j] = var_shape[i][j];
            err = nc_get_vars(ncid,i,start,edge,stride,buf);
#ifdef RELAX_COORD_BOUND
            IF (err != NC_NOERR) /* allowed when edge[j]==0 */
                error("expecting NC_NOERR but got %s",nc_err_code_name(err));
#else
            IF (err != NC_EINVALCOORDS) /* not allowed even when edge[j]==0 */
                error("expecting NC_EINVALCOORDS but got %s",nc_err_code_name(err));
#endif
            ELSE_NOK
            start[j] = var_shape[i][j]+1;  /* out of boundary check */
            err = nc_get_vars(ncid,i,start,edge,stride,buf);
            IF (err != NC_EINVALCOORDS)
                error("expecting NC_EINVALCOORDS but got %s",nc_err_code_name(err));
            ELSE_NOK
            start[j] = 0;
        }
        for (j = 0; j < var_rank[i]; j++) edge[j] = 1;

        err = nc_get_vars(ncid,i,start,edge,stride,buf);
        IF (err != NC_NOERR)
            error("expecting NC_NOERR but got %s",nc_err_code_name(err));
        ELSE_NOK

        /* Choose a random point dividing each dim into 2 parts */
        /* get 2^rank (nslabs) slabs so defined */
        nslabs = 1;
        for (j = 0; j < var_rank[i]; j++) {
            mid[j] = roll( var_shape[i][j] );
            nslabs *= 2;
        }
        /* bits of k determine whether to get lower or upper part of dim */
        /* choose random stride from 1 to edge */
        n = 0;
        for (k = 0; k < nslabs; k++) {
            nstarts = 1;
            for (j = 0; j < var_rank[i]; j++) {
                if ((k >> j) & 1) {
                    start[j] = 0;
                    edge[j] = mid[j];
                }else{
                    start[j] = mid[j];
                    edge[j] = var_shape[i][j] - mid[j];
                }
                sstride[j] = edge[j] > 0 ? 1+roll(edge[j]) : 1;
                stride[j] = (ptrdiff_t)sstride[j];
                nstarts *= stride[j];
            }
            for (m = 0; m < nstarts; m++) {
                err = toMixedBase(m, var_rank[i], sstride, index);
                IF (err != 0) error("error in toMixedBase");
                nels = 1;
                for (j = 0; j < var_rank[i]; j++) {
                    count[j] = 1 + (edge[j] - index[j] - 1) / (size_t)stride[j];
                    nels *= count[j];
                    index[j] += start[j];
                }
                /* Random choice of forward or backward */
/* TODO
                if ( roll(2) ) {
                    for (j = 0; j < var_rank[i]; j++) {
                        index[j] += (count[j] - 1) * (size_t)stride[j];
                        stride[j] = -stride[j];
                    }
                }
 */
                err = nc_get_vars(ncid,i,index,count,stride,buf);
                IF (err != NC_NOERR)
                    error("%s",  nc_strerror(err));
                ELSE_NOK

                for (j = 0; j < nels; j++) {
                    p = (char *) buf;
                    p += j * (size_t)nctypelen(var_type[i]);
                    err = nc2dbl( var_type[i], p, & got );
                    IF (err != NC_NOERR)
                        error("error in nc2dbl");
                    err = toMixedBase(j, var_rank[i], count, index2);
                    IF (err != 0) error("error in toMixedBase");
                    for (d = 0; d < var_rank[i]; d++)
                        index2[d] = index[d] + index2[d] * (size_t)stride[d];
                    expect = hash(var_type[i], var_rank[i], index2);
                    if (inRange(expect,var_type[i])) {
                        IF (!equal2(got,expect,var_type[i])) {
                            error("buf read not that expected");
                            if (verbose) {
                                error("\n");
                                error("varid: %d, ", i);
                                error("var_name: %s, ", var_name[i]);
                                error("element number: %d ", j);
                                error("expect: %g, ", expect);
                                error("got: %g ", got);
                            }
                        }
                        ELSE_NOK
                    }
                    n++;
                }
            }
        }
        IF (n != var_nels[i]) {
            error("count != nels");
            if (verbose) {
                error("\n");
                error("varid: %d, ", i);
                error("var_name: %s, ", var_name[i]);
                error("count: %d, ", n);
                error("nels: %d ", var_nels[i]);
            }
        }
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


/*
 * Test nc_get_varm(,,,,,,)
 * Choose a random point dividing each dim into 2 parts
 * Get 2^rank (nslabs) slabs so defined
 * Choose random stride from 1 to edge
 * Buffer should end up being bit image of external variable.
 * So all gets for a variable store in different elements of buffer
 */
int
test_nc_get_varm(void)
{
    int ncid;
    int i;
    int k;
    int err;
    int nslabs;
    ptrdiff_t nstarts;   /* number of different starts */
    int nok = 0;        /* total count of valid comparisons */
    size_t j, m, nels;
    size_t start[MAX_RANK];
    size_t edge[MAX_RANK];
    size_t index[MAX_RANK];
    size_t mid[MAX_RANK];
    size_t count[MAX_RANK];
    size_t sstride[MAX_RANK];
    ptrdiff_t stride[MAX_RANK];
    ptrdiff_t imap[MAX_RANK];
    ptrdiff_t imap2[MAX_RANK];
    
    double buf[MAX_NELS];        /* (void *) buffer */
    char *p;                     /* (void *) pointer */
    double expect;
    double got;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    /* check if can detect a bad file ID */
    err = nc_get_varm(BAD_ID,0,NULL,NULL,NULL,NULL,NULL);
    IF (err != NC_EBADID) error("expecting NC_EBADID but got %s",nc_err_code_name(err));

    /* check if can detect a bad variable ID */
    err = nc_get_varm(ncid,BAD_VARID,NULL,NULL,NULL,NULL,NULL);
    IF (err != NC_ENOTVAR) error("expecting NC_ENOTVAR but got %s",nc_err_code_name(err));

    for (i = 0; i < numVars; i++) {
        assert(var_rank[i] <= MAX_RANK);
        assert(var_nels[i] <= MAX_NELS);

        /* check if can detect a bad file ID */
        err = nc_get_varm(BAD_ID,i,NULL,NULL,NULL,NULL,buf);
        IF (err != NC_EBADID)
            error("expecting NC_EBADID but got %s",nc_err_code_name(err));
        ELSE_NOK

        

        for (j = 0; j < var_rank[i]; j++) {
            start[j] = 0;
            edge[j] = 1;
            stride[j] = 1;
            imap[j] = 1;
        }


        /* test NC_EINVALCOORDS, first when edge[*] > 0 */
        for (j = 0; j < var_rank[i]; j++) {
            start[j] = var_shape[i][j];
            err = nc_get_varm(ncid,i,start,edge,stride,imap,buf);
            IF (err != NC_EINVALCOORDS)
                error("expecting NC_EINVALCOORDS but got %s",nc_err_code_name(err));
            ELSE_NOK
            start[j] = 0;
            edge[j] = var_shape[i][j] + 1;
            err = nc_get_varm(ncid,i,start,edge,stride,imap,buf);
            IF (err != NC_EEDGE)
                error("expecting NC_EEDGE but got %s",nc_err_code_name(err));
            ELSE_NOK
            edge[j] = 1;
            stride[j] = 0;
            err = nc_get_varm(ncid,i,start,edge,stride,imap,buf);
            IF (err != NC_ESTRIDE)
                error("expecting NC_ESTRIDE but got %s",nc_err_code_name(err));
            ELSE_NOK
            stride[j] = 1;
        }
        /* Check non-scalars for correct error returned even when there is
         * nothing to get (edge[j]==0) */
        for (j = 0; j < var_rank[i]; j++) edge[j] = 0;

        for (j = 0; j < var_rank[i]; j++) {
            if (var_dimid[i][j] == RECDIM) continue; /* skip record dim */
            start[j] = var_shape[i][j];
            err = nc_get_varm(ncid,i,start,edge,stride,imap,buf);
#ifdef RELAX_COORD_BOUND
            IF (err != NC_NOERR) /* allowed when edge[j]==0 */
                error("expecting NC_NOERR but got %s",nc_err_code_name(err));
#else
            IF (err != NC_EINVALCOORDS) /* not allowed even when edge[j]==0 */
                error("expecting NC_EINVALCOORDS but got %s",nc_err_code_name(err));
#endif
            ELSE_NOK
            start[j] = var_shape[i][j]+1;  /* out of boundary check */
            err = nc_get_varm(ncid,i,start,edge,stride,imap,buf);
            IF (err != NC_EINVALCOORDS)
                error("expecting NC_EINVALCOORDS but got %s",nc_err_code_name(err));
            ELSE_NOK
            start[j] = 0;
        }
        for (j = 0; j < var_rank[i]; j++) edge[j] = 1;

        err = nc_get_varm(ncid,i,start,edge,stride,imap,buf);
        IF (err != NC_NOERR)
            error("expecting NC_NOERR but got %s",nc_err_code_name(err));
        ELSE_NOK

        if (var_rank[i] > 0) {
            int jj = var_rank[i] - 1;
            /* imap[jj] = nctypelen(var_type[i]); */
            imap[jj] = 1; /* in numbers of elements */
            for (; jj > 0; jj--)
                imap[jj-1] = imap[jj] * (ptrdiff_t)var_shape[i][jj];
        }

        /* Choose a random point dividing each dim into 2 parts */
        /* get 2^rank (nslabs) slabs so defined */
        nslabs = 1;
        for (j = 0; j < var_rank[i]; j++) {
            mid[j] = roll( var_shape[i][j] );
            nslabs *= 2;
        }
        /* bits of k determine whether to get lower or upper part of dim */
        /* choose random stride from 1 to edge */
        for (k = 0; k < nslabs; k++) {
            nstarts = 1;
            for (j = 0; j < var_rank[i]; j++) {
                if ((k >> j) & 1) {
                    start[j] = 0;
                    edge[j] = mid[j];
                }else{
                    start[j] = mid[j];
                    edge[j] = var_shape[i][j] - mid[j];
                }
                sstride[j] = edge[j] > 0 ? 1+roll(edge[j]) : 1;
                stride[j] = (ptrdiff_t)sstride[j];
                imap2[j] = imap[j] * stride[j];
                nstarts *= stride[j];
            }
            for (m = 0; m < nstarts; m++) {
                if (var_rank[i] == 0 && i%2 ) {
                    err = nc_get_varm(ncid,i,NULL,NULL,NULL,NULL,buf);
                } else {
                    err = toMixedBase(m, var_rank[i], sstride, index);
                    IF (err != 0) error("error in toMixedBase");
                    nels = 1;
                    for (j = 0; j < var_rank[i]; j++) {
                        count[j] = 1 + (edge[j] - index[j] - 1) / (size_t)stride[j];
                        index[j] += start[j];
                        nels *= count[j];
                    }
                    /* Random choice of forward or backward */
/* TODO
                    if ( roll(2) ) {
                        for (j = 0; j < var_rank[i]; j++) {
                            index[j] += (count[j] - 1) * (size_t)stride[j];
                            stride[j] = -stride[j];
                        }
                    }
 */
                    j = fromMixedBase(var_rank[i], index, var_shape[i]);
                    p = (char *) buf + j * (size_t)nctypelen(var_type[i]);
                    err = nc_get_varm(ncid,i,index,count,stride,imap2,p);
                }
                IF (err != NC_NOERR)
                    error("%s",  nc_strerror(err));
                ELSE_NOK
            }
        }
        p = (char *) buf;
        for (j = 0; j < var_nels[i]; j++) {
            err = toMixedBase(j, var_rank[i], var_shape[i], index);
            IF (err != 0) error("error in toMixedBase");
            expect = hash( var_type[i], var_rank[i], index);
            err = nc2dbl( var_type[i], p, & got );
            IF (err != NC_NOERR)
                error("error in nc2dbl");
            if (inRange(expect,var_type[i])) {
                IF (!equal2(got,expect,var_type[i])) {
                    error("buf read not that expected");
                    if (verbose) {
                        error("\n");
                        error("varid: %d, ", i);
                        error("var_name: %s, ", var_name[i]);
                        error("element number: %d ", j);
                        error("expect: %g, ", expect);
                        error("got: %g ", got);
                    }
                }
                ELSE_NOK
            }
            p += nctypelen(var_type[i]);
        }
    }
    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_get_att(void)
{
    int ncid;
    int i;
    int j;
    size_t k, ndx[1];
    int err;
    double buf[MAX_NELS];        /* (void *) buffer */
    char *p;                     /* (void *) pointer */
    double expect;
    double got;
    int nok = 0;      /* count of valid comparisons */

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    for (i = -1; i < numVars; i++) {
        for (j = 0; j < NATTS(i); j++) {
            err =  nc_get_att(BAD_ID, i, ATT_NAME(i,j), buf);
            IF (err != NC_EBADID)
                error("expecting NC_EBADID but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_get_att(ncid, BAD_VARID, ATT_NAME(i,j), buf);
            IF (err != NC_ENOTVAR)
                error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_get_att(ncid, i, "noSuch", buf);
            IF (err != NC_ENOTATT)
                error("expecting NC_ENOTATT but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_get_att(ncid, i, ATT_NAME(i,j), buf);
            IF (err != NC_NOERR) {
                error("%s",  nc_strerror(err));
            } else {
                nok++;
                for (k = 0; k < ATT_LEN(i,j); k++) {
                    ndx[0] = k;
                    expect = hash(ATT_TYPE(i,j), -1, ndx);
                    p = (char *) buf;
                    p += k * (size_t)nctypelen(ATT_TYPE(i,j));
                    err = nc2dbl( ATT_TYPE(i,j), p, &got );
                    IF (err != NC_NOERR)
                        error("error in nc2dbl");
                    if (inRange(expect,ATT_TYPE(i,j))) {
                        IF (!equal2(got,expect,ATT_TYPE(i,j))) {
                            error("buf read not that expected");
                            if (verbose) {
                                error("\n");
                                error("varid: %d, ", i);
                                error("var_name: %s, ",
                                        i >= 0 ? var_name[i] : "Global");
                                error("att_name: %s, ", ATT_NAME(i,j));
                                error("element number: %d\n", k);
                                error("expect: %-23.16e\n", expect);
                                error("   got: %-23.16e", got);
                            }
                        }
                    }
                }
            }
        }
    }

    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_att(void)
{
    int ncid;
    int i;
    int j;
    int err;
    nc_type t;
    size_t n;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    for (i = -1; i < numVars; i++) {
        for (j = 0; j < NATTS(i); j++) {
            err =  nc_inq_att(BAD_ID, i, ATT_NAME(i,j), &t, &n);
            IF (err != NC_EBADID)
                error("expecting NC_EBADID but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_att(ncid, BAD_VARID, ATT_NAME(i,j), &t, &n);
            IF (err != NC_ENOTVAR)
                error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_att(ncid, i, "noSuch", &t, &n);
            IF (err != NC_ENOTATT)
                error("expecting NC_ENOTATT but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_att(ncid, i, ATT_NAME(i,j), &t, &n);
            IF (err != NC_NOERR) {
                error("%s",  nc_strerror(err));
            } else {
                IF (t != ATT_TYPE(i,j))
                    error("type not that expected");
                ELSE_NOK
                IF (n != ATT_LEN(i,j))
                    error("length not that expected");
                ELSE_NOK
            }
        }
    }

    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_attlen(void)
{
    int ncid;
    int i;
    int j;
    int err;
    size_t len;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    for (i = -1; i < numVars; i++) {
        err =  nc_inq_attlen(ncid, i, "noSuch", &len);
        IF (err != NC_ENOTATT)
            error("expecting NC_ENOTATT but got %s", nc_err_code_name(err));
        ELSE_NOK
        for (j = 0; j < NATTS(i); j++) {
            err =  nc_inq_attlen(BAD_ID, i, ATT_NAME(i,j), &len);
            IF (err != NC_EBADID)
                error("expecting NC_EBADID but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_attlen(ncid, BAD_VARID, ATT_NAME(i,j), &len);
            IF (err != NC_ENOTVAR)
                error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_attlen(ncid, i, ATT_NAME(i,j), &len);
            IF (err != NC_NOERR) {
                error("%s",  nc_strerror(err));
            } else {
                IF (len != ATT_LEN(i,j))
                    error("len not that expected");
                ELSE_NOK
            }
        }
    }

    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_atttype(void)
{
    int ncid;
    int i;
    int j;
    int err;
    nc_type datatype;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    for (i = -1; i < numVars; i++) {
        err =  nc_inq_atttype(ncid, i, "noSuch", &datatype);
        IF (err != NC_ENOTATT)
            error("expecting NC_ENOTATT but got %s", nc_err_code_name(err));
        ELSE_NOK
        for (j = 0; j < NATTS(i); j++) {
            err =  nc_inq_atttype(BAD_ID, i, ATT_NAME(i,j), &datatype);
            IF (err != NC_EBADID)
                error("expecting NC_EBADID but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_atttype(ncid, BAD_VARID, ATT_NAME(i,j), &datatype);
            IF (err != NC_ENOTVAR)
                error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_atttype(ncid, i, ATT_NAME(i,j), &datatype);
            IF (err != NC_NOERR) {
                error("%s",  nc_strerror(err));
            } else {
                IF (datatype != ATT_TYPE(i,j))
                    error("type not that expected");
                ELSE_NOK
            }
        }
    }

    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_attname(void)
{
    int ncid;
    int i;
    int j;
    int err;
    char name[NC_MAX_NAME];
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    for (i = -1; i < numVars; i++) {
        err =  nc_inq_attname(ncid, i, BAD_ATTNUM, name);
        IF (err != NC_ENOTATT)
            error("expecting NC_ENOTATT but got %s", nc_err_code_name(err));
        ELSE_NOK
        err =  nc_inq_attname(ncid, i, NATTS(i), name);
        IF (err != NC_ENOTATT)
            error("expecting NC_ENOTATT but got %s", nc_err_code_name(err));
        ELSE_NOK
        for (j = 0; j < NATTS(i); j++) {
            err =  nc_inq_attname(BAD_ID, i, j, name);
            IF (err != NC_EBADID)
                error("expecting NC_EBADID but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_attname(ncid, BAD_VARID, j, name);
            IF (err != NC_ENOTVAR)
                error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_attname(ncid, i, j, name);
            IF (err != NC_NOERR) {
                error("%s",  nc_strerror(err));
            } else {
                IF (strcmp(ATT_NAME(i,j), name) != 0)
                    error("name not that expected");
                ELSE_NOK
            }
        }
    }

    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}


int
test_nc_inq_attid(void)
{
    int ncid;
    int i;
    int j;
    int err;
    int attnum;
    int nok=0;

    err = file_open(testfile, NC_NOWRITE, &ncid);
    IF (err != NC_NOERR)
        error("open: %s",  nc_strerror(err));

    for (i = -1; i < numVars; i++) {
        err =  nc_inq_attid(ncid, i, "noSuch", &attnum);
        IF (err != NC_ENOTATT)
            error("expecting NC_ENOTATT but got %s", nc_err_code_name(err));
        ELSE_NOK
        for (j = 0; j < NATTS(i); j++) {
            err =  nc_inq_attid(BAD_ID, i, ATT_NAME(i,j), &attnum);
            IF (err != NC_EBADID)
                error("expecting NC_EBADID but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_attid(ncid, BAD_VARID, ATT_NAME(i,j), &attnum);
            IF (err != NC_ENOTVAR)
                error("expecting NC_ENOTVAR but got %s", nc_err_code_name(err));
            ELSE_NOK
            err =  nc_inq_attid(ncid, i, ATT_NAME(i,j), &attnum);
            IF (err != NC_NOERR) {
                error("%s",  nc_strerror(err));
            } else {
                IF (attnum != j)
                    error("attnum not that expected");
                ELSE_NOK
            }
        }
    }

    err =  nc_close(ncid);
    IF (err != NC_NOERR)
        error("close: %s",  nc_strerror(err));
    return nok;
}
