/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* Programmer:	Robb Matzke <matzke@llnl.gov>
 *		Friday, October 10, 1997
 *
 * Purpose:	Hyperslab operations are rather complex, so this file
 *		attempts to test them extensively so we can be relatively
 *		sure they really work.	We only test 1d, 2d, and 3d cases
 *		because testing general dimensionalities would require us to
 *		rewrite much of the hyperslab stuff.
 */
#include "h5test.h"
#include "H5private.h"
#include "H5Eprivate.h"
#include "H5VMprivate.h"

#define TEST_SMALL	0x0001
#define TEST_MEDIUM	0x0002

#define VARIABLE_SRC	0
#define VARIABLE_DST	1
#define VARIABLE_BOTH	2

#define ARRAY_FILL_SIZE 4
#define ARRAY_OFFSET_NDIMS 3


/*-------------------------------------------------------------------------
 * Function:	init_full
 *
 * Purpose:	Initialize full array.
 *
 * Return:	void
 *
 * Programmer:	Robb Matzke
 *		Friday, October 10, 1997
 *
 *-------------------------------------------------------------------------
 */
static unsigned
init_full(uint8_t *array, size_t nx, size_t ny, size_t nz)
{
    uint8_t		    acc = 128;
    unsigned		    total = 0;
    size_t		    i, j, k;

    for(i = 0; i < nx; i++)
	for(j = 0; j < ny; j++)
	    for(k = 0; k < nz; k++) {
		total += acc;
		*array = acc;
		acc++;
		array++;
	    } /* end for */

    return total;
} /* end init_full() */


/*-------------------------------------------------------------------------
 * Function:	print_array
 *
 * Purpose:	Prints the values in an array
 *
 * Return:	void
 *
 * Programmer:	Robb Matzke
 *		Friday, October 10, 1997
 *
 *-------------------------------------------------------------------------
 */
static void
print_array(uint8_t *array, size_t nx, size_t ny, size_t nz)
{
    size_t	i, j, k;

    for(i = 0; i < nx; i++) {
	if(nz > 1)
	    printf("i=%lu:\n", (unsigned long)i);
	else
	    printf("%03lu:", (unsigned long)i);

	for(j = 0; j < ny; j++) {
	    if(nz > 1)
		printf("%03lu:", (unsigned long)j);
	    for(k = 0; k < nz; k++)
		printf(" %3d", *array++);
	    if(nz > 1)
		printf("\n");
	} /* end for */
	printf("\n");
    } /* end for */
} /* end print_array() */


/*-------------------------------------------------------------------------
 * Function:	print_ref
 *
 * Purpose:	Prints the reference value
 *
 * Return:	Success:	0
 *
 *		Failure:
 *
 * Programmer:	Robb Matzke
 *		Friday, October 10, 1997
 *
 *-------------------------------------------------------------------------
 */
static void
print_ref(size_t nx, size_t ny, size_t nz)
{
    uint8_t *array;

    if(NULL != (array = (uint8_t *)HDmalloc(nx * ny * nz))) {
        printf("Reference array:\n");
        init_full(array, nx, ny, nz);
        print_array(array, nx, ny, nz);
        HDfree(array);
    } /* end if */
} /* end print_ref() */


