/*
 * Copyright (C) 1997 NCSA
 *		      All rights reserved.
 *
 * 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 <H5private.h>
#include <H5MMprivate.h>
#include <H5Vprivate.h>

#ifndef HAVE_FUNCTION
#undef __FUNCTION__
#define __FUNCTION__ ""
#endif
#define AT() printf ("	 at %s:%d in %s()\n",__FILE__,__LINE__,__FUNCTION__);

#define TEST_SMALL	0x0001
#define TEST_MEDIUM	0x0002

#define VARIABLE_SRC	0
#define VARIABLE_DST	1
#define VARIABLE_BOTH	2

/*-------------------------------------------------------------------------
 * Function:	init_full
 *
 * Purpose:	Initialize full array.
 *
 * Return:	void
 *
 * Programmer:	Robb Matzke
 *		Friday, October 10, 1997
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static uintn
init_full(uint8 *array, size_t nx, size_t ny, size_t nz)
{
    size_t		    i, j, k;
    uint8		    acc = 128;
    uintn		    total = 0;

    for (i=0; i<nx; i++) {
	for (j=0; j<ny; j++) {
	    for (k=0; k<nz; k++) {
		total += acc;
		*array++ = acc++;
	    }
	}
    }
    return total;
}

/*-------------------------------------------------------------------------
 * Function:	print_array
 *
 * Purpose:	Prints the values in an array
 *
 * Return:	void
 *
 * Programmer:	Robb Matzke
 *		Friday, October 10, 1997
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void
print_array(uint8 *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=%d:\n", i);
	} else {
	    printf("%03d:", i);
	}

	for (j=0; j<ny; j++) {
	    if (nz>1)
		printf("%03d:", j);
	    for (k=0; k<nz; k++) {
		printf(" %3d", *array++);
	    }
	    if (nz>1)
		printf("\n");
	}
	printf("\n");
    }
}

/*-------------------------------------------------------------------------
 * Function:	print_ref
 *
 * Purpose:	Prints the reference value
 *
 * Return:	Success:	0
 *
 *		Failure:	
 *
 * Programmer:	Robb Matzke
 *		Friday, October 10, 1997
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void
print_ref(size_t nx, size_t ny, size_t nz)
{
    uint8		   *array;

    array = H5MM_calloc(nx*ny*nz*sizeof(uint8));

    printf("Reference array:\n");
    init_full(array, nx, ny, nz);
    print_array(array, nx, ny, nz);
}

/*-------------------------------------------------------------------------
 * Function:	test_fill
 *
 * Purpose:	Tests the H5V_hyper_fill() function.
 *
 * Return:	Success:	SUCCEED
 *
 *		Failure:	FAIL
 *
 * Programmer:	Robb Matzke
 *		Saturday, October 11, 1997
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
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	*dst = NULL;		/*destination array		*/
    hsize_t	hs_size[3];	    	/*hyperslab size		*/
    hsize_t	dst_size[3];	    	/*destination total size	*/
    hssize_t	dst_offset[3];	    	/*offset of hyperslab in dest   */
    uintn	ref_value;  		/*reference value		*/
    uintn	acc;	    		/*accumulator		    	*/
    size_t	i, j, k, dx, dy, dz;	/*counters		   	*/
    size_t	u, v, w;
    int		ndims;			/*hyperslab dimensionality	*/
    char	dim[64], s[256];    	/*temp string		    	*/
    uintn	fill_value;	    	/*fill value		    	*/

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

    /* Allocate array */
    dst = H5MM_calloc(nx*ny*nz);
    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] = (hssize_t)i;
			    dst_offset[1] = (hssize_t)j;
			    dst_offset[2] = (hssize_t)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=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++) {
					    ref_value -= dst[u*ny*nz+v*nz+w];
					}
				    }
				}
				ref_value += fill_value * dx * dy * dz;

				/* Fill the hyperslab with some value */
				H5V_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) {
				    puts("*FAILED*");
				    if (!isatty(1)) {
					/*
					 * Print debugging info unless output
					 * is going directly to a terminal.
					 */
					AT();
					printf("   acc != ref_value\n");
					printf("   i=%d, j=%d, k=%d, "
					   "dx=%d, dy=%d, dz=%d, fill=%d\n",
					   i, j, k, dx, dy, dz, fill_value);
					print_ref(nx, ny, nz);
					printf("\n   Result is:\n");
					print_array(dst, nx, ny, nz);
				    }
				    goto error;
				}
			    }
			}
		    }
		}
	    }
	}
    }
    puts(" PASSED");
    H5MM_xfree(dst);
    return SUCCEED;

  error:
    H5MM_xfree(dst);
    return FAIL;
}

