/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the files COPYING and Copyright.html.  COPYING can be found at the root   *
 * of the source code distribution tree; Copyright.html can be found at the  *
 * root level of an installed copy of the electronic HDF5 document set and   *
 * is linked from the top-level documents page.  It can also be found at     *
 * http://hdfgroup.org/HDF5/doc/Copyright.html.  If you do not have          *
 * access to either file, you may request a copy from help@hdfgroup.org.     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 *  This example demonstrates the use of NBIT filter for compound datatype
 * 	1. Prepare the datatype for the dataset 
 *	2. Define the creation property list for the dataset 
 *      3. Set the use of NBIT filter in the creation property list
 * 	4. Write to and read data from the dataset, then compare the data
 *	5. Clean up
 *  See description of H5Pset_nbit() in the HDF5 Reference Manual.
 */

#include "hdf5.h"
#include <stdlib.h>
#include <math.h>

#define 	H5FILE_NAME  "nbit_compound.h5"
#define 	DATASET_NAME "nbit_compound"
#define 	NX 	200
#define 	NY 	300
#define 	CH_NX 	10
#define 	CH_NY 	15

int main(void)
{
    typedef struct {     /* Struct with atomic fields */
        int i;
        char c;
        short s;
        float f;
    } atomic;
    hid_t               file, i_tid, c_tid, s_tid, f_tid;
    hid_t               cmpd_tid; /* atomic compound datatype */
    hid_t               mem_cmpd_tid; /* memory atomic compound datatype */
    size_t              precision[3] = {15, 7, 10};
    size_t              offset[3] = {9, 0, 3};
    hid_t               dataset, space, dc;
    const hsize_t       size[2] = {2, 5};
    const hsize_t       chunk_size[2] = {2, 5}; 
    const float         float_val[2][5] = {{(float)188384.00, (float)19.103516, (float)-1.0831790e9, 
					    (float)-84.242188, (float)5.2045898}, 
					   {(float)-49140.000, (float)2350.2500, (float)-3.2110596e-1, 
					    (float)6.4998865e-5, (float)-0.0000000}};
    atomic              orig_data[2][5];
    atomic              new_data[2][5];
    unsigned int        i_mask, s_mask, c_mask; 
    size_t        	i, j;

   
    /* Define datatypes for members of the compound type */
    i_tid = H5Tcopy(H5T_NATIVE_INT);
    c_tid = H5Tcopy(H5T_NATIVE_CHAR);
    s_tid = H5Tcopy(H5T_NATIVE_SHORT);
    f_tid = H5Tcopy(H5T_IEEE_F32BE);

     /* Set precision and offset for the 1st member: int i */
    if(H5Tset_precision(i_tid,precision[0]) < 0) goto error;
    if(H5Tset_offset(i_tid,offset[0]) < 0) goto error;

     /* Set precision and offset for the 2nd member: char c */
    if(H5Tset_precision(c_tid,precision[1]) < 0) goto error;
    if(H5Tset_offset(c_tid,offset[1]) < 0) goto error;

     /* Set precision and offset for the 3rd member: short s */
    if(H5Tset_precision(s_tid,precision[2]) < 0) goto error;
    if(H5Tset_offset(s_tid,offset[2]) < 0) goto error;

     /* Set fields, offset, precision, size and ebias for the 4th member: float f */
    if(H5Tset_fields(f_tid, (size_t)26, (size_t)20, (size_t)6, (size_t)7, (size_t)13) < 0) goto error;
    if(H5Tset_offset(f_tid, (size_t)7) < 0) goto error;   
    if(H5Tset_precision(f_tid, (size_t)20) < 0) goto error;
    if(H5Tset_size(f_tid, (size_t)4) < 0) goto error;
    if(H5Tset_ebias(f_tid, (size_t)31) < 0) goto error;

    /* Create a memory compound datatype before setting the order */
    mem_cmpd_tid = H5Tcreate(H5T_COMPOUND, sizeof(atomic));
    if(H5Tinsert(mem_cmpd_tid, "i", HOFFSET(atomic, i), i_tid) < 0) goto error;
    if(H5Tinsert(mem_cmpd_tid, "c", HOFFSET(atomic, c), c_tid) < 0) goto error;
    if(H5Tinsert(mem_cmpd_tid, "s", HOFFSET(atomic, s), s_tid) < 0) goto error;
    if(H5Tinsert(mem_cmpd_tid, "f", HOFFSET(atomic, f), H5T_NATIVE_FLOAT) < 0) goto error;

    /* Set order of dataset compound member datatype */
    if(H5Tset_order(i_tid, H5T_ORDER_BE) < 0) goto error;
    if(H5Tset_order(c_tid, H5T_ORDER_BE) < 0) goto error;
    if(H5Tset_order(s_tid, H5T_ORDER_BE) < 0) goto error;

    /* Create a dataset compound datatype and insert some atomic types */
    cmpd_tid = H5Tcreate(H5T_COMPOUND, sizeof(atomic));
    if(H5Tinsert(cmpd_tid, "i", HOFFSET(atomic, i), i_tid) < 0) goto error;
    if(H5Tinsert(cmpd_tid, "c", HOFFSET(atomic, c), c_tid) < 0) goto error;
    if(H5Tinsert(cmpd_tid, "s", HOFFSET(atomic, s), s_tid) < 0) goto error;
    if(H5Tinsert(cmpd_tid, "f", HOFFSET(atomic, f), f_tid) < 0) goto error;

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

    /* 
     * Set chunk and nbit filter.
     * Note that application using the NBIT filter must store data with chunked storage.
     */
    if((dc = H5Pcreate(H5P_DATASET_CREATE)) < 0) goto error;
    if(H5Pset_chunk(dc, 2, chunk_size) < 0) goto error;
    if(H5Pset_nbit(dc) < 0) goto error;

    /* Create the HDF5 file */
    if((file = H5Fcreate (H5FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) {
        printf("Error: fail to create file\n");
        goto error;
    }


    /* Create the dataset */
    if((dataset = H5Dcreate2(file, DATASET_NAME, cmpd_tid, space, H5P_DEFAULT, dc, H5P_DEFAULT)) < 0) goto error;

    /* Initialize data, assuming size of long_long >= size of member datatypes */
    for(i= 0;i< (size_t)size[0]; i++)
      for(j = 0; j < (size_t)size[1]; j++) {
        orig_data[i][j].i = (int)(((long_long)random() %
                             (long_long)pow(2.0, (double)(precision[0]-1))) << offset[0]);
        orig_data[i][j].c = (char)(((long_long)random() %
                             (long_long)pow(2.0, (double)(precision[1]-1))) << offset[1]);
        orig_data[i][j].s = (short)(((long_long)random() %
                             (long_long)pow(2.0, (double)(precision[2]-1))) << offset[2]);
        orig_data[i][j].f = float_val[i][j];

        /* some even-numbered integer values are negtive */
        if((i*size[1]+j+1)%2 == 0) {
            orig_data[i][j].i = -orig_data[i][j].i;
            orig_data[i][j].s = -orig_data[i][j].s;
        }
    }


    /* Write the data to the dataset and then close it */
    if(H5Dwrite(dataset, mem_cmpd_tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, orig_data) < 0)
        goto error;

     H5Dclose(dataset);

    /* Reopen the dataset */
    if ((dataset = H5Dopen2(file, DATASET_NAME, H5P_DEFAULT)) < 0) {
        printf("Error: fail to open dataset\n");
        goto error;
    }


    /* Read the data back from the dataset to a buffer */
    if(H5Dread(dataset, mem_cmpd_tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, new_data) < 0)
        goto error;

    /* 
     * Check that the values read are the same as the values written
     * Use mask for checking the significant bits, ignoring the padding bits
     * Exit and return error on the first different value encountered.
     */
    i_mask = ~(~0 << (precision[0] + offset[0])) & (~0 << offset[0]);
    c_mask = ~(~0 << (precision[1] + offset[1])) & (~0 << offset[1]);
    s_mask = ~(~0 << (precision[2] + offset[2])) & (~0 << offset[2]);

    for(i = 0; i < size[0]; i++) {
        for(j = 0; j < size[1]; j++) {
            if((new_data[i][j].i & i_mask) != (orig_data[i][j].i & i_mask) ||
               (new_data[i][j].c & c_mask) != (orig_data[i][j].c & c_mask) ||
               (new_data[i][j].s & s_mask) != (orig_data[i][j].s & s_mask) ||
               (orig_data[i][j].f==orig_data[i][j].f && new_data[i][j].f != orig_data[i][j].f)) {
                printf("Read different values than written.\n");
                goto error;
            }
        }
    }

    /*
     * Cleanup
     */
    if(H5Tclose(i_tid) < 0) goto error;
    if(H5Tclose(c_tid) < 0) goto error;
    if(H5Tclose(s_tid) < 0) goto error;
    if(H5Tclose(f_tid) < 0) goto error;
    if(H5Tclose(cmpd_tid) < 0) goto error;
    if(H5Tclose(mem_cmpd_tid) < 0) goto error;
    if(H5Pclose(dc) < 0) goto error;
    if(H5Sclose(space) < 0) goto error;
    if(H5Dclose(dataset) < 0) goto error;

    return 0;
error:
    return -1;
}
