#include <math.h>
#include <stdio.h>

/* NOTE: 
 *  If variables defined in external libraries/functions, it must be
 *  declared as 'threadprivate', as follows.
 */
#ifdef __OMNI_SCASH__
/*
 * Note that "omcc -omniconfig=scash" automatically defines
 * __OMNI_SCASH__ cpp macro.
 */
#pragma omp threadprivate(stderr)
#endif

/* square region */
#define XSIZE 1022
#define YSIZE 1022

#define PI 3.1415927
#define NITER 50

double time1,time2;
double second();

/*
 * Laplace equation with explict method
 */

double u[XSIZE+2][YSIZE+2],uu[XSIZE+2][YSIZE+2];

main()
{
    int x,y;

    /* initalize */
    for(x = 1; x <= XSIZE; x++)
      for(y = 1; y <= YSIZE; y++)
	u[x][y] = sin((double)(x-1)/XSIZE*PI) + cos((double)(y-1)/YSIZE*PI);

    for(x = 0; x < (XSIZE+2); x++){
	u[x][0] = 0.0;
	u[x][YSIZE+1] = 0.0;
	uu[x][0] = 0.0;
	uu[x][YSIZE+1] = 0.0;
    }

    for(y = 0; y < (YSIZE+2); y++){
	u[0][y] = 0.0;
	u[XSIZE+1][y] = 0.0;
	uu[0][y] = 0.0;
	uu[XSIZE+1][y] = 0.0;
    }

    time1 = second();
    lap_main();
    time2 = second();

    fprintf(stderr,"iter=%d, time=%g\n",NITER,time2-time1);
    exit(0);
}

lap_main()
{
    int x,y,k;
    double sum,e;

    sum = 0.0;
#pragma omp parallel private(k,x,y,e) reduction(+:sum)
{
    for(k = 0; k < NITER; k++){
	/* old <- new */
#pragma omp for 
	for(x = 1; x <= XSIZE; x++)
	  for(y = 1; y <= YSIZE; y++)
	    uu[x][y] = u[x][y];
	/* update */
#pragma omp for
	for(x = 1; x <= XSIZE; x++)
	  for(y = 1; y <= YSIZE; y++)
	    u[x][y] = (uu[x-1][y] + uu[x+1][y] + uu[x][y-1] + uu[x][y+1])*0.25;
    }

#pragma omp for
    for(x = 1; x <= XSIZE; x++)
	for(y = 1; y <= YSIZE; y++){
	    e = (uu[x][y]-u[x][y]);
	    sum += e*e;
	}
}
     fprintf(stderr,"sum = %g\n",sum);
}