/*-------------------------------------------------------------------------
 * Function:	test_fill
 *
 * Purpose:	Tests the H5VM_hyper_fill() function.
 *
 * Return:	Success:	SUCCEED
 *
 *		Failure:	FAIL
 *
 * Programmer:	Robb Matzke
 *		Saturday, October 11, 1997
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_fill(size_t nx, size_t ny, size_t nz,
	  size_t di, size_t dj, size_t dk,
	  size_t ddx, size_t ddy, size_t ddz)
{
    uint8_t *dst = NULL;        /*destination array		*/
    hsize_t hs_size[3];         /*hyperslab size		*/
    hsize_t dst_size[3];        /*destination total size	*/
    hsize_t dst_offset[3];      /*offset of hyperslab in dest   */
    unsigned ref_value;         /*reference value		*/
    unsigned acc;               /*accumulator		    	*/
    size_t i, j, k, dx, dy, dz; /*counters		   	*/
    size_t u, v, w;
    unsigned ndims;             /*hyperslab dimensionality	*/
    char dim[64], s[256];       /*temp string		    	*/
    unsigned fill_value;        /*fill value		    	*/

    /*
     * Dimensionality.
     */
    if(0 == nz) {
        if(0 == ny) {
            ndims = 1;
            ny = nz = 1;
            sprintf(dim, "%lu", (unsigned long) nx);
        } /* end if */
        else {
            ndims = 2;
            nz = 1;
            sprintf(dim, "%lux%lu", (unsigned long) nx, (unsigned long) ny);
        } /* end else */
    } /* end if */
    else {
        ndims = 3;
        sprintf(dim, "%lux%lux%lu", (unsigned long) nx, (unsigned long) ny,
                (unsigned long) nz);
    } /* end else */
    sprintf(s, "Testing hyperslab fill %-11s variable hyperslab", dim);
    printf("%-70s", s);
    fflush(stdout);

    /* Allocate array */
    if(NULL == (dst = (uint8_t *)HDcalloc((size_t)1, nx * ny * nz)))
        TEST_ERROR
    
    init_full(dst, nx, ny, nz);

    for(i = 0; i < nx; i += di) {
        for(j = 0; j < ny; j += dj) {
            for(k = 0; k < nz; k += dk) {
                for(dx = 1; dx <= nx - i; dx += ddx) {
                    for(dy = 1; dy <= ny - j; dy += ddy) {
                        for(dz = 1; dz <= nz - k; dz += ddz) {

                            /* Describe the hyperslab */
                            dst_size[0] = nx;
                            dst_size[1] = ny;
                            dst_size[2] = nz;
                            dst_offset[0] = i;
                            dst_offset[1] = j;
                            dst_offset[2] = k;
                            hs_size[0] = dx;
                            hs_size[1] = dy;
                            hs_size[2] = dz;

                            for(fill_value = 0; fill_value < 256; fill_value += 64) {
                                /*
                                 * Initialize the full array, then subtract the
                                 * original * fill values and add the new ones.
                                 */
                                ref_value = init_full(dst, nx, ny, nz);
                                for(u = (size_t)dst_offset[0]; u < dst_offset[0] + dx; u++)
                                    for(v = (size_t)dst_offset[1]; v < dst_offset[1] + dy; v++)
                                        for(w = (size_t)dst_offset[2]; w < dst_offset[2] + dz; w++)
                                            ref_value -= dst[u * ny * nz + v * nz + w];
                                ref_value += fill_value * (unsigned)dx * (unsigned)dy * (unsigned)dz;

                                /* Fill the hyperslab with some value */
                                H5VM_hyper_fill(ndims, hs_size, dst_size, dst_offset, dst, fill_value);

                                /*
                                 * Sum the array and compare it to the
                                 * reference value.
                                 */
                                acc = 0;
                                for(u = 0; u < nx; u++)
                                    for(v = 0; v < ny; v++)
                                        for(w = 0; w < nz; w++)
                                            acc += dst[u * ny * nz + v * nz + w];

                                if(acc != ref_value) {
                                    H5_FAILED()
                                    if(!HDisatty(1)) {
                                        /*
                                         * Print debugging info unless output
                                         * is going directly to a terminal.
                                         */
                                        AT();
                                        printf("   acc != ref_value\n");
                                        printf("   i=%lu, j=%lu, k=%lu, "
                                            "dx=%lu, dy=%lu, dz=%lu, "
                                            "fill=%d\n", (unsigned long)i,
                                                (unsigned long)j,
                                                (unsigned long)k,
                                                (unsigned long)dx,
                                                (unsigned long)dy,
                                                (unsigned long)dz, fill_value);
                                        print_ref(nx, ny, nz);
                                        printf("\n   Result is:\n");
                                        print_array(dst, nx, ny, nz);
                                    } /* end if */
                                    goto error;
                                } /* end if */
                            } /* end for */
                        } /* end for */
                    } /* end for */
                } /* end for */
            } /* end for */
        } /* end for */
    } /* end for */

    PASSED()

    HDfree(dst);

    return SUCCEED;

error:
    if(dst)
        HDfree(dst);
    return FAIL;
} /* end test_fill() */


