/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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 integer type.
 * 	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
 * 	3. Write to and read data from the dataset, then compare the data
 *	4. 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_int.h5"
#define 	DATASET_NAME "nbit_int"
#define 	NX 	200
#define 	NY 	300
#define 	CH_NX 	10
#define 	CH_NY 	15

int main(void)
{
    hid_t   	file, dataspace, dataset, datatype, mem_datatype, properties;
    hsize_t 	dims[2], chunk_size[2];
    int     	orig_data[NX][NY];
    int     	new_data[NX][NY];   
    int     	i, j;
    size_t  	precision, offset;
    double  	data_range;
    int     	idata_range;
    unsigned 	int mask;
   
    /* Define dataset datatype, set precision and offset */
    datatype = H5Tcopy(H5T_NATIVE_INT);
    precision = 17; /* precision includes sign bit */
    if(H5Tset_precision(datatype,precision)<0) {
	printf("Error: fail to set precision\n");
	goto error;
    }
    offset = 4;
    if (H5Tset_offset(datatype, offset)<0) {
	printf("Error: fail to set offset\n");
	goto error;
    }

    /* Copy to memory datatype */
    mem_datatype = H5Tcopy(datatype);

    /* Set order of dataset datatype */
    if(H5Tset_order(datatype, H5T_ORDER_BE)<0) {
	printf("Error: fail to set endianness\n");
	goto error; 
    }

   /* 
    * Initialize data buffer with random data within correct range
    * corresponding to the memory datatype's precision and offset. 
    */
    data_range = pow((double)2,(double)(precision-1));
    idata_range = (int)data_range; 
    for (i=0; i < NX; i++) 
	for (j=0; j < NY; j++)
	    orig_data[i][j] = rand() % idata_range << offset;

    /* Create the dataspace */
    dims[0] = NX;
    dims[1] = NY;
    if((dataspace = H5Screate_simple (2, dims, NULL))<0) {
	printf("Error: fail to create data space\n");
	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;
    }

   /*
    * Set the dataset creation property list to specify that
    * the raw data is to be partitioned into 10x15 element chunks.
    * Note that application using the NBIT filter must store data with chunked storage.
    */
    chunk_size[0] = CH_NX;
    chunk_size[1] = CH_NY;
    if ((properties = H5Pcreate (H5P_DATASET_CREATE))<0) {
	printf("Error: fail to create dataset property\n");
	goto error;
    }
    if(H5Pset_chunk(properties, 2, chunk_size)<0) {
	printf("Error: fail to set chunk\n");
	goto error;
    }

    /* Set the NBIT filter in the creation property list of the dataset. */
    if(H5Pset_nbit(properties)<0) {
	printf("Error: fail to set nbit filter\n");
	goto error;
    }

    /* Create a dataset with the defined property list */
    if((dataset = H5Dcreate(file, DATASET_NAME, datatype, dataspace, H5P_DEFAULT, properties, H5P_DEFAULT))<0) {
	printf("Error: fail to create dataset\n");
	goto error;
    }

    /* Write the data to the dataset and then close it */
    if(H5Dwrite(dataset, mem_datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, orig_data) < 0 ) {
	printf("Error: fail to write to dataset\n");
	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 into a buffer.  */
    if (H5Dread(dataset, mem_datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, new_data) < 0) {
	printf("Error: fail to read from dataset\n");
	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.
    */
    mask = ~(~0 << (precision + offset)) & (~0 << offset);
    for(i = 0; i < NX; i++) {
        for(j = 0; j < NY; j++) {
            if((new_data[i][j] & mask) != (orig_data[i][j] & mask)) {
                printf("Error: Read different values than written.\n");
		goto error;
            }
        }
    }

    /* Clean up */
    H5Tclose(datatype);
    H5Tclose(mem_datatype);
    H5Dclose(dataset);
    H5Sclose(dataspace);
    H5Pclose(properties);
    H5Fclose(file);

    return 0;
error:
    return -1;
}
