int main(int argc, char **argv) {
	int ret;
	int provided;
	int i;
	struct mdhim_t *md;
	int total = 0;
	struct mdhim_brm_t *brm, *brmp;
	struct mdhim_bgetrm_t *bgrm, *bgrmp;
	struct timeval start_tv, end_tv;
	char     *db_path = "./";
	char     *db_name = "mdhimTstDB-";
	int      dbug = MLOG_DBG; //MLOG_CRIT=1, MLOG_DBG=2
	mdhim_options_t *db_opts; // Local variable for db create options to be passed
	int db_type = LEVELDB; //(data_store.h) 
	long double put_time = 0;
	long double get_time = 0;
	struct index_t *secondary_local_index;
	struct secondary_bulk_info *secondary_info;
	int num_keys[KEYS];
	MPI_Comm comm;

	// Create options for DB initialization
	db_opts = mdhim_options_init();
	mdhim_options_set_db_path(db_opts, db_path);
	mdhim_options_set_db_name(db_opts, db_name);
	mdhim_options_set_db_type(db_opts, db_type);
	mdhim_options_set_key_type(db_opts, MDHIM_LONG_INT_KEY);
	mdhim_options_set_max_recs_per_slice(db_opts, SLICE_SIZE);
        mdhim_options_set_server_factor(db_opts, 4);
	mdhim_options_set_debug_level(db_opts, dbug);

	//Initialize MPI with multiple thread support
	ret = MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided);
	if (ret != MPI_SUCCESS) {
		printf("Error initializing MPI with threads\n");
		exit(1);
	}

	//Quit if MPI didn't initialize with multiple threads
	if (provided != MPI_THREAD_MULTIPLE) {
                printf("Not able to enable MPI_THREAD_MULTIPLE mode\n");
                exit(1);
        }

	gettimeofday(&start_tv, NULL);

	//Initialize MDHIM
	comm = MPI_COMM_WORLD;
	md = mdhimInit(&comm, db_opts);
	if (!md) {
		printf("Error initializing MDHIM\n");
		MPI_Abort(MPI_COMM_WORLD, ret);
		exit(1);
	}	

	key_lens = malloc(sizeof(int) * KEYS);
	value_lens = malloc(sizeof(int) * KEYS);
	keys = malloc(sizeof(uint64_t *) * KEYS);
	values = malloc(sizeof(uint64_t *) * KEYS);
	secondary_key_lens = malloc(sizeof(int *) * KEYS);
	secondary_keys = malloc(sizeof(uint64_t **) * KEYS);
	memset(secondary_keys, 0, sizeof(uint64_t **) * KEYS);

	/* Primary and secondary key entries */
	MPI_Barrier(MPI_COMM_WORLD);	
	total = 0;
	secondary_local_index = create_local_index(md, LEVELDB, 
						   MDHIM_LONG_INT_KEY, NULL);
	for (i = 0; i < KEYS; i++) {
		num_keys[i] = 1;
	}
	while (total != TOTAL_KEYS) {
		//Populate the primary keys and values to insert
		gen_keys_values(md->mdhim_rank, total);
		secondary_info = mdhimCreateSecondaryBulkInfo(secondary_local_index, 
							      (void ***) secondary_keys, 
							      secondary_key_lens, num_keys, 
							      SECONDARY_LOCAL_INFO);
		//record the start time
		start_record(&start_tv);	
		//Insert the primary keys into MDHIM
		brm = mdhimBPut(md, (void **) keys, key_lens,  
				(void **) values, value_lens, KEYS, 
				NULL, secondary_info);
		//Record the end time
		end_record(&end_tv);
		//Add the final time
		add_time(&start_tv, &end_tv, &put_time);
                if (!brm || brm->error) {
                        printf("Rank - %d: Error inserting keys/values into MDHIM\n", md->mdhim_rank);
                } 
		while (brm) {
			if (brm->error < 0) {
				printf("Rank: %d - Error inserting key/values info MDHIM\n", md->mdhim_rank);
			}
	
			brmp = brm->next;
			//Free the message
			mdhim_full_release_msg(brm);
			brm = brmp;
		}
	
		free_key_values();
		mdhimReleaseSecondaryBulkInfo(secondary_info);
		total += KEYS;
	}

	/* End primary and secondary entries */


	MPI_Barrier(MPI_COMM_WORLD);
	/* End secondary key entries */

	//Commit the database
	ret = mdhimCommit(md, md->primary_index);
	if (ret != MDHIM_SUCCESS) {
		printf("Error committing MDHIM database\n");
	} else {
		printf("Committed MDHIM database\n");
	}

	//Get the stats for the secondary index so the client figures out who to query
	ret = mdhimStatFlush(md, secondary_local_index);
	if (ret != MDHIM_SUCCESS) {
		printf("Error getting stats\n");
	} else {
		printf("Got stats\n");
	}

	MPI_Barrier(MPI_COMM_WORLD);
	//Retrieve the primary key's values from the secondary key
	total = 0;
	while (total != TOTAL_KEYS) {
		//Populate the keys and values to retrieve
		gen_keys_values(md->mdhim_rank, total);
		start_record(&start_tv);
		//Get the values back for each key inserted
		for (i = 0; i < KEYS; i++) {
			bgrm = mdhimBGet(md, secondary_local_index, 
					 (void **) secondary_keys[i], secondary_key_lens[i], 
					 1, MDHIM_GET_PRIMARY_EQ);
		}

		end_record(&end_tv);
		add_time(&start_tv, &end_tv, &get_time);
		while (bgrm) {
			/*	if (!bgrm || bgrm->error) {
				printf("Rank: %d - Error retrieving values starting at: %llu", 
				       md->mdhim_rank, (long long unsigned int) *keys[0]);
				       } */
	
			//Validate that the data retrieved is the correct data
			for (i = 0; i < bgrm->num_keys && !bgrm->error; i++) {   				
				if (!bgrm->value_lens[i]) {
					printf("Rank: %d - Got an empty value for key: %llu", 
					       md->mdhim_rank, *(long long unsigned int *)bgrm->keys[i]);
					continue;
				}
			}

			bgrmp = bgrm;
			bgrm = bgrm->next;
			mdhim_full_release_msg(bgrmp);
		}

		free_key_values();
		total += KEYS;
	}

	free(key_lens);
	free(keys);
	free(values);
	free(value_lens);
	free(secondary_key_lens);
	free(secondary_keys);
	MPI_Barrier(MPI_COMM_WORLD);

	//Quit MDHIM
	ret = mdhimClose(md);
	gettimeofday(&end_tv, NULL);
	if (ret != MDHIM_SUCCESS) {
		printf("Error closing MDHIM\n");
	}
	
	MPI_Barrier(MPI_COMM_WORLD);
	MPI_Finalize();
	printf("Took: %Lf seconds to put %d keys\n", 
	       put_time, TOTAL_KEYS * 2);
	printf("Took: %Lf seconds to get %d keys/values\n", 
	       get_time, TOTAL_KEYS * 2);

	return 0;
}
int main(int argc, char **argv) {
	int ret;
	int provided = 0;
	struct mdhim_t *md;
	uint32_t key, key2, **secondary_keys, **secondary_keys2;
	int value, *secondary_key_lens, *secondary_key_lens2;
	struct mdhim_brm_t *brm;
	struct mdhim_bgetrm_t *bgrm;
        mdhim_options_t *db_opts;
	struct index_t *secondary_local_index, *secondary_local_index2;
	struct secondary_info *secondary_info, *secondary_info2;
	MPI_Comm comm;

	ret = MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided);
	if (ret != MPI_SUCCESS) {
		printf("Error initializing MPI with threads\n");
		exit(1);
	}

	if (provided != MPI_THREAD_MULTIPLE) {
                printf("Not able to enable MPI_THREAD_MULTIPLE mode\n");
                exit(1);
        }
        
        db_opts = mdhim_options_init();
        mdhim_options_set_db_path(db_opts, "./");
        mdhim_options_set_db_name(db_opts, "mdhimTstDB");
        mdhim_options_set_db_type(db_opts, LEVELDB);
        mdhim_options_set_key_type(db_opts, MDHIM_INT_KEY); //Key_type = 1 (int)
	mdhim_options_set_debug_level(db_opts, MLOG_CRIT);

	comm = MPI_COMM_WORLD;
	md = mdhimInit(&comm, db_opts);
	if (!md) {
		printf("Error initializing MDHIM\n");
		exit(1);
	}	
	
	//Put the primary keys and values
	key = 100 * (md->mdhim_rank + 1);
	value = 500 * (md->mdhim_rank + 1);
	
	secondary_keys = malloc(sizeof(uint32_t *));		
	secondary_keys[0] = malloc(sizeof(uint32_t));
	*secondary_keys[0] = md->mdhim_rank + 1;
	secondary_key_lens = malloc(sizeof(int));
	secondary_key_lens[0] = sizeof(uint32_t);
	
	secondary_keys2 = malloc(sizeof(uint32_t *));		
	secondary_keys2[0] = malloc(sizeof(uint32_t));
	*secondary_keys2[0] = md->mdhim_rank + 1;
	secondary_key_lens2 = malloc(sizeof(int));
	secondary_key_lens2[0] = sizeof(uint32_t);

	//Create a secondary index on only one range server
	secondary_local_index = create_local_index(md, LEVELDB, 
						   MDHIM_INT_KEY);
	secondary_local_index2 = create_local_index(md, LEVELDB, 
						    MDHIM_INT_KEY);
	secondary_info = mdhimCreateSecondaryInfo(secondary_local_index, 
						  (void **) secondary_keys, 
						  secondary_key_lens, 1, 
						  SECONDARY_LOCAL_INFO);
	secondary_info2 = mdhimCreateSecondaryInfo(secondary_local_index2, 
						   (void **) secondary_keys2, 
						   secondary_key_lens2, 1, 
						   SECONDARY_LOCAL_INFO);
	brm = mdhimPut(md, 
		       &key, sizeof(key), 
		       &value, sizeof(value), 
		       NULL, secondary_info);
	if (!brm || brm->error) {
		printf("Error inserting key/value into MDHIM\n");
	} else {
		printf("Successfully inserted key/value into MDHIM\n");
	}

	//Release the received message
	mdhim_full_release_msg(brm);

	//Insert a new key with the second secondary key
	key2 = 200 * (md->mdhim_rank + 1);
	brm = mdhimPut(md, 
		       &key2, sizeof(key2), 
		       &value, sizeof(value), 
		       NULL, secondary_info2);
	if (!brm || brm->error) {
		printf("Error inserting key/value into MDHIM\n");
	} else {
		printf("Successfully inserted key/value into MDHIM\n");
	}

	//Release the received message
	mdhim_full_release_msg(brm);

	//Commit the database
	ret = mdhimCommit(md, md->primary_index);
	if (ret != MDHIM_SUCCESS) {
		printf("Error committing MDHIM database\n");
	} else {
		printf("Committed MDHIM database\n");
	}

	//Get the stats for the secondary index so the client figures out who to query
	ret = mdhimStatFlush(md, secondary_local_index);
	if (ret != MDHIM_SUCCESS) {
		printf("Error getting stats\n");
	} else {
		printf("Got stats\n");
	}
	//Get the stats for the secondary index so the client figures out who to query
	ret = mdhimStatFlush(md, secondary_local_index2);
	if (ret != MDHIM_SUCCESS) {
		printf("Error getting stats\n");
	} else {
		printf("Got stats\n");
	}

	//Get the primary key values from the secondary local key
	value = 0;
	bgrm = mdhimGet(md, secondary_local_index, 
			secondary_keys[0], 
			secondary_key_lens[0], 
			MDHIM_GET_PRIMARY_EQ);
	if (!bgrm || bgrm->error) {
		printf("Error getting value for key: %d from MDHIM\n", key);
	} else if (bgrm->value_lens[0]) {
		printf("Successfully got value: %d from MDHIM\n", *((int *) bgrm->values[0]));
	}

	mdhim_full_release_msg(bgrm);

	//Get the primary key values from the secondary local key
	value = 0;
	bgrm = mdhimGet(md, secondary_local_index2, secondary_keys2[0], 
			secondary_key_lens2[0], 
			MDHIM_GET_PRIMARY_EQ);
	if (!bgrm || bgrm->error) {
		printf("Error getting value for key: %d from MDHIM\n", key);
	} else if (bgrm->value_lens[0]) {
		printf("Successfully got value: %d from MDHIM\n", *((int *) bgrm->values[0]));
	}

	mdhim_full_release_msg(bgrm);

	ret = mdhimClose(md);
	free(secondary_keys[0]);
	free(secondary_keys);
	free(secondary_key_lens);
	free(secondary_keys2[0]);
	free(secondary_keys2);
	free(secondary_key_lens2);
	mdhim_options_destroy(db_opts);
	mdhimReleaseSecondaryInfo(secondary_info);
	mdhimReleaseSecondaryInfo(secondary_info2);
	if (ret != MDHIM_SUCCESS) {
		printf("Error closing MDHIM\n");
	}

	MPI_Barrier(MPI_COMM_WORLD);
	MPI_Finalize();

	return 0;
}