// 
// Copyright 2010 Intel Corporation
// 
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
// 
//        http://www.apache.org/licenses/LICENSE-2.0
// 
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// 
#include "RCCE.h"
long long RC_global_clock();
#include <stdio.h>
#include <math.h>

/* hardwired predefined constants */
#define NX      256
#define NY      256
#define NXNY    ((NX)*(NY))
#define NXNY1   ((NX)*(NY-1))
#define NXNY2   ((NX)*(NY-2))

/*
#define O1      0
#define O2      NX-1
#define O3      NX
#define O4      NX+1
#define O5      2*(NX)
#define W1      0.25
#define W2      0.25
#define W4      0.25
#define W5      0.25
#define W3      -1.0
#define FABS(x) ((x)>0?(x):(-x))
*/

/*
const int scc2grid_mapping[48] = {0, 6, 1, 7, 2, 8,
				  3, 9, 4, 10, 5, 11,
				  12, 18, 13, 19, 14, 20,
				  15, 21, 16, 22, 17, 23,
				  24, 30, 25, 31, 26, 32,
				  27, 33, 28, 34, 29, 35,
				  36, 42, 37, 43, 38, 44,
				  39, 45, 40, 46, 41, 47};

const int grid2scc_mapping[48] = {0, 2, 4, 6, 8, 10,
				  1, 3, 5, 7, 9, 11,
				  12, 14, 16, 18, 20, 22,
				  13, 15, 17, 19, 21, 23,
				  24, 26, 28, 30, 32, 34,
				  25, 27, 19, 31, 33, 35,
				  36, 38, 40, 42, 44, 46,
				  37, 39, 41, 43, 45, 47};
*/