/*-------------------------------------------------------------------------
 * Function:	test_copy
 *
 * Purpose:	Tests H5V_hyper_copy().
 *
 *		The NX, NY, and NZ arguments are the size for the source and
 *		destination arrays.  You map 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
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
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	*src = NULL;		/*source array			*/
    uint8	*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		*/
    hssize_t	dst_offset[3];		/*offset of hyperslab in dest	*/
    hssize_t	src_offset[3];		/*offset of hyperslab in source */
    uintn	ref_value;		/*reference value		*/
    uintn	acc;			/*accumulator			*/
    hsize_t	i, j, k, dx, dy, dz;	/*counters		     	*/
    hsize_t	u, v, w;
    int		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);
	} else {
	    ndims = 2;
	    nz = 1;
	    sprintf(dim, "%lux%lu", (unsigned long) nx, (unsigned long) ny);
	}
    } else {
	ndims = 3;
	sprintf(dim, "%lux%lux%lu",
		(unsigned long) nx, (unsigned long) ny, (unsigned long) nz);
    }

    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:
	abort();
    }

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

    /*
     * Allocate arrays
     */
    src = H5MM_calloc(nx*ny*nz);
    dst = H5MM_calloc(nx*ny*nz);
    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] = (hssize_t)i;
				src_offset[1] = (hssize_t)j;
				src_offset[2] = (hssize_t)k;
				break;
			    case VARIABLE_DST:
				dst_offset[0] = (hssize_t)i;
				dst_offset[1] = (hssize_t)j;
				dst_offset[2] = (hssize_t)k;
				src_offset[0] = 0;
				src_offset[1] = 0;
				src_offset[2] = 0;
				break;
			    case VARIABLE_BOTH:
				dst_offset[0] = (hssize_t)i;
				dst_offset[1] = (hssize_t)j;
				dst_offset[2] = (hssize_t)k;
				src_offset[0] = (hssize_t)i;
				src_offset[1] = (hssize_t)j;
				src_offset[2] = (hssize_t)k;
				break;
			    default:
				abort();
			    }

			    /*
			     * 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.
			     */
			    H5V_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) {
				puts("*FAILED*");
				if (!isatty(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);
				}
				goto error;
			    }
			    /*
			     * 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];
				    }
				}
			    }
			    if (acc != ref_value + nx*ny*nz - dx*dy*dz) {
				puts("*FAILED*");
				if (!isatty(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);
				}
				goto error;
			    }
			}
		    }
		}
	    }
	}
    }
    puts(" PASSED");
    H5MM_xfree(src);
    H5MM_xfree(dst);
    return SUCCEED;

  error:
    H5MM_xfree(src);
    H5MM_xfree(dst);
    return FAIL;
}

/*-------------------------------------------------------------------------
 * Function:	test_multifill
 *
 * Purpose:	Tests the H5V_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
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_multifill(size_t nx)
{
    hsize_t		    i, j;
    hsize_t		    size;
    hssize_t		    src_stride;
    hssize_t		    dst_stride;
    char		    s[64];

    struct a_struct {
	int			left;
	double			mid;
	int			right;
    } fill		   , *src = NULL, *dst = NULL;

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

    /* Initialize the source and destination */
    src = H5MM_malloc(nx * sizeof(*src));
    dst = H5MM_malloc(nx * sizeof(*dst));
    for (i = 0; i < nx; i++) {
	src[i].left = 1111111;
	src[i].mid = 12345.6789;
	src[i].right = 2222222;
	dst[i].left = 3333333;
	dst[i].mid = 98765.4321;
	dst[i].right = 4444444;
    }

    /*
     * Describe the fill value.	 The zero stride says to read the same thing
     * over and over again.
     */
    fill.left = 55555555;
    fill.mid = 3.1415927;
    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;
    H5V_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 (dst[i].mid != fill.mid) {
	    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]) {
	    puts("*FAILED*");
	    if (!isatty(1)) {
		AT();
		printf("   fill={%d,%g,%d}\n   ",
		       fill.left, fill.mid, fill.right);
		for (j = 0; j < sizeof(fill); j++) {
		    printf(" %02x", ((uint8 *) &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 *) (dst + i))[j]);
		}
		printf("\n");
	    }
	    goto error;
	}
    }

    puts(" PASSED");
    H5MM_xfree(src);
    H5MM_xfree(dst);
    return SUCCEED;

  error:
    H5MM_xfree(src);
    H5MM_xfree(dst);
    return FAIL;
}

