/*
 * Copyright (C) 1998 NCSA
 *                    All rights reserved.
 *
 * Programmer:  Robb Matzke <matzke@llnl.gov>
 *              Friday, January 23, 1998
 */
#undef NDEBUG
#include <assert.h>
#include <hdf5.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <H5config.h>
#ifndef HAVE_ATTRIBUTE
#   undef __attribute__
#   define __attribute__(X) /*void*/
#   define __unused__ /*void*/
#else
#   define __unused__ __attribute__((unused))
#endif

#define TEST_FILE_NAME	"cmpd_dset.h5"

/* The first dataset */
typedef struct s1_t {
    unsigned int a;
    unsigned int b;
    unsigned int c[4];
    unsigned int d;
    unsigned int e;
} s1_t;

/* The second dataset (same as first) */
typedef s1_t s2_t;

/* The third dataset (reversed fields of s1) */
typedef struct s3_t {
    unsigned int e;
    unsigned int d;
    unsigned int c[4];
    unsigned int b;
    unsigned int a;
} s3_t;

/* The fourth dataset (a subset of s1) */
typedef struct s4_t {
    unsigned int b;
    unsigned int d;
} s4_t;

/* The fifth dataset (a superset of s1) */
typedef struct s5_t {
    unsigned int pre;
    unsigned int a;
    unsigned int b;
    unsigned int mid1;
    unsigned int c[4];
    unsigned int mid2;
    unsigned int d;
    unsigned int e;
    unsigned int post;
} s5_t;


#if 1
#  define NX	100u
#  define NY	2000u
#else
#  define NX	12u
#  define NY    9u
#endif


/*-------------------------------------------------------------------------
 * Function:	cleanup
 *
 * Purpose:	Cleanup temporary test files
 *
 * Return:	none
 *
 * Programmer:	Albert Cheng
 *              May 28, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void
cleanup(void)
{
    if (!getenv ("HDF5_NOCLEANUP")) {
	remove(TEST_FILE_NAME);
    }
}


/*-------------------------------------------------------------------------
 * Function:	display_error_cb
 *
 * Purpose:	Displays the error stack after printing "*FAILED*".
 *
 * Return:	Success:	0
 *
 *		Failure:	-1
 *
 * Programmer:	Robb Matzke
 *		Wednesday, March  4, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
display_error_cb (void __unused__ *client_data)
{
    puts ("*FAILED*");
    H5Eprint (stdout);
    return 0;
}


/*-------------------------------------------------------------------------
 * Function:	main
 *
 * Purpose:	Creates a simple dataset of a compound type and then reads
 *		it back.  The dataset is read back in various ways to
 *		exercise the I/O pipeline and compound type conversion.
 *
 * Return:	Success:	0
 *
 *		Failure:	1
 *
 * Programmer:	Robb Matzke
 *              Friday, January 23, 1998
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
int
main (void)
{
    /* First dataset */
    static s1_t		s1[NX*NY];
    hid_t		s1_tid;

    /* Second dataset */
    static s2_t		s2[NX*NY];
    hid_t		s2_tid;

    /* Third dataset */
    static s3_t		s3[NX*NY];
    hid_t		s3_tid;
    
    /* Fourth dataset */
    static s4_t		s4[NX*NY];
    hid_t		s4_tid;
    
    /* Fifth dataset */
    static s5_t		s5[NX*NY];
    hid_t		s5_tid;

    /* Sixth dataset */

    /* Seventh dataset */
    hid_t		s7_sid;

    /* Eighth dataset */
    s1_t		*s8 = NULL;
    hid_t		s8_f_sid;	/*file data space		*/
    hid_t		s8_m_sid;	/*memory data space		*/

    /* Ninth dataset */

    /* Tenth dataset */

    /* Eleventh dataset */
#if 0
    s4_t		*s11 = NULL;
    int			ndims;
