/*
 * 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
#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)
{
    int                     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)
{
    int                     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_xcalloc(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             */
    size_t                  hs_size[3];         /*hyperslab size                */
    size_t                  dst_size[3];        /*destination total size        */
    size_t                  dst_offset[3];      /*offset of hyperslab in dest   */
    uintn                   ref_value;  /*reference value               */
    uintn                   acc;        /*accumulator                   */
    int                     i, j, k, dx, dy, dz, u, v, w;       /*counters              */
    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_xcalloc(nx * ny * nz, 1);
    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 = 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             */
    size_t                  hs_size[3];         /*hyperslab size                */
    size_t                  dst_size[3];        /*destination total size        */
    size_t                  src_size[3];        /*source total size             */
    size_t                  dst_offset[3];      /*offset of hyperslab in dest   */
    size_t                  src_offset[3];      /*offset of hyperslab in source */
    uintn                   ref_value;  /*reference value               */
    uintn                   acc;        /*accumulator                   */
    int                     i, j, k, dx, dy, dz, u, v, w;       /*counters              */
    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_xcalloc(nx * ny * nz, 1);
    dst = H5MM_xcalloc(nx * ny * nz, 1);
    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:
                                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=%d, j=%d, k=%d, "
                                           "dx=%d, dy=%d, dz=%d\n",
                                           i, j, k, dx, dy, 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=%d, j=%d, k=%d, "
                                           "dx=%d, dy=%d, dz=%d\n",
                                           i, j, k, dx, dy, 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(int nx)
{
    int                     i, j;
    size_t                  size;
    intn                    src_stride;
    intn                    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_xmalloc(nx * sizeof(*src));
    dst = H5MM_xmalloc(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, 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[%d].left", i);
        } else if (dst[i].mid != fill.mid) {
            sprintf(s, "bad dst[%d].mid", i);
        } else if (dst[i].right != 4444444) {
            sprintf(s, "bad dst[%d].right", 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[%d]={%d,%g,%d}\n   ",
                       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             */
    intn                    src_stride[2];      /*source strides                */
    intn                    dst_stride[2];      /*destination strides           */
    size_t                  size[2];    /*size vector                   */
    int                     i, j;

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

    /* Initialize arrays */
    src = H5MM_xmalloc(nx * 4);
    init_full(src, nx, 4, 1);
    dst = H5MM_xcalloc(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, 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=%d, j=%d\n", i, 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;
    int                     i, j;
    intn                    src_stride[2], dst_stride[2];
    size_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_xmalloc(nx * ny * sizeof(*src));
    for (i = 0; i < nx; i++) {
        for (j = 0; j < ny; j++) {
            src[i * ny + j] = i * ny + j;
        }
    }
    dst = H5MM_xcalloc(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] = (1 - nx * ny) * sizeof(*src);
    dst_stride[1] = nx * sizeof(*src);

    /* Copy and transpose */
    if (nx == ny) {
        H5V_stride_copy(2, sizeof(*src), size,
                        dst_stride, dst,
                        src_stride, src);
    } else {
        H5V_stride_copy(2, 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=%d, j=%d\n", i, j);
                    printf("   Source is:\n");
                    for (i = 0; i < nx; i++) {
                        printf("%3d:", 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("%3d:", 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                    */
    intn                    src_stride[4];      /*source stride info            */
    intn                    dst_stride[4];      /*destination stride info       */
    size_t                  size[4];    /*number of sample points       */
    int                     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_xmalloc(4 * nx * ny);
    init_full(full, 2 * nx, 2 * ny, 1);
    half = H5MM_xcalloc(nx * ny, 1);
    twice = H5MM_xcalloc(4 * nx * ny, 1);

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

    /* Copy */
    H5V_stride_copy(2, 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[%d][%d] != half[%d][%d]\n", i * 2, j * 2, i, 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] = 2 * ny;
    dst_stride[1] = 2 * sizeof(uint8) - 4 * ny;
    dst_stride[2] = 2 * ny - 2 * sizeof(uint8);
    dst_stride[3] = sizeof(uint8);

    /* Copy */
    H5V_stride_copy(4, 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[%d][%d] != twice[%d][%d]", i, j, 2 * i, 2 * j);
            } else if (half[i * ny + j] != twice[4 * i * ny + 2 * j + 1]) {
                sprintf(s, "half[%d][%d] != twice[%d][%d]", i, j, 2 * i, 2 * j + 1);
            } else if (half[i * ny + j] != twice[(2 * i + 1) * 2 * ny + 2 * j]) {
                sprintf(s, "half[%d][%d] != twice[%d][%d]", i, j, 2 * i + 1, 2 * j);
            } else if (half[i * ny + j] != twice[(2 * i + 1) * 2 * ny + 2 * j + 1]) {
                sprintf(s, "half[%d][%d] != twice[%d][%d]", i, j, 2 * i + 1, 2 * j + 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");
    exit(0);
}