/*-------------------------------------------------------------------------
 * Function:	test_endian
 *
 * Purpose:	Tests the H5V_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
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_endian(size_t nx)
{
    uint8	*src = NULL;		/*source array			*/
    uint8	*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 */
    src = H5MM_malloc(nx * 4);
    init_full(src, nx, 4, 1);
    dst = H5MM_calloc(nx * 4);

    /* 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 */
    H5V_stride_copy(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]) {
		puts("*FAILED*");
		if (!isatty(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, 4, 1);
		    printf("\n	 Result is:\n");
		    print_array(dst, nx, 4, 1);
		}
		goto error;
	    }
	}
    }

    puts(" PASSED");
    H5MM_xfree(src);
    H5MM_xfree(dst);
    return SUCCEED;

  error:
    H5MM_xfree(src);
    H5MM_xfree(dst);
    return FAIL;
}

/*-------------------------------------------------------------------------
 * 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
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_transpose(size_t nx, size_t ny)
{
    intn	*src = NULL;
    intn	*dst = NULL;
    hsize_t	i, j;
    hssize_t	src_stride[2], dst_stride[2];
    hsize_t	size[2];
    char	s[256];

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

    /* Initialize */
    src = H5MM_malloc(nx * ny * sizeof(*src));
    for (i = 0; i < nx; i++) {
	for (j = 0; j < ny; j++) {
	    src[i * ny + j] = (intn)(i * ny + j);
	}
    }
    dst = H5MM_calloc(nx*ny*sizeof(*dst));

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

    /* Copy and transpose */
    if (nx == ny) {
	H5V_stride_copy(2, (hsize_t)sizeof(*src), size,
			dst_stride, dst,
			src_stride, src);
    } else {
	H5V_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]) {
		puts("*FAILED*");
		if (!isatty(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");
		    }
		    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");
		    }
		}
		goto error;
	    }
	}
    }

    puts(" PASSED");
    H5MM_xfree(src);
    H5MM_xfree(dst);
    return SUCCEED;

  error:
    H5MM_xfree(src);
    H5MM_xfree(dst);
    return FAIL;
}