/*-------------------------------------------------------------------------
 * Function:	test_copy
 *
 * Purpose:	Tests H5VM_hyper_copy().
 *
 *		The NX, NY, and NZ arguments are the size for the source and
 *		destination arrays.  You may pass zero for NZ or for NY and
 *		NZ to test the 2-d and 1-d cases respectively.
 *
 *		A hyperslab is copied from/to (depending on MODE) various
 *		places in SRC and DST beginning at 0,0,0 and increasing
 *		location by DI,DJ,DK in the x, y, and z directions.
 *
 *		For each hyperslab location, various sizes of hyperslabs are
 *		tried beginning with 1x1x1 and increasing the size in each
 *		dimension by DDX,DDY,DDZ.
 *
 * Return:	Success:	SUCCEED
 *
 *		Failure:	FAIL
 *
 * Programmer:	Robb Matzke
 *		Friday, October 10, 1997
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_copy(int mode,
	  size_t nx, size_t ny, size_t nz,
	  size_t di, size_t dj, size_t dk,
	  size_t ddx, size_t ddy, size_t ddz)
{
    uint8_t    *src = NULL;     /*source array			*/
    uint8_t    *dst = NULL;     /*destination array		*/
    hsize_t     hs_size[3];     /*hyperslab size		*/
    hsize_t     dst_size[3];    /*destination total size	*/
    hsize_t     src_size[3];    /*source total size		*/
    hsize_t     dst_offset[3];  /*offset of hyperslab in dest	*/
    hsize_t     src_offset[3];  /*offset of hyperslab in source */
    unsigned    ref_value;      /*reference value		*/
    unsigned    acc;            /*accumulator			*/
    hsize_t     i, j, k, dx, dy, dz; /*counters		     	*/
    hsize_t     u, v, w;
    unsigned    ndims;          /*hyperslab dimensionality	*/
    char        dim[64], s[256]; /*temp string			*/
    const char *sub;

    /*
     * Dimensionality.
     */
    if(0 == nz) {
        if(0 == ny) {
            ndims = 1;
            ny = nz = 1;
            sprintf(dim, "%lu", (unsigned long) nx);
        } /* end if */
        else {
            ndims = 2;
            nz = 1;
            sprintf(dim, "%lux%lu", (unsigned long) nx, (unsigned long) ny);
        } /* end else */
    } /* end if */
    else {
        ndims = 3;
        sprintf(dim, "%lux%lux%lu", (unsigned long) nx, (unsigned long) ny,
                (unsigned long) nz);
    } /* end else */

    switch(mode) {
        case VARIABLE_SRC:
            /*
             * The hyperslab "travels" through the source array but the
             * destination hyperslab is always at the origin of the destination
             * array.
             */
            sub = "variable source";
            break;

        case VARIABLE_DST:
            /*
             * We always read a hyperslab from the origin of the source and copy it
             * to a hyperslab at various locations in the destination.
             */
            sub = "variable destination";
            break;

        case VARIABLE_BOTH:
            /*
             * We read the hyperslab from various locations in the source and copy
             * it to the same location in the destination.
             */
            sub = "sync source & dest  ";
            break;

        default:
            HDabort();
    } /* end switch */

    sprintf(s, "Testing hyperslab copy %-11s %s", dim, sub);
    printf("%-70s", s);
    fflush(stdout);

    /*
     * Allocate arrays
     */
    if(NULL == (src = (uint8_t *)HDcalloc((size_t)1, nx * ny * nz)))
        TEST_ERROR
    if(NULL == (dst = (uint8_t *)HDcalloc((size_t)1, nx * ny * nz)))
        TEST_ERROR

    init_full(src, nx, ny, nz);

    for(i = 0; i < nx; i += di) {
        for(j = 0; j < ny; j += dj) {
            for(k = 0; k < nz; k += dk) {
                for(dx = 1; dx <= nx - i; dx += ddx) {
                    for(dy = 1; dy <= ny - j; dy += ddy) {
                        for(dz = 1; dz <= nz - k; dz += ddz) {

                            /*
                             * Describe the source and destination hyperslabs
                             * and the arrays to which they belong.
                             */
                            hs_size[0] = dx;
                            hs_size[1] = dy;
                            hs_size[2] = dz;
                            dst_size[0] = src_size[0] = nx;
                            dst_size[1] = src_size[1] = ny;
                            dst_size[2] = src_size[2] = nz;
                            switch(mode) {
                                case VARIABLE_SRC:
                                    dst_offset[0] = 0;
                                    dst_offset[1] = 0;
                                    dst_offset[2] = 0;
                                    src_offset[0] = i;
                                    src_offset[1] = j;
                                    src_offset[2] = k;
                                    break;

                                case VARIABLE_DST:
                                    dst_offset[0] = i;
                                    dst_offset[1] = j;
                                    dst_offset[2] = k;
                                    src_offset[0] = 0;
                                    src_offset[1] = 0;
                                    src_offset[2] = 0;
                                    break;

                                case VARIABLE_BOTH:
                                    dst_offset[0] = i;
                                    dst_offset[1] = j;
                                    dst_offset[2] = k;
                                    src_offset[0] = i;
                                    src_offset[1] = j;
                                    src_offset[2] = k;
                                    break;

                                default:
                                    HDabort();
                            } /* end switch */

                            /*
                             * Sum the main array directly to get a reference
                             * value to compare against later.
                             */
                            ref_value = 0;
                            for(u = src_offset[0]; u < src_offset[0] + dx; u++)
                                for(v = src_offset[1]; v < src_offset[1] + dy; v++)
                                    for(w = src_offset[2]; w < src_offset[2] + dz; w++)
                                        ref_value += src[u * ny * nz + v * nz + w];

                            /*
                             * Set all loc values to 1 so we can detect writing
                             * outside the hyperslab.
                             */
                            for(u = 0; u < nx; u++)
                                for(v = 0; v < ny; v++)
                                    for(w = 0; w < nz; w++)
                                        dst[u * ny * nz + v * nz + w] = 1;

                            /*
                             * Copy a hyperslab from the global array to the
                             * local array.
                             */
                            H5VM_hyper_copy(ndims, hs_size, dst_size, dst_offset, dst, src_size, src_offset, src);

                            /*
                             * Sum the destination hyperslab.  It should be
                             * the same as the reference value.
                             */
                            acc = 0;
                            for(u = dst_offset[0]; u < dst_offset[0] + dx; u++)
                                for(v = dst_offset[1]; v < dst_offset[1] + dy; v++)
                                    for(w = dst_offset[2]; w < dst_offset[2] + dz; w++)
                                        acc += dst[u * ny * nz + v * nz + w];
                            if(acc != ref_value) {
                                H5_FAILED()
                                if(!HDisatty(1)) {
                                    /*
                                     * Print debugging info unless output is
                                     * going directly to a terminal.
                                     */
                                    AT();
                                    printf("   acc != ref_value\n");
                                    printf("   i=%lu, j=%lu, k=%lu, "
                                        "dx=%lu, dy=%lu, dz=%lu\n",
                                            (unsigned long)i,
                                            (unsigned long)j,
                                            (unsigned long)k,
                                            (unsigned long)dx,
                                            (unsigned long)dy,
                                            (unsigned long)dz);
                                    print_ref(nx, ny, nz);
                                    printf("\n	 Destination array is:\n");
                                    print_array(dst, nx, ny, nz);
                                } /* end if */
                                goto error;
                            } /* end if */

                            /*
                             * Sum the entire array. It should be a fixed
                             * amount larger than the reference value since
                             * we added the border of 1's to the hyperslab.
                             */
                            acc = 0;
                            for(u = 0; u < nx; u++)
                                for(v = 0; v < ny; v++)
                                    for(w = 0; w < nz; w++)
                                        acc += dst[u * ny * nz + v * nz + w];

                            /*
                             * The following casts are to work around an
                             * optimization bug in the Mongoose 7.20 Irix64
                             * compiler.
                             */
                            if(acc + (unsigned) dx * (unsigned) dy
                                    * (unsigned) dz != ref_value + nx * ny * nz) {
                                H5_FAILED()
                                if(!HDisatty(1)) {
                                    /*
                                     * Print debugging info unless output is
                                     * going directly to a terminal.
                                     */
                                    AT();
                                    printf("   acc != ref_value + nx*ny*nz - "
                                        "dx*dy*dz\n");
                                    printf("   i=%lu, j=%lu, k=%lu, "
                                        "dx=%lu, dy=%lu, dz=%lu\n",
                                            (unsigned long)i,
                                            (unsigned long)j,
                                            (unsigned long)k,
                                            (unsigned long)dx,
                                            (unsigned long)dy,
                                            (unsigned long)dz);
                                    print_ref(nx, ny, nz);
                                    printf("\n	 Destination array is:\n");
                                    print_array(dst, nx, ny, nz);
                                } /* end if */
                                goto error;
                            } /* end if */
                        } /* end for */
                    } /* end for */
                } /* end for */
            } /* end for */
        } /* end for */
    } /* end for */

    PASSED()

    HDfree(src);
    HDfree(dst);

    return SUCCEED;

