/* This is part of the netCDF package.
   Copyright 2005 University Corporation for Atmospheric Research/Unidata
   See COPYRIGHT file for conditions of use.

   Test HDF5 file code. These are not intended to be exhaustive tests,
   but they use HDF5 the same way that netCDF-4 does, so if these
   tests don't work, than netCDF-4 won't work either.

   This program deals with HDF5 compound types.

   $Id: tst_h_compounds.c,v 1.17 2008/05/30 17:23:26 ed Exp $
*/
#include <nc_tests.h>

#define FILE_NAME "tst_h_compounds.h5"
#define DIM1_LEN 3
#define OSMONDS "TheOsmonds"
#define WHO "TheWho"
#define BOOZE_VAR "Alcohol_Consumed"
#define BEER_OR_WINE "Beer_or_Wine"
#define LIQUOR "The_Hard_Stuff"
#define COMPOUND_NAME "Booze_Index"
#define ARRAY_LEN 5

int
main()
{
   hid_t fileid, osmonds_grpid, who_grpid, spaceid, typeid;
   hid_t datasetid, datasetid1, typeid1;
   hsize_t dims[1];
   struct s1 {
	 int i1, i2;
   } data[DIM1_LEN];
   struct s2 {
	 int i1[ARRAY_LEN], i2;
   } data2[DIM1_LEN];
   int i, j;

   for (i=0; i<DIM1_LEN; i++)
   {
      data[i].i1 = 99;
      data[i].i2 = -99;
      data2[i].i2 = -99;
      for (j=0; j<ARRAY_LEN; j++)
	 data2[i].i1[j] = 99;
  }

   printf("\n*** Checking HDF5 compound types.\n");
   printf("*** Checking simple HDF5 compound types...");
   {
   
      /* Open file and create group. */
      if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, 
			      H5P_DEFAULT)) < 0) ERR;
      if ((osmonds_grpid = H5Gcreate(fileid, OSMONDS, 0)) < 0) ERR;

      /* Create a simple compound type. */
      if ((typeid = H5Tcreate(H5T_COMPOUND, sizeof(struct s1))) < 0) ERR;
      if (H5Tinsert(typeid, BEER_OR_WINE, HOFFSET(struct s1, i1), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tinsert(typeid, LIQUOR, HOFFSET(struct s1, i2), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tcommit(osmonds_grpid, COMPOUND_NAME, typeid) < 0) ERR;

      /* Create a space. */
      dims[0] = DIM1_LEN;
      if ((spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;

      /* Create a dataset of this compound type. */
      if ((datasetid = H5Dcreate(osmonds_grpid, BOOZE_VAR, typeid, 
				 spaceid, H5P_DEFAULT)) < 0) ERR;

      /* Write some data. */
      if (H5Dwrite(datasetid, typeid, H5S_ALL, H5S_ALL, 
		   H5P_DEFAULT, data) < 0) ERR;

      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Sclose(spaceid) < 0 ||
	  H5Gclose(osmonds_grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

      /* Now open the file and read it. */
      if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) ERR;
      if ((osmonds_grpid = H5Gopen(fileid, OSMONDS)) < 0) ERR;
      if ((datasetid = H5Dopen(osmonds_grpid, BOOZE_VAR)) < 0) ERR;
      if ((typeid = H5Dget_type(datasetid)) < 0) ERR;
      if (H5Tget_class(typeid) != H5T_COMPOUND) ERR;
      if (H5Tget_nmembers(typeid) != 2) ERR;
      /* This doesn't work because all I have is a reference to the type! 
	 if (H5Iget_name(typeid, type_name, NC_MAX_NAME) < 0) ERR;
	 if (strcmp(type_name, COMPOUND_NAME)) ERR;*/

      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Gclose(osmonds_grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;
   }

   SUMMARIZE_ERR;
   printf("*** Checking HDF5 compound types and groups...");
   
   {
      /* Open file and create two group. */
      if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, 
			      H5P_DEFAULT)) < 0) ERR;
      if ((osmonds_grpid = H5Gcreate(fileid, OSMONDS, 0)) < 0) ERR;
      if ((who_grpid = H5Gcreate(fileid, WHO, 0)) < 0) ERR;

      /* Create a simple compound type. */
      if ((typeid = H5Tcreate(H5T_COMPOUND, sizeof(struct s1))) < 0) ERR;
      if (H5Tinsert(typeid, BEER_OR_WINE, HOFFSET(struct s1, i1), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tinsert(typeid, LIQUOR, HOFFSET(struct s1, i2), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tcommit(osmonds_grpid, COMPOUND_NAME, typeid) < 0) ERR;

      /* Create a space. */
      dims[0] = DIM1_LEN;
      if ((spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;

      /* Create a dataset of this compound type in the same group. */
      if ((datasetid = H5Dcreate(osmonds_grpid, BOOZE_VAR, typeid, 
				 spaceid, H5P_DEFAULT)) < 0) ERR;

      /* Write some data. */
      if (H5Dwrite(datasetid, typeid, H5S_ALL, H5S_ALL, 
		   H5P_DEFAULT, data) < 0) ERR;

      /* Create a dataset of this compound type in a different group. */
      if ((datasetid1 = H5Dcreate(who_grpid, BOOZE_VAR, typeid, 
				  spaceid, H5P_DEFAULT)) < 0) ERR;

      /* Write some data. */
      if (H5Dwrite(datasetid1, typeid, H5S_ALL, H5S_ALL, 
		   H5P_DEFAULT, data) < 0) ERR;

      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Dclose(datasetid1) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Sclose(spaceid) < 0 ||
	  H5Gclose(osmonds_grpid) < 0 ||
	  H5Gclose(who_grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

      {
	 hsize_t num_obj;
	 int i, obj_type;
	 char name[NC_MAX_NAME + 1];
	 htri_t equal;


	 /* Now open the file and read it. */
	 if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) ERR;
	 if ((osmonds_grpid = H5Gopen(fileid, OSMONDS)) < 0) ERR;
	 if (H5Gget_num_objs(osmonds_grpid, &num_obj) < 0) ERR;
	 for (i=0; i<num_obj; i++)
	 {
	    if (H5Gget_objname_by_idx(osmonds_grpid, i, name, NC_MAX_NAME+1) < 0) ERR;
	    if ((obj_type = H5Gget_objtype_by_idx(osmonds_grpid, i)) < 0) ERR;
	    switch(obj_type)
	    {
	       case H5G_DATASET:
		  if ((datasetid = H5Dopen(osmonds_grpid, name)) < 0) ERR;
		  break;
	       case H5G_TYPE:
		  if ((typeid = H5Topen(osmonds_grpid, name)) < 0) ERR;
		  if (H5Tget_class(typeid) != H5T_COMPOUND) ERR;
		  if (H5Tget_nmembers(typeid) != 2) ERR;
		  if (strcmp(name, COMPOUND_NAME)) ERR;
		  break;
	       default:
		  ERR;
	    }
	 }

	 /* Open the other dataset, and learn about its type. */
	 if ((who_grpid = H5Gopen(fileid, WHO)) < 0) ERR;
	 if ((datasetid1 = H5Dopen(who_grpid, BOOZE_VAR)) < 0) ERR;
	 if ((typeid1 = H5Dget_type(datasetid1)) < 0) ERR;
	 if ((equal = H5Tequal(typeid, typeid1)) < 0) ERR;
	 if (!equal) ERR;
      }

      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Dclose(datasetid1) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Tclose(typeid1) < 0 ||
	  H5Gclose(osmonds_grpid) < 0 ||
	  H5Gclose(who_grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

   }

   SUMMARIZE_ERR;
   printf("*** Checking HDF5 compound type which contains an array...");
   
   {
      hsize_t array_dims[] = {ARRAY_LEN};
      hid_t array_typeid;

      /* Open file and create group. */
      if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, 
			      H5P_DEFAULT)) < 0) ERR;
      if ((osmonds_grpid = H5Gcreate(fileid, OSMONDS, 0)) < 0) ERR;

      /* Create an array type. */
      if ((array_typeid = H5Tarray_create(H5T_NATIVE_INT, 1, array_dims, NULL)) < 0) ERR;

      /* Create a compound type containing an array. */
      if ((typeid = H5Tcreate(H5T_COMPOUND, sizeof(struct s2))) < 0) ERR;
      if (H5Tinsert(typeid, BEER_OR_WINE, HOFFSET(struct s2, i1), array_typeid) < 0) ERR;
      if (H5Tinsert(typeid, LIQUOR, HOFFSET(struct s2, i2), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tcommit(osmonds_grpid, COMPOUND_NAME, typeid) < 0) ERR;

      /* Create a space. */
      dims[0] = DIM1_LEN;
      if ((spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;

      /* Create a dataset of this compound type. */
      if ((datasetid = H5Dcreate(osmonds_grpid, BOOZE_VAR, typeid, 
				 spaceid, H5P_DEFAULT)) < 0) ERR;

      /* Write some data. */
      if (H5Dwrite(datasetid, typeid, H5S_ALL, H5S_ALL, 
		   H5P_DEFAULT, data2) < 0) ERR;
      
      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Tclose(array_typeid) < 0 ||
	  H5Sclose(spaceid) < 0 ||
	  H5Gclose(osmonds_grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

      /* Now open the file and read it. */
      if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) ERR;
      if ((osmonds_grpid = H5Gopen(fileid, OSMONDS)) < 0) ERR;
      if ((datasetid = H5Dopen(osmonds_grpid, BOOZE_VAR)) < 0) ERR;
      if ((typeid = H5Dget_type(datasetid)) < 0) ERR;
      if (H5Tget_class(typeid) != H5T_COMPOUND) ERR;
      if (H5Tget_nmembers(typeid) != 2) ERR;
      /* This doesn't work because all I have is a reference to the type! 
	 if (H5Iget_name(typeid, type_name, NC_MAX_NAME) < 0) ERR;
	 if (strcmp(type_name, COMPOUND_NAME)) ERR;*/

      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Gclose(osmonds_grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;
   }

   SUMMARIZE_ERR;
   printf("*** Checking HDF5 compound type 6 different types...");
   
   {
#define DAY "day"
#define ELEV "elev"
#define COUNT "count"
#define RELHUM "relhum"
#define TIME "time"
#define OBS_T "obs_t"
#define OBS_VAR "obs_var"
#define DIM6_LEN 3
      
      hid_t fileid, grpid, spaceid, typeid, native_typeid;
      hid_t datasetid, mem_type;
      hsize_t dims[1];
      typedef struct obs_t {
	    char day ;
	    short elev;
	    int count;
	    float relhum;
	    double time;
      } obs_t ;
      obs_t obsdata[DIM6_LEN] = {
	 {15, 2, 1, 0.5, 3600.01},
	 {-99, -99, -99, -99.0f, -99.0},
	 {20, 6, 3, 0.75, 5000.01}
      };
      obs_t obsdata_in[DIM6_LEN], obsdata2_in[DIM6_LEN];
      char file_in[NC_MAX_NAME * 2];
      size_t size_in;

      /* Open file and create group. */
      if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;

      /* Create a compound type containing some different types. */
      if ((typeid = H5Tcreate(H5T_COMPOUND, sizeof(struct obs_t))) < 0) ERR;
      if (H5Tinsert(typeid, DAY, HOFFSET(struct obs_t, day), H5T_NATIVE_CHAR) < 0) ERR;
      if (H5Tinsert(typeid, ELEV, HOFFSET(struct obs_t, elev), H5T_NATIVE_SHORT) < 0) ERR;
      if (H5Tinsert(typeid, COUNT, HOFFSET(struct obs_t, count), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tinsert(typeid, RELHUM, HOFFSET(struct obs_t, relhum), H5T_NATIVE_FLOAT) < 0) ERR;
      if (H5Tinsert(typeid, TIME, HOFFSET(struct obs_t, time), H5T_NATIVE_DOUBLE) < 0) ERR;
      if (H5Tcommit(grpid, OBS_T, typeid) < 0) ERR;

      /* Create a space. */
      dims[0] = DIM6_LEN;
      if ((spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;

      /* Create a dataset of this compound type. */
      if ((datasetid = H5Dcreate(grpid, OBS_VAR, typeid, spaceid, H5P_DEFAULT)) < 0) ERR;

      /* Write some data. */
      if (H5Dwrite(datasetid, typeid, H5S_ALL, H5S_ALL, H5P_DEFAULT, obsdata) < 0) ERR;
      
      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Sclose(spaceid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

      /* Now open the file and read it. */
      if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;
      if ((datasetid = H5Dopen(grpid, OBS_VAR)) < 0) ERR;
      if ((typeid = H5Dget_type(datasetid)) < 0) ERR;
      if ((size_in = H5Tget_size(typeid)) < 0) ERR;
      if (size_in != sizeof(struct obs_t)) ERR;
      if ((native_typeid = H5Tget_native_type(typeid, H5T_DIR_DEFAULT)) < 0) ERR;
      if ((size_in = H5Tget_size(native_typeid)) < 0) ERR;
      if (size_in != sizeof(struct obs_t)) ERR;
      if (H5Tget_class(typeid) != H5T_COMPOUND) ERR;
      if (H5Tget_nmembers(typeid) != 5) ERR;

      /* Read all data. */
      if (H5Dread(datasetid, typeid, H5S_ALL, H5S_ALL, H5P_DEFAULT, obsdata_in) < 0) ERR;

      /* Check the data. */
      for (i = 0; i < DIM6_LEN; i++)
      {
	 if (obsdata[i].day != obsdata_in[i].day || obsdata[i].elev != obsdata_in[i].elev ||
	     obsdata[i].count != obsdata_in[i].count || obsdata[i].relhum != obsdata_in[i].relhum ||
	     obsdata[i].time != obsdata_in[i].time) 
	 {
	    ERR;
	    return 1;
	 }
      }

      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

      /* Now open the reference copy of this file and read it. */
#define REF_FILE_NAME "ref_tst_h_compounds.h5"      
      if (getenv("srcdir"))
      {
	 strcpy(file_in, getenv("srcdir"));
	 strcat(file_in, "/");
	 strcat(file_in, REF_FILE_NAME);
      } 
      else
	 strcpy(file_in, REF_FILE_NAME);

      if ((fileid = H5Fopen(file_in, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;
      if ((datasetid = H5Dopen(grpid, OBS_VAR)) < 0) ERR;
      if ((typeid = H5Dget_type(datasetid)) < 0) ERR;
      if ((mem_type = H5Tget_native_type(typeid, H5T_DIR_DEFAULT)) < 0) ERR;
      if (H5Tget_class(typeid) != H5T_COMPOUND) ERR;
      if (H5Tget_nmembers(typeid) != 5) ERR;

      /* Read all data. */
      if (H5Dread(datasetid, mem_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, obsdata2_in) < 0) ERR;

      /* Check the data. */
      for (i = 0; i < DIM6_LEN; i++)
      {
	 if (obsdata[i].day != obsdata2_in[i].day || obsdata[i].elev != obsdata2_in[i].elev ||
	     obsdata[i].count != obsdata2_in[i].count || obsdata[i].relhum != obsdata2_in[i].relhum ||
	     obsdata[i].time != obsdata2_in[i].time) 
	 {
	    ERR;
	    return 1;
	 }
      }

      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;
   }

   SUMMARIZE_ERR;
   printf("*** Checking HDF5 compound variable which contains a compound type...");
   {
#define DATASET_NAME "Enterprise"
      /* This struct will be embeddeded in another. */
      struct s1 
      {
	    int i1;
	    int i2;
      };
      /* StarFleet Human Resources Department has data records for all
       * employees. */
      struct hr_rec
      {
	    int starfleet_id;
	    struct s1 svc_rec;
	    char name[NC_MAX_NAME + 1];
	    float max_temp, min_temp; /* temperature range */
	    double percent_transporter_errosion; 
      };
      struct hr_rec hr_data_out[DIM1_LEN], hr_data_in[DIM1_LEN];

      hid_t fileid, grpid, typeid_inner, typeid, spaceid, array1_tid, datasetid;
      hsize_t dims[1];
      int i;

      /* Create some phony data. */   
      for (i = 0; i < DIM1_LEN; i++)
      {
	 hr_data_out[i].starfleet_id = i;
	 hr_data_out[i].svc_rec.i1 = 95;
	 hr_data_out[i].svc_rec.i2 = 90;
	 if (sprintf(hr_data_out[i].name, "alien_%d", i) < 0) ERR;
	 hr_data_out[i].max_temp = 99.99;
	 hr_data_out[i].min_temp = -9.99;
	 hr_data_out[i].percent_transporter_errosion = .1;
      }

      /* Open file and get root group. */
      if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;

      /* Create a compound type. */
      if ((typeid_inner = H5Tcreate(H5T_COMPOUND, sizeof(struct s1))) < 0) ERR;
      if (H5Tinsert(typeid_inner, "i1", HOFFSET(struct s1, i1), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tinsert(typeid_inner, "i2", HOFFSET(struct s1, i2), H5T_NATIVE_INT) < 0) ERR;

      /* Create a compound type containing a compound type. */
      if ((typeid = H5Tcreate(H5T_COMPOUND, sizeof(struct hr_rec))) < 0) ERR;
      if (H5Tinsert(typeid, "starfleet_id", HOFFSET(struct hr_rec, starfleet_id), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tinsert(typeid, "svc_rec", HOFFSET(struct hr_rec, svc_rec), typeid_inner) < 0) ERR;
      if ((array1_tid = H5Tcopy(H5T_C_S1)) < 0) ERR;
      if (H5Tset_size(array1_tid, NC_MAX_NAME + 1) < 0) ERR;
      if (H5Tinsert(typeid, "name", HOFFSET(struct hr_rec, name), array1_tid) < 0) ERR;
      if (H5Tinsert(typeid, "max_temp", HOFFSET(struct hr_rec, max_temp), H5T_NATIVE_FLOAT) < 0) ERR;
      if (H5Tinsert(typeid, "min_temp", HOFFSET(struct hr_rec, min_temp), H5T_NATIVE_FLOAT) < 0) ERR;
      if (H5Tinsert(typeid, "percent_transporter_errosion", HOFFSET(struct hr_rec, percent_transporter_errosion), 
		    H5T_NATIVE_DOUBLE) < 0) ERR;
      if (H5Tcommit(grpid, "hr_rec", typeid) < 0) ERR;

      /* Create a space. */
      dims[0] = DIM1_LEN;
      if ((spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;

      /* Create a dataset of this compound type. */
      if ((datasetid = H5Dcreate(grpid, DATASET_NAME, typeid, spaceid, H5P_DEFAULT)) < 0) ERR;

      /* Write some data. */
      if (H5Dwrite(datasetid, typeid, H5S_ALL, H5S_ALL, H5P_DEFAULT, hr_data_out) < 0) ERR;
      
      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(array1_tid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Tclose(typeid_inner) < 0 ||
	  H5Sclose(spaceid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

      /* Now open the file and read it. */
      if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;
      if ((datasetid = H5Dopen(grpid, DATASET_NAME)) < 0) ERR;
      if ((typeid = H5Dget_type(datasetid)) < 0) ERR;
      if (H5Tget_class(typeid) != H5T_COMPOUND) ERR;
      if (H5Dread(datasetid, typeid, H5S_ALL, H5S_ALL, H5P_DEFAULT, hr_data_in) < 0) ERR;

      /* Check the data. */
      for (i = 0; i < DIM1_LEN; i++)
	 if (hr_data_out[i].starfleet_id != hr_data_in[i].starfleet_id ||
	     hr_data_out[i].svc_rec.i1 != hr_data_in[i].svc_rec.i1 ||
	     hr_data_out[i].svc_rec.i2 != hr_data_in[i].svc_rec.i2 ||
	     strcmp(hr_data_out[i].name, hr_data_in[i].name) ||
	     hr_data_out[i].max_temp != hr_data_in[i].max_temp ||
	     hr_data_out[i].min_temp != hr_data_in[i].min_temp ||
	     hr_data_out[i].percent_transporter_errosion != hr_data_in[i].percent_transporter_errosion)
	    ERR;

      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;
   }

   SUMMARIZE_ERR;
   printf("*** Checking HDF5 variable which contains a compound type with different string handling...");
   {
#define DATASET_NAME "Enterprise"
      /* This struct will be embeddeded in another. */
      struct s1 
      {
	    int i1;
	    int i2;
      };
      /* StarFleet Human Resources Department has data records for all
       * employees. */
      struct hr_rec
      {
	    int starfleet_id;
	    struct s1 svc_rec;
	    char name[NC_MAX_NAME + 1];
	    float max_temp, min_temp; /* temperature range */
	    double percent_transporter_errosion; 
      };
      struct hr_rec hr_data_out[DIM1_LEN], hr_data_in[DIM1_LEN];

      hid_t fileid, grpid, typeid_inner, typeid, spaceid, array1_tid, datasetid, str_tid;
      hsize_t dims[1];
      int i;

      /* Create some phony data. */   
      for (i = 0; i < DIM1_LEN; i++)
      {
	 hr_data_out[i].starfleet_id = i;
	 hr_data_out[i].svc_rec.i1 = 95;
	 hr_data_out[i].svc_rec.i2 = 90;
	 if (sprintf(hr_data_out[i].name, "alien_%d", i) < 0) ERR;
	 hr_data_out[i].max_temp = 99.99;
	 hr_data_out[i].min_temp = -9.99;
	 hr_data_out[i].percent_transporter_errosion = .1;
      }

      /* Open file and get root group. */
      if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;

      /* Create a compound type. */
      if ((typeid_inner = H5Tcreate(H5T_COMPOUND, sizeof(struct s1))) < 0) ERR;
      if (H5Tinsert(typeid_inner, "i1", HOFFSET(struct s1, i1), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tinsert(typeid_inner, "i2", HOFFSET(struct s1, i2), H5T_NATIVE_INT) < 0) ERR;

      /* Create a compound type containing a compound type. */
      if ((typeid = H5Tcreate(H5T_COMPOUND, sizeof(struct hr_rec))) < 0) ERR;
      if (H5Tinsert(typeid, "starfleet_id", HOFFSET(struct hr_rec, starfleet_id), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tinsert(typeid, "svc_rec", HOFFSET(struct hr_rec, svc_rec), typeid_inner) < 0) ERR;
      dims[0] = NC_MAX_NAME + 1;
      if ((str_tid = H5Tcopy(H5T_C_S1)) < 0) ERR;
      if (H5Tset_strpad(str_tid, H5T_STR_NULLTERM) < 0) ERR;
      if ((array1_tid = H5Tarray_create2(str_tid, 1, dims)) < 0) ERR;
      if (H5Tinsert(typeid, "name", HOFFSET(struct hr_rec, name), array1_tid) < 0) ERR;
      if (H5Tinsert(typeid, "max_temp", HOFFSET(struct hr_rec, max_temp), H5T_NATIVE_FLOAT) < 0) ERR;
      if (H5Tinsert(typeid, "min_temp", HOFFSET(struct hr_rec, min_temp), H5T_NATIVE_FLOAT) < 0) ERR;
      if (H5Tinsert(typeid, "percent_transporter_errosion", HOFFSET(struct hr_rec, percent_transporter_errosion), 
		    H5T_NATIVE_DOUBLE) < 0) ERR;
      if (H5Tcommit(grpid, "hr_rec", typeid) < 0) ERR;

      /* Create a space. */
      dims[0] = DIM1_LEN;
      if ((spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;

      /* Create a dataset of this compound type. */
      if ((datasetid = H5Dcreate(grpid, DATASET_NAME, typeid, spaceid, H5P_DEFAULT)) < 0) ERR;

      /* Write some data. */
      if (H5Dwrite(datasetid, typeid, H5S_ALL, H5S_ALL, H5P_DEFAULT, hr_data_out) < 0) ERR;
      
      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(array1_tid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Tclose(typeid_inner) < 0 ||
	  H5Tclose(str_tid) < 0 ||
	  H5Sclose(spaceid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

      /* Now open the file and read it. */
      if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;
      if ((datasetid = H5Dopen(grpid, DATASET_NAME)) < 0) ERR;
      if ((typeid = H5Dget_type(datasetid)) < 0) ERR;
      if (H5Tget_class(typeid) != H5T_COMPOUND) ERR;
      if (H5Dread(datasetid, typeid, H5S_ALL, H5S_ALL, H5P_DEFAULT, hr_data_in) < 0) ERR;

      /* Check the data. */
      for (i = 0; i < DIM1_LEN; i++)
	 if (hr_data_out[i].starfleet_id != hr_data_in[i].starfleet_id ||
	     hr_data_out[i].svc_rec.i1 != hr_data_in[i].svc_rec.i1 ||
	     hr_data_out[i].svc_rec.i2 != hr_data_in[i].svc_rec.i2 ||
	     strcmp(hr_data_out[i].name, hr_data_in[i].name) ||
	     hr_data_out[i].max_temp != hr_data_in[i].max_temp ||
	     hr_data_out[i].min_temp != hr_data_in[i].min_temp ||
	     hr_data_out[i].percent_transporter_errosion != hr_data_in[i].percent_transporter_errosion)
	    ERR;

      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;
   }

   SUMMARIZE_ERR;
   printf("*** Checking HDF5 compound attribute which contains a compound type...");
   {
#define ATT_NAME1 "Space_Station_Regula_One"
      /* This struct will be embeddeded in another. */
      struct s1 
      {
	    int i1;
	    int i2;
      };
      /* StarFleet Human Resources Department has data records for all
       * employees. */
      struct hr_rec
      {
	    int starfleet_id;
	    struct s1 svc_rec;
	    char name[NC_MAX_NAME + 1];
	    float max_temp, min_temp; /* temperature range */
	    double percent_transporter_errosion; 
      };
      struct hr_rec hr_data_out[DIM1_LEN], hr_data_in[DIM1_LEN];

      hid_t fileid, grpid, typeid_inner, typeid, spaceid, array1_tid, attid, native_typeid;
      hsize_t dims[1];
      int i;

      /* Create some phony data. */   
      for (i = 0; i < DIM1_LEN; i++)
      {
	 hr_data_out[i].starfleet_id = i;
	 hr_data_out[i].svc_rec.i1 = 95;
	 hr_data_out[i].svc_rec.i2 = 90;
	 if (sprintf(hr_data_out[i].name, "alien_%d", i) < 0) ERR;
	 hr_data_out[i].max_temp = 99.99;
	 hr_data_out[i].min_temp = -9.99;
	 hr_data_out[i].percent_transporter_errosion = .1;
      }

      /* Open file and get root group. */
      if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;

      /* Create a compound type. */
      if ((typeid_inner = H5Tcreate(H5T_COMPOUND, sizeof(struct s1))) < 0) ERR;
      if (H5Tinsert(typeid_inner, "i1", HOFFSET(struct s1, i1), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tinsert(typeid_inner, "i2", HOFFSET(struct s1, i2), H5T_NATIVE_INT) < 0) ERR;

      /* Create a compound type containing a compound type. */
      if ((typeid = H5Tcreate(H5T_COMPOUND, sizeof(struct hr_rec))) < 0) ERR;
      if (H5Tinsert(typeid, "starfleet_id", HOFFSET(struct hr_rec, starfleet_id), H5T_NATIVE_INT) < 0) ERR;
      if (H5Tinsert(typeid, "svc_rec", HOFFSET(struct hr_rec, svc_rec), typeid_inner) < 0) ERR;
      if ((array1_tid = H5Tcopy(H5T_C_S1)) < 0) ERR;
      if (H5Tset_size(array1_tid, NC_MAX_NAME + 1) < 0) ERR;
      if (H5Tinsert(typeid, "name", HOFFSET(struct hr_rec, name), array1_tid) < 0) ERR;
      if (H5Tinsert(typeid, "max_temp", HOFFSET(struct hr_rec, max_temp), H5T_NATIVE_FLOAT) < 0) ERR;
      if (H5Tinsert(typeid, "min_temp", HOFFSET(struct hr_rec, min_temp), H5T_NATIVE_FLOAT) < 0) ERR;
      if (H5Tinsert(typeid, "percent_transporter_errosion", HOFFSET(struct hr_rec, percent_transporter_errosion), 
		    H5T_NATIVE_DOUBLE) < 0) ERR;
      if (H5Tcommit(grpid, "hr_rec", typeid) < 0) ERR;

      /* Create a space. */
      dims[0] = DIM1_LEN;
      if ((spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;

      /* Create an attribute of this compound type. */
      if ((attid = H5Acreate2(grpid, ATT_NAME1, typeid, spaceid, H5P_DEFAULT, H5P_DEFAULT)) < 0) ERR;

      /* Write some data to the attribute. */
      if (H5Awrite(attid, typeid, hr_data_out) < 0) ERR;
      
      /* Release all resources. */
      if (H5Aclose(attid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Tclose(typeid_inner) < 0 ||
	  H5Sclose(spaceid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

      /* Now open the file and read it. */
      if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;
      if ((attid = H5Aopen_by_name(grpid, ".", ATT_NAME1, H5P_DEFAULT, H5P_DEFAULT)) < 0) ERR;
      if ((typeid = H5Aget_type(attid)) < 0) ERR;
      if ((native_typeid = H5Tget_native_type(typeid, H5T_DIR_DEFAULT)) < 0) ERR;
      if (H5Aread(attid, native_typeid, hr_data_in) < 0) ERR;

      /* Check the data. */
      for (i = 0; i < DIM1_LEN; i++)
	 if (hr_data_out[i].starfleet_id != hr_data_in[i].starfleet_id ||
	     hr_data_out[i].svc_rec.i1 != hr_data_in[i].svc_rec.i1 ||
	     hr_data_out[i].svc_rec.i2 != hr_data_in[i].svc_rec.i2 ||
	     strcmp(hr_data_out[i].name, hr_data_in[i].name) ||
	     hr_data_out[i].max_temp != hr_data_in[i].max_temp ||
	     hr_data_out[i].min_temp != hr_data_in[i].min_temp ||
	     hr_data_out[i].percent_transporter_errosion != hr_data_in[i].percent_transporter_errosion)
	    ERR;

      /* Release all resources. */
      if (H5Aclose(attid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

   }

   SUMMARIZE_ERR;
   printf("*** Checking HDF5 variable of compound type which contains a string...");
   {
#define DATASET_NAME "Enterprise"
      struct hr_rec
      {
	    char name[NC_MAX_NAME + 1];
	    float max_temp;
      };
      struct hr_rec hr_data_out[DIM1_LEN], hr_data_in[DIM1_LEN];

      hid_t fileid, grpid, typeid, spaceid, array1_tid, datasetid, str_tid;
      hsize_t dims[1] = {NC_MAX_NAME + 1};
      int i;

      /* Create some phony data. */   
      for (i = 0; i < DIM1_LEN; i++)
      {
	 if (sprintf(hr_data_out[i].name, "alien_%d", i) < 0) ERR;
	 hr_data_out[i].max_temp = 99.99;
      }

      /* Open file and get root group. */
      if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;

      /* Create a compound type. */
      if ((typeid = H5Tcreate(H5T_COMPOUND, sizeof(struct hr_rec))) < 0) ERR;
/*      printf("sizeof(struct hr_rec)=%d dims[0]=%d\n", sizeof(struct hr_rec), dims[0]);*/
      if ((str_tid = H5Tcopy(H5T_C_S1)) < 0) ERR;
      if (H5Tset_strpad(str_tid, H5T_STR_NULLTERM) < 0) ERR;
      if ((array1_tid = H5Tarray_create2(str_tid, 1, dims)) < 0) ERR;
/*      printf("sizeof(struct hr_rec)=%d HOFFSET(struct hr_rec, name) = %d HOFFSET(struct hr_rec, max_temp) = %d\n", 
	sizeof(struct hr_rec), HOFFSET(struct hr_rec, name), HOFFSET(struct hr_rec, max_temp));*/
      if (H5Tinsert(typeid, "name", HOFFSET(struct hr_rec, name), array1_tid) < 0) ERR;
      if (H5Tinsert(typeid, "max_temp", HOFFSET(struct hr_rec, max_temp), H5T_NATIVE_FLOAT) < 0) ERR;
      if (H5Tcommit(grpid, "hr_rec", typeid) < 0) ERR;

      /* Create a space. */
      dims[0] = DIM1_LEN;
      if ((spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;

      /* Create a dataset of this compound type. */
      if ((datasetid = H5Dcreate(grpid, DATASET_NAME, typeid, spaceid, H5P_DEFAULT)) < 0) ERR;

      /* Write some data. */
      if (H5Dwrite(datasetid, typeid, H5S_ALL, H5S_ALL, H5P_DEFAULT, hr_data_out) < 0) ERR;
      
      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(array1_tid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Tclose(str_tid) < 0 ||
	  H5Sclose(spaceid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

      /* Now open the file and read it. */
      if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;
      if ((datasetid = H5Dopen(grpid, DATASET_NAME)) < 0) ERR;
      if ((typeid = H5Dget_type(datasetid)) < 0) ERR;
      if (H5Tget_class(typeid) != H5T_COMPOUND) ERR;
      if (H5Dread(datasetid, typeid, H5S_ALL, H5S_ALL, H5P_DEFAULT, hr_data_in) < 0) ERR;

      /* Check the data. */
      for (i = 0; i < DIM1_LEN; i++)
	 if (strcmp(hr_data_out[i].name, hr_data_in[i].name) ||
	     hr_data_out[i].max_temp != hr_data_in[i].max_temp) ERR;

      /* Release all resources. */
      if (H5Dclose(datasetid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;
   }

   SUMMARIZE_ERR;
   printf("*** Checking HDF5 compound attribute which contains an array of char...");
   {
#define DIM2_LEN 1
#define ATT_NAME "HR_Records"
      struct hr_rec
      {
	    char name[NC_MAX_NAME + 1];
	    float max_temp;
      };
      struct hr_rec hr_data_out[DIM2_LEN], hr_data_in[DIM2_LEN];

      hid_t fileid, grpid, typeid, spaceid, array1_tid, attid, str_tid;
      hsize_t dims[1] = {NC_MAX_NAME + 1};
      int i;

      /* Create some phony data. */   
      for (i = 0; i < DIM2_LEN; i++)
      {
	 if (sprintf(hr_data_out[i].name, "alien_%d", i) < 0) ERR;
	 hr_data_out[i].max_temp = 99.99;
      }

      /* Open file and get root group. */
      if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;

      /* Create a compound type. */
      if ((typeid = H5Tcreate(H5T_COMPOUND, sizeof(struct hr_rec))) < 0) ERR;
      if ((str_tid = H5Tcopy(H5T_C_S1)) < 0) ERR;
      if (H5Tset_strpad(str_tid, H5T_STR_NULLTERM) < 0) ERR;
      if ((array1_tid = H5Tarray_create2(str_tid, 1, dims)) < 0) ERR;
/*      printf("sizeof(struct hr_rec)=%d HOFFSET(struct hr_rec, name) = %d HOFFSET(struct hr_rec, max_temp) = %d\n", 
	sizeof(struct hr_rec), HOFFSET(struct hr_rec, name), HOFFSET(struct hr_rec, max_temp));*/
      if (H5Tinsert(typeid, "Name", HOFFSET(struct hr_rec, name), array1_tid) < 0) ERR;
      if (H5Tinsert(typeid, "Max_Temp", HOFFSET(struct hr_rec, max_temp), H5T_NATIVE_FLOAT) < 0) ERR;
      if (H5Tcommit(grpid, "SF_HR_Record", typeid) < 0) ERR;

      /* Create a space. */
      dims[0] = DIM2_LEN;
      if ((spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;

      /* Create an attribute of this compound type. */
      if ((attid = H5Acreate2(grpid, ATT_NAME, typeid, spaceid, H5P_DEFAULT, H5P_DEFAULT)) < 0) ERR;

      /* Write some data. */
      if (H5Awrite(attid, typeid, hr_data_out) < 0) ERR;
      
      /* Release all resources. */
      if (H5Aclose(attid) < 0 ||
	  H5Tclose(array1_tid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Tclose(str_tid) < 0 ||
	  H5Sclose(spaceid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;

      /* Now open the file and read it. */
      if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0) ERR;
      if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;
      if ((attid = H5Aopen_by_name(grpid, ".", ATT_NAME, H5P_DEFAULT, H5P_DEFAULT)) < 0) ERR;
      if ((typeid = H5Aget_type(attid)) < 0) ERR;
      if (H5Tget_class(typeid) != H5T_COMPOUND) ERR;
      if (H5Aread(attid, typeid, hr_data_in) < 0) ERR;

      /* Check the data. */
      for (i = 0; i < DIM2_LEN; i++)
	 if (strcmp(hr_data_out[i].name, hr_data_in[i].name) ||
	     hr_data_out[i].max_temp != hr_data_in[i].max_temp) ERR;

      /* Release all resources. */
      if (H5Aclose(attid) < 0 ||
	  H5Tclose(typeid) < 0 ||
	  H5Gclose(grpid) < 0 ||
	  H5Fclose(fileid) < 0) ERR;
   }

   SUMMARIZE_ERR;
   FINAL_RESULTS;
}