#endif

    /* Other variables */
    unsigned int	i, j;
    hid_t		file, dataset, space, PRESERVE;
    static hsize_t	dim[] = {NX, NY};
    hssize_t 		f_offset[2];	/*offset of hyperslab in file	*/
    hsize_t 		h_size[2];	/*size of hyperslab		*/
    size_t		memb_size[1] = {4};

    /* Set up error handling */
    H5Eset_auto(display_error_cb, NULL);

    /* Create the file */
    if ((file = H5Fcreate (TEST_FILE_NAME, H5F_ACC_TRUNC|H5F_ACC_DEBUG,
			   H5P_DEFAULT, H5P_DEFAULT))<0) goto error;

    /* Create the data space */
    if ((space = H5Screate_simple (2, dim, NULL))<0) goto error;

    /* Create xfer properties to preserve initialized data */
    if ((PRESERVE = H5Pcreate (H5P_DATASET_XFER))<0) goto error;
    if (H5Pset_preserve (PRESERVE, 1)<0) goto error;

    /*
     *######################################################################
     * STEP 1: Save the original dataset natively.
     */
    printf("%-70s", "Testing basic compound write");
    fflush(stdout);
    
    /* Initialize the dataset */
    for (i=0; i<NX*NY; i++) {
	s1[i].a = 8*i+0;
	s1[i].b = 2000+2*i;
	s1[i].c[0] = 8*i+2;
	s1[i].c[1] = 8*i+3;
	s1[i].c[2] = 8*i+4;
	s1[i].c[3] = 8*i+5;
	s1[i].d = 2001+2*i;
	s1[i].e = 8*i+7;
    }

    /* Create the memory data type */
    if ((s1_tid = H5Tcreate (H5T_COMPOUND, sizeof(s1_t)))<0) goto error;
    if (H5Tinsert (s1_tid, "a", HOFFSET(s1_t,a), H5T_NATIVE_INT)<0 ||
	H5Tinsert (s1_tid, "b", HOFFSET(s1_t,b), H5T_NATIVE_INT)<0 ||
	H5Tinsert_array (s1_tid, "c", HOFFSET(s1_t,c), 1, memb_size, NULL,
			 H5T_NATIVE_INT)<0 ||
	H5Tinsert (s1_tid, "d", HOFFSET(s1_t,d), H5T_NATIVE_INT)<0 ||
	H5Tinsert (s1_tid, "e", HOFFSET(s1_t,e), H5T_NATIVE_INT)<0) {
	goto error;
    }

    /* Create the dataset */
    if ((dataset = H5Dcreate (file, "s1", s1_tid, space, H5P_DEFAULT))<0) {
	goto error;
    }

    /* Write the data */
    if (H5Dwrite (dataset, s1_tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, s1)<0) {
	goto error;
    }
    puts(" PASSED");

    /*
     *######################################################################
     * STEP 2: We create a new type ID for the second dataset even though
     * 	       it's the same as the first just to test things better, but
     *	       in fact, we could have used s1_tid.
     */
    printf("%-70s", "Testing basic compound read");
    fflush(stdout);
    
    /* Create a data type for s2 */
    if ((s2_tid = H5Tcreate (H5T_COMPOUND, sizeof(s2_t)))<0) goto error;
    if (H5Tinsert (s2_tid, "a", HOFFSET(s2_t,a), H5T_NATIVE_INT)<0 ||
	H5Tinsert (s2_tid, "b", HOFFSET(s2_t,b), H5T_NATIVE_INT)<0 ||
	H5Tinsert_array (s2_tid, "c", HOFFSET(s2_t,c), 1, memb_size, NULL,
			 H5T_NATIVE_INT)<0 ||
	H5Tinsert (s2_tid, "d", HOFFSET(s2_t,d), H5T_NATIVE_INT)<0 ||
	H5Tinsert (s2_tid, "e", HOFFSET(s2_t,e), H5T_NATIVE_INT)<0) {
	goto error;
    }
    
    /* Read the data */
    if (H5Dread (dataset, s2_tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, s2)<0) {
	goto error;
    }

    /* Compare s2 with s1.  They should be the same */
    for (i=0; i<NX*NY; i++) {
	if (s1[i].a!=s2[i].a ||
	    s1[i].b!=s2[i].b ||
	    s1[i].c[0]!=s2[i].c[0] ||
	    s1[i].c[1]!=s2[i].c[1] ||
	    s1[i].c[2]!=s2[i].c[2] ||
	    s1[i].c[3]!=s2[i].c[3] ||
	    s1[i].d!=s2[i].d ||
	    s1[i].e!=s2[i].e) {
	    puts("*FAILED*");
	    puts("   Incorrect values read from the file");
	    goto error;
	}
    }
    puts(" PASSED");
    
    /*
     *######################################################################
     * STEP 3: Read the dataset back into a third memory buffer. This buffer
     * 	       has the same data space but the data type is different: the
     *	       data type is a struct whose members are in the opposite order.
     */
    printf("%-70s", "Testing reversal of struct members");
    fflush (stdout);
    
    /* Create a data type for s3 */
    if ((s3_tid = H5Tcreate (H5T_COMPOUND, sizeof(s3_t)))<0) goto error;
    if (H5Tinsert (s3_tid, "a", HOFFSET(s3_t,a), H5T_NATIVE_INT)<0 ||
	H5Tinsert (s3_tid, "b", HOFFSET(s3_t,b), H5T_NATIVE_INT)<0 ||
	H5Tinsert_array (s3_tid, "c", HOFFSET(s3_t,c), 1, memb_size, NULL,
			 H5T_NATIVE_INT)<0 ||
	H5Tinsert (s3_tid, "d", HOFFSET(s3_t,d), H5T_NATIVE_INT)<0 ||
	H5Tinsert (s3_tid, "e", HOFFSET(s3_t,e), H5T_NATIVE_INT)<0) {
	goto error;
    }
    
    /* Read the data */
    if (H5Dread (dataset, s3_tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, s3)<0) {
	goto error;
    }

    /* Compare s3 with s1.  They should be the same */
    for (i=0; i<NX*NY; i++) {
	if (s1[i].a!=s3[i].a ||
	    s1[i].b!=s3[i].b ||
	    s1[i].c[0]!=s3[i].c[0] ||
	    s1[i].c[1]!=s3[i].c[1] ||
	    s1[i].c[2]!=s3[i].c[2] ||
	    s1[i].c[3]!=s3[i].c[3] ||
	    s1[i].d!=s3[i].d ||
	    s1[i].e!=s3[i].e) {
	    puts("*FAILED*");
	    puts("   Incorrect values read from the file");
	    goto error;
	}
    }
    puts(" PASSED");

    /*
     *######################################################################
     * STEP 4: Read a subset of the members.  Of the <a,b,c,d,e> members
     *         stored on disk we'll read <b,d>.
     */
    printf("%-70s", "Testing subset struct read");
    fflush (stdout);

    /* Create a datatype for s4 */
    if ((s4_tid = H5Tcreate (H5T_COMPOUND, sizeof(s4_t)))<0) goto error;
    if (H5Tinsert (s4_tid, "b", HOFFSET(s4_t,b), H5T_NATIVE_INT)<0) goto error;
    if (H5Tinsert (s4_tid, "d", HOFFSET(s4_t,d), H5T_NATIVE_INT)<0) goto error;

    /* Read the data */
    if (H5Dread (dataset, s4_tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, s4)<0) {
	goto error;
    }

    /* Compare s4 with s1 */
    for (i=0; i<NX*NY; i++) {
	if (s1[i].b!=s4[i].b ||
	    s1[i].d!=s4[i].d) {
	    puts("*FAILED*");
	    puts("   Incorrect values read from the file");
	    goto error;
	}
    }
    puts(" PASSED");

    /*
     *######################################################################
     * STEP 5: Read all the members into a struct which has other members
     * 	       which have already been initialized.
     */
    printf("%-70s", "Testing partially initialized superset read");
    fflush(stdout);

    /* Initialize some members */
    for (i=0; i<NX*NY; i++) {
	s5[i].pre =  1000+4*i;
	s5[i].mid1 = 1001+4*i;
	s5[i].mid2 = 1002+4*i;
	s5[i].post = 1003+4*i;
    }
    
    /* Create a data type for s5 */
    if ((s5_tid = H5Tcreate (H5T_COMPOUND, sizeof(s5_t)))<0) goto error;
    if (H5Tinsert (s5_tid, "a", HOFFSET(s5_t,a), H5T_NATIVE_INT)<0 ||
	H5Tinsert (s5_tid, "b", HOFFSET(s5_t,b), H5T_NATIVE_INT)<0 ||
	H5Tinsert_array (s5_tid, "c", HOFFSET(s5_t,c), 1, memb_size, NULL,
			 H5T_NATIVE_INT)<0 ||
	H5Tinsert (s5_tid, "d", HOFFSET(s5_t,d), H5T_NATIVE_INT)<0 ||
	H5Tinsert (s5_tid, "e", HOFFSET(s5_t,e), H5T_NATIVE_INT)) {
	goto error;
    }
	
    /* Read the data */
    if (H5Dread (dataset, s5_tid, H5S_ALL, H5S_ALL, PRESERVE, s5)<0) {
	goto error;
    }

    /* Check that the data was read properly */
    for (i=0; i<NX*NY; i++) {
	if (s1[i].a!=s5[i].a ||
	    s1[i].b!=s5[i].b ||
	    s1[i].c[0]!=s5[i].c[0] ||
	    s1[i].c[1]!=s5[i].c[1] ||
	    s1[i].c[2]!=s5[i].c[2] ||
	    s1[i].c[3]!=s5[i].c[3] ||
	    s1[i].d!=s5[i].d ||
	    s1[i].e!=s5[i].e) {
	    puts("*FAILED*");
	    puts("   Incorrect values read from the file");
	    goto error;
	}
    }

    /* Check that no previous values were clobbered */
    for (i=0; i<NX*NY; i++) {
	if (s5[i].pre  != 1000+4*i ||
	    s5[i].mid1 != 1001+4*i ||
	    s5[i].mid2 != 1002+4*i ||
	    s5[i].post != 1003+4*i) {
	    puts("*FAILED*");
	    puts("   Memory values were clobbered");
	    goto error;
	}
    }
    puts(" PASSED");

    /*
     *######################################################################
     * STEP 6: Update fields `b' and `d' on the file leaving the other
     *         fields unchanged.  This tests member alignment and background
     *	       buffers.
     */
    printf("%-70s", "Testing partially initialized superset write");
    fflush (stdout);

    /* Initialize `s4' with new values */
    for (i=0; i<NX*NY; i++) {
	s4[i].b = 8*i+1;
	s4[i].d = 8*i+6;
    }

    /* Write the data to file */
    if (H5Dwrite (dataset, s4_tid, H5S_ALL, H5S_ALL, PRESERVE, s4)<0) {
	goto error;
    }

    /* Read the data back */
    if (H5Dread (dataset, s1_tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, s1)<0) {
	goto error;
    }

    /* Compare */
    for (i=0; i<NX*NY; i++) {
	if (s1[i].a != 8*i+0 ||
	    s1[i].b != 8*i+1 ||
	    s1[i].c[0] != 8*i+2 ||
	    s1[i].c[1] != 8*i+3 ||
	    s1[i].c[2] != 8*i+4 ||
	    s1[i].c[3] != 8*i+5 ||
	    s1[i].d != 8*i+6 ||
	    s1[i].e != 8*i+7) {
	    puts("*FAILED*");
	    puts("   File values were clobbered");
	    goto error;
	}
    }
    puts(" PASSED");
    
    /*
     *######################################################################
     * STEP 7. Read the original dataset with an explicit data space.  Even
     * though these data spaces are equal it tests a different part of the
     * library.
     */
    printf("%-70s", "Testing explicit data space");
    fflush (stdout);

    /* Create the data space */
    if ((s7_sid = H5Screate_simple (2, dim, NULL))<0) goto error;
    
    /* Read the dataset */
    if (H5Dread (dataset, s2_tid, s7_sid, H5S_ALL, H5P_DEFAULT, s2)<0) {
	goto error;
    }

    /* Compare */
    for (i=0; i<NX*NY; i++) {
	if (s2[i].a != s1[i].a ||
	    s2[i].b != s1[i].b ||
	    s2[i].c[0] != s1[i].c[0] ||
	    s2[i].c[1] != s1[i].c[1] ||
	    s2[i].c[2] != s1[i].c[2] ||
	    s2[i].c[3] != s1[i].c[3] ||
	    s2[i].d != s1[i].d ||
	    s2[i].e != s1[i].e) {
	    puts("*FAILED*");
	    puts("   Incorrect values read from file");
	    goto error;
	}
    }
    puts(" PASSED");
    

    /*
     *######################################################################
     * STEP 8. Read a hyperslab of the file into a complete array in memory.
     * The hyperslab is the middle third of the array.
     */
    printf("%-70s", "Testing hyperslab partial read to array");
    fflush (stdout);

    /* Create the file data space */
    if ((s8_f_sid = H5Dget_space (dataset))<0) goto error;
    f_offset[0] = NX/3;
    f_offset[1] = NY/3;
    h_size[0] = 2*NX/3 - f_offset[0];
    h_size[1] = 2*NY/3 - f_offset[1];
    if (H5Sselect_hyperslab (s8_f_sid, H5S_SELECT_SET, f_offset, NULL,
			     h_size, NULL)<0) goto error;

    /* Create memory data space */
    if ((s8_m_sid = H5Screate_simple (2, h_size, NULL))<0) goto error;

    /* Read the dataset */
    s8 = calloc ((size_t)(h_size[0]*h_size[1]), sizeof(s1_t));
    assert (s8);
    if (H5Dread (dataset, s1_tid, s8_m_sid, s8_f_sid, H5P_DEFAULT, s8)<0) {
	goto error;
    }

    /* Compare */
    for (i=0; i<h_size[0]; i++) {
	for (j=0; j<h_size[1]; j++) {
	    s1_t *ps1 = s1 + (f_offset[0]+i)*NY + f_offset[1] + j;
	    s1_t *ps8 = s8 + i*h_size[1] + j;

	    if (ps8->a != ps1->a ||
		ps8->b != ps1->b ||
		ps8->c[0] != ps1->c[0] ||
		ps8->c[1] != ps1->c[1] ||
		ps8->c[2] != ps1->c[2] ||
		ps8->c[3] != ps1->c[3] ||
		ps8->d != ps1->d ||
		ps8->e != ps1->e) {
		puts("*FAILED*");
		puts("   Incorrect values read from file");
		goto error;
	    }
	}
    }

    free (s8);
    s8 = NULL;
    puts(" PASSED");


    /*
     *######################################################################
     * STEP 9.  Read a hyperslab of the file into a hyperslab of memory.  The
     * part of memory not read is already initialized and must not change.
     */
    printf("%-70s", "Testing hyperslab partial read to another hyperslab");
    fflush (stdout);

    /* Initialize */
    for (i=0; i<NX*NY; i++) {
	s2[i].a = s2[i].b = s2[i].d = s2[i].e = (unsigned)(-1);
	s2[i].c[0] = s2[i].c[1] = s2[i].c[2] = s2[i].c[3] = (unsigned)(-1);
    }
    
    /* Read the hyperslab */
    if (H5Dread (dataset, s2_tid, s8_f_sid, s8_f_sid, H5P_DEFAULT, s2)<0) {
	goto error;
    }

    /* Compare */
    for (i=0; i<NX; i++) {
	for (j=0; j<NY; j++) {
	    s1_t *ps1 = s1 + i*NY + j;
	    s2_t *ps2 = s2 + i*NY + j;
	    if ((hssize_t)i>=f_offset[0] &&
		(hsize_t)i<f_offset[0]+h_size[0] &&
		(hssize_t)j>=f_offset[1] &&
		(hsize_t)j<f_offset[1]+h_size[1]) {
		if (ps2->a != ps1->a ||
		    ps2->b != ps1->b ||
		    ps2->c[0] != ps1->c[0] ||
		    ps2->c[1] != ps1->c[1] ||
		    ps2->c[2] != ps1->c[2] ||
		    ps2->c[3] != ps1->c[3] ||
		    ps2->d != ps1->d ||
		    ps2->e != ps1->e) {
		    puts("*FAILED*");
		    puts("   Memory values clobbered");
		    goto error;
		}
	    } else {
		if (ps2->a != (unsigned)(-1) ||
		    ps2->b != (unsigned)(-1) ||
		    ps2->c[0] != (unsigned)(-1) ||
		    ps2->c[1] != (unsigned)(-1) ||
		    ps2->c[2] != (unsigned)(-1) ||
		    ps2->c[3] != (unsigned)(-1) ||
		    ps2->d != (unsigned)(-1) ||
		    ps2->e != (unsigned)(-1)) {
		    puts("*FAILED*");
		    puts("   Incorrect values read from file");
		    goto error;
		}
	    }
	}
    }
    puts(" PASSED");
    
    /*
     *######################################################################
     * STEP 10. Same as step 9 except the memory array contains some members
     * which are already initialized, like step 5.
     */
    printf("%-70s", "Testing hyperslab to hyperslab part initialized read");
    fflush (stdout);

    /* Initialize */
    for (i=0; i<NX*NY; i++) {
	s5[i].a = s5[i].b = s5[i].d = s5[i].e = (unsigned)(-1);
	s5[i].c[0] = s5[i].c[1] = s5[i].c[2] = s5[i].c[3] = (unsigned)(-1);
	s5[i].pre = s5[i].mid1 = s5[i].mid2 = s5[i].post = (unsigned)(-1);
    }
    
    /* Read the hyperslab */
    if (H5Dread (dataset, s5_tid, s8_f_sid, s8_f_sid, PRESERVE, s5)<0) {
	goto error;
    }

    /* Compare */
    for (i=0; i<NX; i++) {
	for (j=0; j<NY; j++) {
	    s1_t *ps1 = s1 + i*NY + j;
	    s5_t *ps5 = s5 + i*NY + j;
	    if ((hssize_t)i>=f_offset[0] &&
		(hsize_t)i<f_offset[0]+h_size[0] &&
		(hssize_t)j>=f_offset[1] &&
		(hsize_t)j<f_offset[1]+h_size[1]) {
		if (ps5->pre != (unsigned)(-1) ||
		    ps5->a != ps1->a ||
		    ps5->b != ps1->b ||
		    ps5->mid1 != (unsigned)(-1) ||
		    ps5->c[0] != ps1->c[0] ||
		    ps5->c[1] != ps1->c[1] ||
		    ps5->c[2] != ps1->c[2] ||
		    ps5->c[3] != ps1->c[3] ||
		    ps5->mid2 != (unsigned)(-1) ||
		    ps5->d != ps1->d ||
		    ps5->e != ps1->e ||
		    ps5->post != (unsigned)(-1)) {
		    puts("*FAILED*");
		    puts("   Memory values clobbered");
		    goto error;
		}
	    } else {
		if (ps5->pre != (unsigned)(-1) ||
		    ps5->a != (unsigned)(-1) ||
		    ps5->b != (unsigned)(-1) ||
		    ps5->mid1 != (unsigned)(-1) ||
		    ps5->c[0] != (unsigned)(-1) ||
		    ps5->c[1] != (unsigned)(-1) ||
		    ps5->c[2] != (unsigned)(-1) ||
		    ps5->c[3] != (unsigned)(-1) ||
		    ps5->mid2 != (unsigned)(-1) ||
		    ps5->d != (unsigned)(-1) ||
		    ps5->e != (unsigned)(-1) ||
		    ps5->post != (unsigned)(-1)) {
		    puts("*FAILED*");
		    puts("   Incorrect values read from file");
		    goto error;
		}
	    }
	}
    }
    puts(" PASSED");
		
    /*
     *######################################################################
     * Step 11: Write an array into the middle third of the dataset
     * initializeing only members `b' and `d' to -1.
     */