error:
    if(src)
        HDfree(src);
    if(dst)
        HDfree(dst);

    return FAIL;
} /* end test_copy() */


/*-------------------------------------------------------------------------
 * Function:	test_multifill
 *
 * Purpose:	Tests the H5VM_stride_copy() function by using it to fill a
 *		hyperslab by replicating a multi-byte sequence.	 This might
 *		be useful to initialize an array of structs with a default
 *		struct value, or to initialize an array of floating-point
 *		values with a default bit-pattern.
 *
 * Return:	Success:	SUCCEED
 *
 *		Failure:	FAIL
 *
 * Programmer:	Robb Matzke
 *		Saturday, October 11, 1997
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_multifill(size_t nx)
{
    hsize_t		    size;
    hsize_t		    src_stride;
    hsize_t		    dst_stride;
    char		    s[64];
    struct a_struct {
        int left;
        double mid;
        int right;
    } fill, *src = NULL, *dst = NULL;
    hsize_t		    i, j;

    printf("%-70s", "Testing multi-byte fill value");
    fflush(stdout);

    /* Initialize the source and destination */
    if(NULL == (src = (struct a_struct *)HDmalloc(nx * sizeof(*src))))
        TEST_ERROR
    if(NULL == (dst = (struct a_struct *)HDmalloc(nx * sizeof(*dst))))
        TEST_ERROR

    for(i = 0; i < nx; i++) {
        src[i].left = 1111111;
        src[i].mid = 12345.6789F;
        src[i].right = 2222222;
        dst[i].left = 3333333;
        dst[i].mid = 98765.4321F;
        dst[i].right = 4444444;
    } /* end for */

    /*
     * Describe the fill value.	 The zero stride says to read the same thing
     * over and over again.
     */
    fill.left = 55555555;
    fill.mid = 3.1415927F;
    fill.right = 66666666;
    src_stride = 0;

    /*
     * The destination stride says to fill in one value per array element
     */
    dst_stride = sizeof(fill);

    /*
     * Copy the fill value into each element
     */
    size = nx;
    H5VM_stride_copy(1, (hsize_t)sizeof(double), &size, &dst_stride,
            &(dst[0].mid), &src_stride, &(fill.mid));

    /*
     * Check
     */
    s[0] = '\0';
    for(i = 0; i < nx; i++) {
        if(dst[i].left != 3333333)
            sprintf(s, "bad dst[%lu].left", (unsigned long)i);
        else if(!H5_DBL_ABS_EQUAL(dst[i].mid, fill.mid))
            /* Check if two DOUBLE values are equal.  If their difference
             * is smaller than the EPSILON value for double, they are
             * considered equal. See the definition in h5test.h.
             */
            sprintf(s, "bad dst[%lu].mid", (unsigned long)i);
        else if(dst[i].right != 4444444)
            sprintf(s, "bad dst[%lu].right", (unsigned long)i);
        if(s[0]) {
            H5_FAILED()
            if(!HDisatty(1)) {
                AT();
                printf("   fill={%d,%g,%d}\n   ", fill.left, fill.mid,
                        fill.right);
                for(j = 0; j < sizeof(fill); j++)
                    printf(" %02x", ((uint8_t *)&fill)[j]);
                printf("\n   dst[%lu]={%d,%g,%d}\n   ", (unsigned long)i,
                        dst[i].left, dst[i].mid, dst[i].right);
                for(j = 0; j < sizeof(dst[i]); j++)
                    printf(" %02x", ((uint8_t *)(dst + i))[j]);
                printf("\n");
            } /* end if */
            goto error;
        } /* end if */
    } /* end for */

    PASSED()

    HDfree(src);
    HDfree(dst);

    return SUCCEED;

error:
    if(src)
        HDfree(src);
    if(dst)
        HDfree(dst);

    return FAIL;
} /* end test_multifill() */


/*-------------------------------------------------------------------------
 * Function:	test_endian
 *
 * Purpose:	Tests the H5VM_stride_copy() function by using it to copy an
 *		array of integers and swap the byte ordering from little
 *		endian to big endian or vice versa depending on the hardware.
 *
 * Return:	Success:	SUCCEED
 *
 *		Failure:	FAIL
 *
 * Programmer:	Robb Matzke
 *		Saturday, October 11, 1997
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_endian(size_t nx)
{
    uint8_t	   *src = NULL;		/*source array			*/
    uint8_t	   *dst = NULL;		/*destination array		*/
    hssize_t	src_stride[2];		/*source strides		*/
    hssize_t	dst_stride[2];		/*destination strides		*/
    hsize_t	    size[2];		/*size vector			*/
    hsize_t	    i, j;

    printf("%-70s", "Testing endian conversion by stride");
    fflush(stdout);

    /* Initialize arrays */
    if(NULL == (src = (uint8_t *)HDmalloc(nx * 4)))
        TEST_ERROR
    if(NULL == (dst = (uint8_t *)HDcalloc(nx , (size_t)4)))
        TEST_ERROR

    init_full(src, nx, (size_t)4,(size_t)1);

    /* Initialize strides */
    src_stride[0] = 0;
    src_stride[1] = 1;
    dst_stride[0] = 8;
    dst_stride[1] = -1;
    size[0] = nx;
    size[1] = 4;

    /* Copy the array */
    H5VM_stride_copy_s(2, (hsize_t)1, size, dst_stride, dst + 3, src_stride, src);

    /* Compare */
    for(i = 0; i < nx; i++) {
        for(j = 0; j < 4; j++) {
            if(src[i * 4 + j] != dst[i * 4 + 3 - j]) {
                H5_FAILED()
                if(!HDisatty(1)) {
                    /*
                     * Print debugging info unless output is going directly
                     * to a terminal.
                     */
                    AT();
                    printf("   i=%lu, j=%lu\n", (unsigned long)i, (unsigned long)j);
                    printf("   Source array is:\n");
                    print_array(src, nx, (size_t)4, (size_t)1);
                    printf("\n	 Result is:\n");
                    print_array(dst, nx, (size_t)4, (size_t)1);
                } /* end if */
                goto error;
            } /* end if */
        } /* end for */
    } /* end for */

    PASSED()

    HDfree(src);
    HDfree(dst);

    return SUCCEED;