int RCCE_APP(int argc, char **argv){

  float     ***U, buffer_in[NX], buffer_out[NX];
  int       i, j, iter, itermax=10;
  int       ID, ID_right, ID_left, y_start, y_fin; //, ID_up, ID_down;
  int       NTILES1;
  double    time;
  //char      *result;

  RCCE_init(&argc, &argv);
  
  NTILES1 = RCCE_num_ues();
  if ((NTILES1>1) && (NTILES1%2)) {
    printf("Grid width should be multiple of 2: %d\n", NTILES1);
    exit(1);
  }  

	ID = RCCE_ue();
  printf("My UE is %d\n", ID); 

	ID_right = (ID+1)%RCCE_num_ues(); /* As if all PEs lie horizontally */
  ID_left = (ID-1+RCCE_num_ues())%RCCE_num_ues();

	y_start = ID * (NY / NTILES1);
	y_fin = ((ID+1) * (NY / NTILES1)) - 1;
	/*
	if (NTILES1 <= 12) {
		CPUX = 2;
		CPUY = NTILES1 / CPUX;
	} else {
		printf("Ntiles greater than 12\n");
	}

  // Observation 1: In scc laytou every + 2 for right will be either in or out w.r.t. NTILES1

	if ((ID+2) >= NTILES1) {
		ID_right = -1;	
	} else {
		ID_right = ID+2;
	}

	if ((ID-2) >= 0) {
		ID_left = -1;	
	} else {
		ID_left = ID-2;
	}

	if ((ID%2) == 0) { // Down row -- this applies only up to 16
		ID_up = ID + 1;
		ID_down = -1;		
	} else {
		ID_down = ID - 1;
		ID_up = -1;
	}
	
	printf("Core %d ID_right %d ID_left %d ID_up %d ID_down %d\n", ID, ID_right, ID_left, ID_up, ID_down);
	*/

  if (argc>1) itermax=atoi(*++argv);
  //if (!ID) printf("Core %d Executing %d iterations\n", ID, itermax);

  /* initialize array a on all tiles; this stuffs a into private caches  */
	U = (float ***) malloc(2 * sizeof(float **));
	
	U[0] = (float **) malloc(NX * sizeof(float *));
	U[1] = (float **) malloc(NX * sizeof(float *));

	for (i=0; i<NX; i++) {
		U[0][i] = (float *) malloc(NY * sizeof(float));
		U[1][i] = (float *) malloc(NY * sizeof(float));

		for (j=0; j<NY; j++) {
			U[0][i][j] = 4.0; 
		}
	}

  //if (!ID) printf("Core %d Executing %d iterations\n", ID, itermax);	
  /* main loop */
  RCCE_barrier(&RCCE_COMM_WORLD);
  time = RCCE_wtime();

  for (iter=0; iter<itermax; iter++) {
  
		for (i = 1; i < NX - 1; i ++)
			for (j = y_start; j < y_fin; j ++) {
				U[(iter+1)%2][i][j] = (1/4)*(U[iter%2][i-1][j]+U[iter%2][i][j-1] +U[iter%2][i+1][j]+U[iter%2][i][j+1]);	
			}

		/* start with copying fringe data to neighboring tiles; we need to
			 group semantic send/recv pairs together to avoid deadlock         */
		if (NTILES1 > 1) { 
			/* send to right */
			for (i=0; i<NX; i++) {
				buffer_out[i] = U[(iter+1)%2][i][y_fin];
			}
			if (ID_right!=0) RCCE_send((char*)(&buffer_out[0]), NX*sizeof(float), ID_right);
		
			if (ID != 0) {
				RCCE_recv((char*)(&buffer_in[0]),  NX*sizeof(float), ID_left);
				for (i=0; i<NX; i++) {
					U[(iter+1)%2][i][y_start-1] = buffer_in[i]; 
				}		
			}
			/* send to left */
			for (i=0; i<NX; i++) {
				buffer_out[i] = U[(iter+1)%2][i][y_start];
			}
			if (ID!=0)       RCCE_send((char *)(&buffer_out[0]), NX*sizeof(float), ID_left);

		
			if (ID_right!=0) {
				RCCE_recv((char *)(&buffer_in[0]), NX*sizeof(float), ID_right);
				for (i=0; i<NX; i++) {
					U[(iter+1)%2][i][y_fin+1] = buffer_in[i]; 
				}
			}
		}
  }		

  RCCE_barrier(&RCCE_COMM_WORLD);
  time = RCCE_wtime()-time;

  /* print result strip by strip; avoid output mangling by letting one core print */
	/*
  checksum = 0.0;
  if (ID==0) {
    for (id=0; id<=NTILES1; id++) {
      if (id!=ID) RCCE_recv((char *)a, NXNY*sizeof(float), id);     
      int start = NX; int end = NXNY1;
      if (id==0) start = 0;
      if (id == NTILES1) end = NXNY;
      for (offset=0, i=start; i<end; i++) {
        if (!(i%NX)) {printf("\n");}
        printf("%1.4f ",a[i+offset]); 
        checksum += (a[i+offset]*a[i+offset]);
      }
    }
    checksum = sqrt(checksum/NXNY);
  }
  else RCCE_send((char *)a, NXNY*sizeof(float), 0);
	*/

  if (ID==0) { 
    printf("\nTotal time: %lf\n", time); 
		fflush(NULL);
    //printf("Checksum = %lf", checksum);
		/*
    if (NTILES1==3 && itermax == 10) {
      vchecksum = 0.635851;
      error = FABS(vchecksum - checksum);
      if (error < 1.e-5) result = "SUCCESSFUL";
      else               result = "FAILED";
      printf("  Verification value = %lf, error = %lf; run %s",
             vchecksum, error, result);
    }
    if (NTILES1==15 && itermax == 100) {
      vchecksum = 1.019079;
      error = FABS(vchecksum - checksum);
      if (error < 1.e-5) result = "SUCCESSFUL";
      else               result = "FAILED";
      printf("  Verification value = %lf, error = %lf; run %s",
             vchecksum, error, result);
    }
    if (NTILES1==47 && itermax == 1000) {
      vchecksum = 1.741555;
      error = FABS(vchecksum - checksum);
      if (error < 1.e-5) result = "SUCCESSFUL";
      else               result = "FAILED";
      printf("  Verification value = %lf, error = %lf; run %s",
             vchecksum, error, result);
    }
		*/
  }  

  RCCE_finalize();
  return(0);
}