#if 0
    printf ("\
STEP 11: Write an array back to the middle third of the dataset to\n\
         initialize the `b' and `d' members to -1.\n");
    fflush (stdout);
    
    /* Create the memory array and initialize all fields to zero */
    ndims = 2;
    f_offset[0] = NX/3;
    f_offset[1] = NY/3;
    h_size[0] = 2*NX/3 - f_offset[0];
    h_size[1] = 2*NY/3 - f_offset[1];
    s11 = malloc ((size_t)h_size[0]*(size_t)h_size[1]*sizeof(s4_t));
    assert (s11);

    /* Initialize */
    for (i=0; i<h_size[0]*h_size[1]; i++) {
	s11[i].b = s11[i].d = (unsigned)(-1);
    }
    
    /* Write to disk */
    status = H5Dwrite (dataset, s4_tid, s8_m_sid, s8_f_sid, PRESERVE, s11);
    assert (status>=0);
    free (s11);
    s11=NULL;

    /* Read the whole thing */
    status = H5Dread (dataset, s1_tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, s1);
    assert (status>=0);

    /* Compare */
    for (i=0; i<NX; i++) {
	for (j=0; j<NY; j++) {
	    s1_t *ps1 = s1 + i*NY + j;
	    
	    assert (ps1->a == 8*(i*NY+j)+0);
	    assert (ps1->c[0] == 8*(i*NY+j)+2);
	    assert (ps1->c[1] == 8*(i*NY+j)+3);
	    assert (ps1->c[2] == 8*(i*NY+j)+4);
	    assert (ps1->c[3] == 8*(i*NY+j)+5);
	    assert (ps1->e == 8*(i*NY+j)+7);
	    if ((hssize_t)i>=f_offset[0] &&
		(hsize_t)i<f_offset[0]+h_size[0] &&
		(hssize_t)j>=f_offset[1] &&
		(hsize_t)j<f_offset[1]+h_size[1]) {
		assert (ps1->b == (unsigned)(-1));
		assert (ps1->d == (unsigned)(-1));
	    } else {
		assert (ps1->b == 8*(i*NY+j)+1);
		assert (ps1->d == 8*(i*NY+j)+6);
	    }
	}
    }
#endif



    
    /*
     * Release resources.
     */
    H5Pclose (PRESERVE);
    H5Dclose (dataset);
    H5Fclose (file);

    cleanup();
    puts("All compound dataset tests passed.");
    return 0;

error:
    cleanup();
    puts("Remaining tests have been skipped.");
    puts("*** DATASET TESTS FAILED ***");
    return 1;
}