error:
    if(src)
        HDfree(src);
    if(dst)
        HDfree(dst);

    return FAIL;
} /* end test_endian() */


/*-------------------------------------------------------------------------
 * Function:	test_transpose
 *
 * Purpose:	Copy a 2d array from here to there and transpose the elements
 *		as it's copied.
 *
 * Return:	Success:	SUCCEED
 *
 *		Failure:	FAIL
 *
 * Programmer:	Robb Matzke
 *		Saturday, October 11, 1997
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_transpose(size_t nx, size_t ny)
{
    int	   *src = NULL;
    int	   *dst = NULL;
    hsize_t	src_stride[2], dst_stride[2];
    hsize_t	size[2];
    char	s[256];
    hsize_t	i, j;

    sprintf(s, "Testing 2d transpose by stride %4lux%-lud", (unsigned long)nx,
            (unsigned long)ny);
    printf("%-70s", s);
    fflush(stdout);

    /* Initialize */
    if(NULL == (src = (int *)HDmalloc(nx * ny * sizeof(*src))))
        TEST_ERROR
    if(NULL == (dst = (int *)HDcalloc(nx * ny, sizeof(*dst))))
        TEST_ERROR

    for(i = 0; i < nx; i++)
        for(j = 0; j < ny; j++)
            src[i * ny + j] = (int)(i * ny + j);

    /* Build stride info */
    size[0] = nx;
    size[1] = ny;
    src_stride[0] = 0;
    src_stride[1] = sizeof(*src);
    dst_stride[0] = (hsize_t)((1 - nx * ny) * sizeof(*src));
    dst_stride[1] = (hsize_t)(nx * sizeof(*src));

    /* Copy and transpose */
    if(nx == ny)
        H5VM_stride_copy(2, (hsize_t)sizeof(*src), size, dst_stride, dst,
                src_stride, src);
    else
        H5VM_stride_copy(2, (hsize_t)sizeof(*src), size, dst_stride, dst,
                src_stride, src);

    /* Check */
    for(i = 0; i < nx; i++) {
        for(j = 0; j < ny; j++) {
            if(src[i * ny + j] != dst[j * nx + i]) {
                H5_FAILED()
                if(!HDisatty(1)) {
                    AT();
                    printf("   diff at i=%lu, j=%lu\n", (unsigned long)i, (unsigned long)j);
                    printf("   Source is:\n");
                    for(i = 0; i < nx; i++) {
                        printf("%3lu:", (unsigned long)i);
                        for(j = 0; j < ny; j++)
                            printf(" %6d", src[i * ny + j]);
                        printf("\n");
                    } /* end for */
                    printf("\n	 Destination is:\n");
                    for (i = 0; i < ny; i++) {
                        printf("%3lu:", (unsigned long)i);
                        for(j = 0; j < nx; j++)
                            printf(" %6d", dst[i * nx + j]);
                        printf("\n");
                    } /* end for */
                } /* end if */
                goto error;
            } /* end if */
        } /* end for */
    } /* end for */

    PASSED()

    HDfree(src);
    HDfree(dst);

    return SUCCEED;

error:
    if(src)
        HDfree(src);
    if(dst)
        HDfree(dst);

    return FAIL;
} /* end test_transpose() */


