/*******************************************************************************
 *        CILK Histogram                                                 *
 *                                                                             *
 *  AUTHOR: Marco Procaccini - UNISI                             *
 * VERSION: 0.0.1                                                              *
 *    DATE: 09/09/2021 (dd/mm/yy)                                              *
 *                                                                             *
 ******************************************************************************/





#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <pthread.h>
#include <cilk/cilk.h>
#include <cilk/cilk_api.h>
#include <sys/time.h>

/*******************************************************************************
 * Definitions for ROI for COTSon
 ******************************************************************************/

/* COTSON Base for custom instruction definition */
/* IMPORTANT: keep in sync with AbAeterno: abaeterno/lib/cpuid_call.h */
#define _COTSON_CUSTOM_BASE	0x2DAF0000
#define _COTSON_CUSTOM_TRACER   0xFF
#define _XSMOPCODE(_opc)	(_COTSON_CUSTOM_BASE+(_opc))
#define _XSMOPCODE_I(_opc,_im)	(_COTSON_CUSTOM_BASE+((_im)<<8)+(_opc))
#define _PACKED8(_sw,_zo)       ((((_sw)&1)<<7)|((_zo)&0x7F))
#define _AVOL			__asm__ volatile
#define _RCLB  /*example: #define _RCLB :"%rax" // don't forget the colon! */
#define _XP(str)		#str
#define _AS0(_of)		_XP(prefetchnta _of(%%rax,%%rax))
#define _JHERE			";jmp 1f;1:"
#define _XSM_XZONESTART(_i)	_XSMOPCODE_I(_COTSON_CUSTOM_TRACER,_PACKED8(0,_i))
#define _XSM_XZONESTOP(_i)	_XSMOPCODE_I(_COTSON_CUSTOM_TRACER,_PACKED8(1,_i))
#define XSM_XZONESTART(_z)	_AVOL (_AS0(_XSM_XZONESTART(_z)) _JHERE:: _RCLB)
#define XSM_XZONESTOP(_z)	_AVOL (_AS0(_XSM_XZONESTOP(_z))  _JHERE:: _RCLB)

/******************************************************************************/
/* XSM PERFORMANCE COUNTERS FUNCTIONS */
/******************************************************************************/
#define xzonestart(_zone) XSM_XZONESTART(_zone)
#define xzonestop(_zone) XSM_XZONESTOP(_zone)
/******************************************************************************/

/*******************************************************************************
 * These definitions are modified by filter passed in mydse.sh
 * Plese not change the name!
 ******************************************************************************/

//#define DEBUG

#define CSIZE 1000000000
#define BSIZE 4

#define XDATA_INT64

#ifdef XDATA_SINGLE
    #define DATA float
#endif
#ifdef XDATA_DOUBLE
    #define DATA double
#endif
#ifdef XDATA_INT64
    #define DATA uint64_t
#endif


DATA *histogram, *color; 


#define Cilk_lockvar pthread_mutex_t
#define Cilk_lock pthread_mutex_lock
#define Cilk_unlock pthread_mutex_unlock


//int data_count, bin_count, size, *histogram, *color;

Cilk_lockvar *lock;


struct timeval  tv;

using namespace std;


void prepare(){
	color = (DATA*) malloc(CSIZE * sizeof(DATA));
	histogram = (DATA*) malloc(BSIZE * sizeof(DATA));
	lock  = (pthread_mutex_t *) calloc(BSIZE,sizeof(pthread_mutex_t));
	for (int i = 0; i < CSIZE; ++i)
	{
		color[i] = (DATA)i%BSIZE;
	}
#ifdef DEBUG
	for(int i=0; i< CSIZE; i++){
		printf("[INFO] colors[%d]\t%lu\n",i, color[i]);
	}
#endif
}
void histo_cilk(DATA *histogram, DATA *color, uint64_t size)
{
	if (size == 1) {
		Cilk_lock(&lock[*color]);
		histogram[*color]++;
		Cilk_unlock(&lock[*color]);
	}
	else {
		cilk_spawn histo_cilk(histogram, color, size/2);
		cilk_spawn histo_cilk(histogram, color + size/2, size - size/2);
		cilk_sync;
	}
} 
void compute(){

	histo_cilk(histogram, color, CSIZE);
}


void report()
{
    #ifdef _DEBUG
	for(int i=0;i<BSIZE;i++)
	{
		printf("histogram[%d] = %lu\n",i,histogram[i]);
	}
    #endif

    DATA *histo_check = (DATA*) calloc (BSIZE,sizeof(DATA));
    for(int i=0;i<CSIZE;i++)
    {
	histo_check[color[i]/BSIZE]+=1;
    }

    uint64_t histo_sum=0; 
    uint64_t histocheck_sum=0; 

    for (int i=0; i<BSIZE;i++)
    {
        histo_sum+=histogram[i];
        histocheck_sum+=histo_check[i];
    }
#ifdef _DEBUG
    printf("histogram checksum %d histoceck %d\n", histo_sum,histocheck_sum);
#endif

    if(histo_sum !=  histocheck_sum)
     {
         printf("FAILED\n");
     }
    else{
        printf("***SUCCESS****\n");
    }
}


int main(int argc, char* argv[]){
	
        printf("histogram size %d, colors %d\n",BSIZE, CSIZE);
	prepare();
    gettimeofday(&tv, NULL);
    long long time_in_mill = 
             (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000 ; // convert tv_sec & tv_usec to millisecond
    long long start_time = time_in_mill;

    xzonestart(1);
    compute();
    xzonestop(1);
    
    gettimeofday(&tv, NULL);
    time_in_mill = 
             (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000 ; // convert tv_sec & tv_usec to millisecond
    printf("[INFO]: EXECUTION TIME = %lld ms\n",(time_in_mill - start_time));

    report();
 
	return 0;
}