/*-------------------------------------------------------------------------
 * Function:	test_sub_super
 *
 * Purpose:	Tests H5V_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
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
test_sub_super(size_t nx, size_t ny)
{
    uint8	*full = NULL;	/*original image		*/
    uint8	*half = NULL;	/*image at 1/2 resolution	*/
    uint8	*twice = NULL;	/*2x2 pixels			*/
    hssize_t	src_stride[4];	/*source stride info		*/
    hssize_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 */
    full = H5MM_malloc(4 * nx * ny);
    init_full(full, 2 * nx, 2 * ny, 1);
    half = H5MM_calloc(nx*ny);
    twice = H5MM_calloc(4*nx*ny);

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

    /* Copy */
    H5V_stride_copy(2, (hsize_t)sizeof(uint8), 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]) {
		puts("*FAILED*");
		if (!isatty(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, 1);
		    printf("\n	 half is:\n");
		    print_array(half, nx, ny, 1);
		}
		goto error;
	    }
	}
    }
    puts(" 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] = (ssize_t)(2 * ny);
    dst_stride[1] = (ssize_t)(2 * sizeof(uint8) - 4 * ny);
    dst_stride[2] = (ssize_t)(2 * ny - 2 * sizeof(uint8));
    dst_stride[3] = sizeof(uint8);

    /* Copy */
    H5V_stride_copy(4, (hsize_t)sizeof(uint8), 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]) {
		puts("*FAILED*");
		if (!isatty(1)) {
		    AT();
		    printf("   %s\n   Half is:\n", s);
		    print_array(half, nx, ny, 1);
		    printf("\n	 Twice is:\n");
		    print_array(twice, 2 * nx, 2 * ny, 1);
		}
		goto error;
	    }
	}
    }
    puts(" PASSED");

    H5MM_xfree(full);
    H5MM_xfree(half);
    H5MM_xfree(twice);
    return SUCCEED;

  error:
    H5MM_xfree(full);
    H5MM_xfree(half);
    H5MM_xfree(twice);
    return FAIL;
}

/*-------------------------------------------------------------------------
 * 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(0)
 *
 *		Failure:	exit(non-zero)
 *
 * Programmer:	Robb Matzke
 *		Friday, October 10, 1997
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
int
main(int argc, char *argv[])
{
    herr_t		    status;
    int			    nerrors = 0;
    uintn		    size_of_test;

    /* Parse arguments or assume `small' */
    if (1 == argc) {
	size_of_test = TEST_SMALL;
    } else {
	intn			i;
	for (i = 1, size_of_test = 0; i < argc; i++) {
	    if (!strcmp(argv[i], "small")) {
		size_of_test |= TEST_SMALL;
	    } else if (!strcmp(argv[i], "medium")) {
		size_of_test |= TEST_MEDIUM;
	    } else {
		printf("unrecognized argument: %s\n", argv[i]);
		exit(1);
	    }
	}
    }
    printf("Test sizes: ");
    if (size_of_test & TEST_SMALL)
	printf(" SMALL");
    if (size_of_test & TEST_MEDIUM)
	printf(" MEDIUM");
    printf("\n");

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

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

    if (size_of_test & TEST_SMALL) {
	status = test_multifill(10);
	nerrors += status < 0 ? 1 : 0;
    }
    if (size_of_test & TEST_MEDIUM) {
	status = test_multifill(500000);
	nerrors += status < 0 ? 1 : 0;
    }
   /*---------------------------
    * TEST TRANSLATION OPERATORS
    *---------------------------
    */

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

    if (size_of_test & TEST_SMALL) {
	status = test_sub_super(5, 10);
	nerrors += status < 0 ? 1 : 0;
    }
    if (size_of_test & TEST_MEDIUM) {
	status = test_sub_super(480, 640);
	nerrors += status < 0 ? 1 : 0;
    }

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

    if (nerrors) {
	printf("***** %d HYPERSLAB TEST%s FAILED! *****\n",
	       nerrors, 1 == nerrors ? "" : "S");
	if (isatty(1)) {
	    printf("(Redirect output to a pager or a file to see "
		   "debug output)\n");
	}
	exit(1);
    }
    printf("All hyperslab tests passed.\n");
    return 0;
}