/*-------------------------------------------------------------------------
 * Function:	test_sub_super
 *
 * Purpose:	Tests H5VM_stride_copy() to reduce the resolution of an image
 *		by copying half the pixels in the X and Y directions.  Then
 *		we use the small image and duplicate every pixel to result in
 *		a 2x2 square.
 *
 * Return:	Success:	SUCCEED
 *
 *		Failure:	FAIL
 *
 * Programmer:	Robb Matzke
 *		Monday, October 13, 1997
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_sub_super(size_t nx, size_t ny)
{
    uint8_t	   *full = NULL;	/*original image		*/
    uint8_t	   *half = NULL;	/*image at 1/2 resolution	*/
    uint8_t	   *twice = NULL;	/*2x2 pixels			*/
    hsize_t	    src_stride[4];	/*source stride info		*/
    hsize_t	    dst_stride[4];	/*destination stride info	*/
    hsize_t	    size[4];            /*number of sample points	*/
    hsize_t	    i, j;
    char	    s[256];

    sprintf(s, "Testing image sampling %4lux%-4lu to %4lux%-4lu ",
            (unsigned long)(2 * nx), (unsigned long)(2 * ny),
            (unsigned long)nx, (unsigned long)ny);
    printf("%-70s", s);
    fflush(stdout);

    /* Initialize */
    if(NULL == (full = (uint8_t *)HDmalloc(4 * nx * ny)))
        TEST_ERROR
    if(NULL == (half = (uint8_t *)HDcalloc((size_t)1, nx * ny)))
        TEST_ERROR
    if(NULL == (twice = (uint8_t *)HDcalloc((size_t)4, nx * ny)))
        TEST_ERROR

    init_full(full, 2 * nx, 2 * ny, (size_t)1);

    /* Setup */
    size[0] = nx;
    size[1] = ny;
    src_stride[0] = (hsize_t)(2 * ny);
    src_stride[1] = 2;
    dst_stride[0] = 0;
    dst_stride[1] = 1;

    /* Copy */
    H5VM_stride_copy(2, (hsize_t)sizeof(uint8_t), size, dst_stride, half,
            src_stride, full);

    /* Check */
    for(i = 0; i < nx; i++) {
        for(j = 0; j < ny; j++) {
            if(full[4 * i * ny + 2 * j] != half[i * ny + j]) {
                H5_FAILED()
                if(!HDisatty(1)) {
                    AT();
                    printf("   full[%lu][%lu] != half[%lu][%lu]\n",
                            (unsigned long)i * 2, (unsigned long)j * 2,
                            (unsigned long)i, (unsigned long)j);
                    printf("   full is:\n");
                    print_array(full, 2 * nx, 2 * ny, (size_t)1);
                    printf("\n	 half is:\n");
                    print_array(half, nx, ny, (size_t)1);
                } /* end if */
                goto error;
            } /* end if */
        } /* end for */
    } /* end for */
    PASSED()

    /*
     * Test replicating pixels to produce an image twice as large in each
     * dimension.
     */
    sprintf(s, "Testing image sampling %4lux%-4lu to %4lux%-4lu ",
            (unsigned long)nx, (unsigned long)ny, (unsigned long)(2 * nx),
            (unsigned long)(2 * ny));
    printf("%-70s", s);
    fflush(stdout);

    /* Setup stride */
    size[0] = nx;
    size[1] = ny;
    size[2] = 2;
    size[3] = 2;
    src_stride[0] = 0;
    src_stride[1] = 1;
    src_stride[2] = 0;
    src_stride[3] = 0;
    dst_stride[0] = (hsize_t)(2 * ny);
    dst_stride[1] = (hsize_t)(2 * sizeof(uint8_t) - 4 * ny);
    dst_stride[2] = (hsize_t)(2 * ny - 2 * sizeof(uint8_t));
    dst_stride[3] = sizeof(uint8_t);

    /* Copy */
    H5VM_stride_copy(4, (hsize_t)sizeof(uint8_t), size, dst_stride, twice,
            src_stride, half);

    /* Check */
    s[0] = '\0';
    for(i = 0; i < nx; i++) {
        for(j = 0; j < ny; j++) {
            if(half[i * ny + j] != twice[4 * i * ny + 2 * j])
                sprintf(s, "half[%lu][%lu] != twice[%lu][%lu]",
                        (unsigned long)i, (unsigned long)j,
                        (unsigned long)i * 2, (unsigned long)j * 2);
            else if(half[i * ny + j] != twice[4 * i * ny + 2 * j + 1])
                sprintf(s, "half[%lu][%lu] != twice[%lu][%lu]",
                        (unsigned long)i, (unsigned long)j,
                        (unsigned long)i * 2, (unsigned long)j * 2 + 1);
            else if(half[i * ny + j] != twice[(2 * i + 1) * 2 * ny + 2 * j])
                sprintf(s, "half[%lu][%lu] != twice[%lu][%lu]",
                        (unsigned long)i, (unsigned long)j,
                        (unsigned long)i * 2 + 1, (unsigned long)j * 2);
            else if(half[i * ny + j] != twice[(2 * i + 1) * 2 * ny + 2 * j + 1])
                sprintf(s, "half[%lu][%lu] != twice[%lu][%lu]",
                        (unsigned long)i, (unsigned long)j,
                        (unsigned long)i * 2 + 1, (unsigned long)j * 2 + 1);
            if(s[0]) {
                H5_FAILED()
                if(!HDisatty(1)) {
                    AT();
                    printf("   %s\n   Half is:\n", s);
                    print_array(half, nx, ny, (size_t)1);
                    printf("\n	 Twice is:\n");
                    print_array(twice, 2 * nx, 2 * ny, (size_t)1);
                } /* end if */
                goto error;
            } /* end if */
        } /* end for */
    } /* end for */

    PASSED()

    HDfree(full);
    HDfree(half);
    HDfree(twice);

    return SUCCEED;

error:
    if(full)
        HDfree(full);
    if(half)
        HDfree(half);
    if(twice)
        HDfree(twice);

    return FAIL;
} /* test_sub_super() */


/*-------------------------------------------------------------------------
 * Function:	test_array_fill
 *
 * Purpose:	Tests H5VM_array_fill routine by copying a multibyte value
 *              (an array of ints, in our case) into all the elements of an
 *              array.
 *
 * Return:	Success:	SUCCEED
 *
 *		Failure:	FAIL
 *
 * Programmer:	Quincey Koziol
 *		Monday, April 21, 2003
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_array_fill(size_t lo, size_t hi)
{
    int    *dst = NULL;           /* Destination                  */
    int     src[ARRAY_FILL_SIZE]; /* Source to duplicate    */
    size_t	u, v, w;        /* Local index variables        */
    char	s[256];

    sprintf(s, "array filling %4lu-%-4lu elements", (unsigned long)lo,(unsigned long)hi);
    TESTING(s);

    /* Initialize */
    if(NULL == (dst = (int *)HDcalloc(sizeof(int),ARRAY_FILL_SIZE * hi)))
        TEST_ERROR

    /* Setup */
    for(u = 0; u < ARRAY_FILL_SIZE; u++)
        src[u] = (char)u;

    /* Fill */
    for(w = lo; w <= hi; w++) {
        H5VM_array_fill(dst, src, sizeof(src), w);

        /* Check */
        for(u = 0; u < w; u++)
            for(v = 0; v < ARRAY_FILL_SIZE; v++)
                if(dst[(u * ARRAY_FILL_SIZE) + v] != src[v])
                    TEST_ERROR

        HDmemset(dst, 0, sizeof(int) * ARRAY_FILL_SIZE * w);
    } /* end for */

    PASSED();

    HDfree(dst);

    return SUCCEED;

