#include "correlation.fpga.h"
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>


#pragma omp target no_copy_deps device(smp)
#pragma omp task in(v1[0;vecSizeFPGA], v2[0;vecSizeFPGA]) out(co[0;1],delay_co[0;1])
void corr_plain(float * v1, float mean_v1, int n1, float * v2, float mean_v2, int n2, float * co, int *delay_co)
{
   int i;
   float correl = 0.0f;
   int n = MIN(n1,n2);

   float sx = 0;
   float sy = 0;
   float sxy = 0;
   for (i=0;i<n;i++) {
      sx += (v1[i] - mean_v1) * (v1[i] - mean_v1);
      sy += (v2[i] - mean_v2) * (v2[i] - mean_v2);
   }
   float denom = sqrt(sx*sy);

   /* Calculate the correlation series */
   int maxdelay=MAXDELAY;
   int delay_i = -maxdelay-1;;
   float delay_score = -100000.0f;
   int delay;
   int j;
   float r;

   for (delay=-maxdelay;delay<maxdelay;delay++) {
      sxy = 0;
      for (i=0;i<n;i++) {
         j = i + delay;
         if (j < 0 || j >= n)
            continue;
         else
            sxy += (v1[i] - mean_v1) * (v2[j] - mean_v2);
      }
      r = sxy / denom;
      if (r>delay_score)
      {
	      delay_i=delay;
	      delay_score=r;
      }
      
      /* r is the correlation coefficient at delay */
   }

   *co= delay_score;
   *delay_co=delay_i;

}

#pragma omp target device(fpga)
#pragma omp task in(v1[0;vecSizeFPGA], v2[0;vecSizeFPGA]) out(co[0;1],delay_co[0;1])
void corr_fpga(float * v1, float mean_v1, int n1, float * v2, float mean_v2, int n2, float * co, int *delay_co)
{
   int i;
   float correl = 0.0f;
   int n = MIN(n1,n2);
#pragma HLS array_partition variable=v1 cyclic factor=FADD_LAT
#pragma HLS array_partition variable=v2 cyclic factor=FADD_LAT
   float sum_ab[FADD_LAT];
   float sum_a2[FADD_LAT];
   float sum_b2[FADD_LAT];
#pragma HLS ARRAY_PARTITION variable=sum_ab cyclic factor=FADD_LAT dim=1
#pragma HLS ARRAY_PARTITION variable=sum_a2 cyclic factor=FADD_LAT dim=1
#pragma HLS ARRAY_PARTITION variable=sum_b2 cyclic factor=FADD_LAT dim=1

  loop_init: for(i=0;i<FADD_LAT;i++) {
    #pragma HLS UNROLL
        sum_ab[i] = 0.0;
        sum_a2[i] = 0.0;
        sum_b2[i] = 0.0;
    }

   float sx = 0;
   float sy = 0;
   float sxy = 0;
loop_sum_a2_b2:
   //intr 1
   for (i=0; i < n; i+=FADD_LAT) {
      #pragma HLS PIPELINE II=8 rewind
	   for(int j=0; j<FADD_LAT; j++)
	   { 
		     sum_b2[j]+=(v2[j+i]-mean_v2)*(v2[j+i]-mean_v2);
		     sum_a2[j]+=(v1[j+i]-mean_v1)*(v1[j+i]-mean_v1); 
	   }
   }

   loop_sum_f: for (int k=0; k<FADD_LAT; k++)
   {
      #pragma HLS UNROLL
	sx+=sum_a2[k];
	sy+=sum_b2[k];
   }
// /intr 1
   float denom = sqrt(sx*sy);

   /* Calculate the correlation series */
   int maxdelay=MAXDELAY;
   int delay_i = -maxdelay-1;;
   float delay_score = -100000.0f;
   int delay;
   int j;
   float r;

loop_delay_1: 
   for (delay=-maxdelay;delay<maxdelay;delay++) {
          loop_init_delay_1: for(i=0;i<FADD_LAT;i++) {
          #pragma HLS UNROLL
              sum_ab[i] = 0.0;
          }
loop_delay_internal: 
	  for (i=0;i<n-FADD_LAT+1;i+=FADD_LAT) {
          #pragma HLS PIPELINE II=1 rewind
loop_fadd_lat_k:
	    for(int k=0; k<FADD_LAT; k++)
	    { 
                if (k+i+delay < 0 || k+i+delay >= n)
                  continue;
                else
		   sum_ab[k]+=(v1[k+i]-mean_v1)*(v2[k+i+delay]-mean_v2); 
            }
          }
	 sxy=0;
         loop_sum_ab_delay_internal: for (int k=0; k<FADD_LAT; k++)
         {
           #pragma HLS UNROLL
	   sxy+=sum_ab[k];
         }
         r = sxy / denom;
         if (r>delay_score)
         {
	      delay_i=delay;
	      delay_score=r;
         }
      }

   *co= delay_score;
   *delay_co=delay_i;

}



double   cclock( void );

int main(int argc, char * argv[])
{
  float result;
  float res;
  float res2;
  float mean1;
  float mean2;
  int c1, c2;
  int delay;
  int delay2;
  float *v1=(float *)malloc((1<<20)*sizeof(float));
  float *v2=(float *)malloc((1<<20)*sizeof(float));


  result = 0.0f;
  res = 0.0f;
  //Initialize the vectors

  c1 = readvector(argv[1], v1, vecSize, &mean1);
  c2 = readvector(argv[2], v2, vecSize, &mean2);

  double time1;
  double time2;

  time1=cclock();
  corr_fpga(v1, mean1, c1, v2, mean2, c2, &res, &delay);
#pragma omp taskwait
  time1 = cclock() - time1;
  printf ("computation time:%4lf\tresult fpga: %f\tdelay:%d\n", time1, res, delay);



  time2 = cclock();
  corr_plain(v1, mean1, c1, v2, mean2, c2, &res2, &delay2);
#pragma omp taskwait
  time2 = cclock() - time2;
  printf ("computation time:%4lf\tresult one: %f\tdelay:%d\n", time2, res2, delay2);


  return 0;
}



int readvector(char * filename, float * vector, int n_elems, float *mean_vector)
{
   FILE * f = fopen(filename, "r");
   int count = 0;
   int M, d, h, m;
   int base;
   float val;
   char datetime[30];
   char value[10];
   char * loc;
   struct tm tm;
   int index;
   float local_mean_vector=0.0f;

   if (f==NULL) { perror ("fopen"); return 0; }

   fscanf(f, "%s %s %s\n", datetime, &datetime[11], value);
   datetime[10] = ' ' ;

   while (!feof(f) && count < n_elems) {
      int c = fscanf(f, "%s %s %s\n", datetime, &datetime[11], value);
      datetime[10] = ' ' ;

      loc = strptime(datetime, "%Y-%m-%d %H:%M:%S", &tm);
     
      if (count == 0) {
         base = tm.tm_yday*24*60 + tm.tm_hour*60 + tm.tm_min;
      }
      index = tm.tm_yday*24*60 + tm.tm_hour*60 + tm.tm_min - base;
      val = atof(value);

      while ((count < index) && (count < n_elems-1))
         vector[count++] = val;

      if (index != (count)) 
          fprintf(stderr, "index %d count %d\n", index, count);

      vector[count] = val;
      local_mean_vector += val;
      
    
     ++count;
   } 

   *mean_vector = local_mean_vector/count;

   fclose(f);

   return count;
}