error:
    if(dst)
        HDfree(dst);
    return FAIL;
} /* end test_array_fill() */


/*-------------------------------------------------------------------------
 * Function:	test_array_offset_n_calc
 *
 * Purpose:	Tests H5VM_array_offset and H5VM_array_calc routines by comparing
 *              computed array offsets against calculated ones and then going
 *              back to the coordinates from the offset and checking those.
 *
 * Return:	Success:	SUCCEED
 *
 *		Failure:	FAIL
 *
 * Programmer:	Quincey Koziol
 *		Monday, April 21, 2003
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_array_offset_n_calc(size_t n, size_t x, size_t y, size_t z)
{
    hsize_t    *a = NULL;
    hsize_t    *temp_a;         /* Array for stored calculated offsets */
    hsize_t     off;            /* Offset in array */
    size_t	u, v, w;        /* Local index variables        */
    hsize_t     dims[ARRAY_OFFSET_NDIMS];        /* X, Y & X coordinates of array to check */
    hsize_t     coords[ARRAY_OFFSET_NDIMS];      /* X, Y & X coordinates to check offset of */
    hsize_t     new_coords[ARRAY_OFFSET_NDIMS];  /* X, Y & X coordinates of offset */
    char	s[256];

    sprintf(s, "array offset %4lux%4lux%4lu elements", (unsigned long)z,(unsigned long)y,(unsigned long)x);
    TESTING(s);

    /* Initialize */
    if(NULL == (a = (hsize_t *)HDmalloc(sizeof(hsize_t) * x * y *z)))
        TEST_ERROR

    dims[0] = z;
    dims[1] = y;
    dims[2] = x;

    /* Setup */
    for(u = 0, temp_a = a, off = 0; u < z; u++)
        for(v = 0; v < y; v++)
            for(w = 0; w < x; w++)
                *temp_a++ = off++;

    /* Check offsets */
    for(u = 0; u < n; u++) {
        /* Get random coordinate */
        coords[0] = (hsize_t)((size_t)HDrandom() % z);
        coords[1] = (hsize_t)((size_t)HDrandom() % y);
        coords[2] = (hsize_t)((size_t)HDrandom() % x);

        /* Get offset of coordinate */
        off = H5VM_array_offset(ARRAY_OFFSET_NDIMS, dims, coords);

        /* Check offset of coordinate */
        if(a[off] != off)
            TEST_ERROR

        /* Get coordinates of offset */
        if(H5VM_array_calc(off, ARRAY_OFFSET_NDIMS, dims, new_coords) < 0)
            TEST_ERROR

        /* Check computed coordinates */
        for(v = 0; v < ARRAY_OFFSET_NDIMS; v++)
            if(coords[v] != new_coords[v]) {
                HDfprintf(stderr,"coords[%u]=%Hu, new_coords[%u]=%Hu\n", (unsigned)v, coords[v], (unsigned)v, new_coords[v]);
                TEST_ERROR;
            } /* end if */
    } /* end for */

    PASSED();

    HDfree(a);

    return SUCCEED;

error:
    if(a)
        HDfree(a);

    return FAIL;
} /* end test_array_offset_n_calc() */


/*-------------------------------------------------------------------------
 * Function:	main
 *
 * Purpose:	Test various hyperslab operations.  Give the words
 *		`small' and/or `medium' on the command line or only `small'
 *		is assumed.
 *
 * Return:	Success:	exit(EXIT_SUCCESS)
 *
 *		Failure:	exit(EXIT_FAILURE)
 *
 * Programmer:	Robb Matzke
 *		Friday, October 10, 1997
 *
 *-------------------------------------------------------------------------
 */
int
main(int argc, char *argv[])
{
    herr_t		    status;
    int			    nerrors = 0;
    unsigned		size_of_test;

    /* Parse arguments or assume `small' & `medium' */
    if(1 == argc)
        size_of_test = TEST_SMALL | TEST_MEDIUM;
    else {
        int i;

        for(i = 1, size_of_test = 0; i < argc; i++) {
            if(!HDstrcmp(argv[i], "small"))
                size_of_test |= TEST_SMALL;
            else if(!HDstrcmp(argv[i], "medium"))
                size_of_test |= TEST_MEDIUM;
            else {
                printf("unrecognized argument: %s\n", argv[i]);
                HDexit(EXIT_FAILURE);
            } /* end else */
        } /* end for */
    } /* end else */

    printf("Test sizes: ");
    if(size_of_test & TEST_SMALL)
        printf(" SMALL");
    if(size_of_test & TEST_MEDIUM)
        printf(" MEDIUM");
    printf("\n");

    /* Set the random # seed */
    HDsrandom((unsigned)HDtime(NULL));

    /*
     * Open the library explicitly for thread-safe builds, so per-thread
     * things are initialized correctly.
     */
#ifdef H5_HAVE_THREADSAFE
    H5open();
#endif  /* H5_HAVE_THREADSAFE */

    /*
     *------------------------------
     * TEST HYPERSLAB FILL OPERATION
     *------------------------------
     */
    if(size_of_test & TEST_SMALL) {
        status = test_fill((size_t)11, (size_t)0, (size_t)0, (size_t)1,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_fill((size_t)11, (size_t)10, (size_t)0, (size_t)1,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_fill((size_t)3, (size_t)5, (size_t)5, (size_t)1,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    if(size_of_test & TEST_MEDIUM) {
        status = test_fill((size_t)113, (size_t)0, (size_t)0, (size_t)1,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_fill((size_t)15, (size_t)11, (size_t)0, (size_t)1,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_fill((size_t)5, (size_t)7, (size_t)7, (size_t)1,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */

    /*------------------------------
     * TEST HYPERSLAB COPY OPERATION
     *------------------------------
     */

    /* exhaustive, one-dimensional test */
    if(size_of_test & TEST_SMALL) {
        status = test_copy(VARIABLE_SRC, (size_t)11, (size_t)0, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_DST, (size_t)11, (size_t)0, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_BOTH, (size_t)11, (size_t)0, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    if(size_of_test & TEST_MEDIUM) {
        status = test_copy(VARIABLE_SRC, (size_t)179, (size_t)0, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_DST, (size_t)179, (size_t)0, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_BOTH, (size_t)179, (size_t)0, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    /* exhaustive, two-dimensional test */
    if(size_of_test & TEST_SMALL) {
        status = test_copy(VARIABLE_SRC, (size_t)11, (size_t)10, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_DST, (size_t)11, (size_t)10, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_BOTH, (size_t)11, (size_t)10, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    if(size_of_test & TEST_MEDIUM) {
        status = test_copy(VARIABLE_SRC, (size_t)13, (size_t)19, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_DST, (size_t)13, (size_t)19, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_BOTH, (size_t)13, (size_t)19, (size_t)0,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    /* sparse, two-dimensional test */
    if(size_of_test & TEST_MEDIUM) {
        status = test_copy(VARIABLE_SRC, (size_t)73, (size_t)67, (size_t)0,
                (size_t)7, (size_t)11, (size_t)1, (size_t)13, (size_t)11, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_DST, (size_t)73, (size_t)67, (size_t)0,
                (size_t)7, (size_t)11, (size_t)1, (size_t)13, (size_t)11, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_BOTH, (size_t)73, (size_t)67, (size_t)0,
                (size_t)7, (size_t)11, (size_t)1, (size_t)13, (size_t)11, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    /* exhaustive, three-dimensional test */
    if(size_of_test & TEST_SMALL) {
        status = test_copy(VARIABLE_SRC, (size_t)3, (size_t)5, (size_t)5,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_DST, (size_t)3, (size_t)5, (size_t)5,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_BOTH, (size_t)3, (size_t)5, (size_t)5,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    if(size_of_test & TEST_MEDIUM) {
        status = test_copy(VARIABLE_SRC, (size_t)7, (size_t)9, (size_t)5,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_DST, (size_t)7, (size_t)9, (size_t)5,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
        status = test_copy(VARIABLE_BOTH, (size_t)7, (size_t)9, (size_t)5,
                (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */

    /*---------------------
     * TEST MULTI-BYTE FILL
     *---------------------
     */
    if(size_of_test & TEST_SMALL) {
        status = test_multifill((size_t)10);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    if(size_of_test & TEST_MEDIUM) {
        status = test_multifill((size_t)500000);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */

    /*---------------------------
     * TEST TRANSLATION OPERATORS
     *---------------------------
     */
    if(size_of_test & TEST_SMALL) {
        status = test_endian((size_t)10);
        nerrors += status < 0 ? 1 : 0;
        status = test_transpose((size_t)9, (size_t)9);
        nerrors += status < 0 ? 1 : 0;
        status = test_transpose((size_t)3, (size_t)11);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    if(size_of_test & TEST_MEDIUM) {
        status = test_endian((size_t)800000);
        nerrors += status < 0 ? 1 : 0;
        status = test_transpose((size_t)1200, (size_t)1200);
        nerrors += status < 0 ? 1 : 0;
        status = test_transpose((size_t)800, (size_t)1800);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */

    /*-------------------------
     * TEST SAMPLING OPERATIONS
     *-------------------------
     */
    if(size_of_test & TEST_SMALL) {
        status = test_sub_super((size_t)5, (size_t)10);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    if(size_of_test & TEST_MEDIUM) {
        status = test_sub_super((size_t)480, (size_t)640);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */

    /*-------------------------
     * TEST ARRAY FILL OPERATIONS
     *-------------------------
     */
    if(size_of_test & TEST_SMALL) {
        status = test_array_fill((size_t)1, (size_t)9);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    if(size_of_test & TEST_MEDIUM) {
        status = test_array_fill((size_t)9, (size_t)257);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */

    /*-------------------------
     * TEST ARRAY OFFSET OPERATIONS
     *-------------------------
     */
    if(size_of_test & TEST_SMALL) {
        status = test_array_offset_n_calc((size_t)20, (size_t)7, (size_t)11,
                (size_t)13);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */
    if(size_of_test & TEST_MEDIUM) {
        status = test_array_offset_n_calc((size_t)500, (size_t)71,
                (size_t)193, (size_t)347);
        nerrors += status < 0 ? 1 : 0;
    } /* end if */

    /*--- END OF TESTS ---*/

    if(nerrors) {
        printf("***** %d HYPERSLAB TEST%s FAILED! *****\n", nerrors, 1
                == nerrors ? "" : "S");
        if(HDisatty(1))
            printf("(Redirect output to a pager or a file to see debug output)\n");
        HDexit(EXIT_FAILURE);
    } /* end if */

    printf("All hyperslab tests passed.\n");

#ifdef H5_HAVE_THREADSAFE
    H5close();
#endif  /* H5_HAVE_THREADSAFE */

    return 0;
}

