#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

#define ITERNUM 10000
#define DSIZE_X 10
#define DSIZE_Y 5000


int main(int argc, char *argv[])
{
    int i, j, n;
    double sum = 0;
    double grid[DSIZE_X][DSIZE_Y];

    double start_time, final_time;

    /* starting the clock */
    start_time = omp_get_wtime();

    /*
     * @TODO: Insert OpenMP pragma here,
     * hint: omp for, collapse, schedule, nowait
     */
    for(i = 0; i < DSIZE_X; i++)
        for(j = 0; j < DSIZE_Y; j++)
            grid[i][j] = (i * j) % 10;

    /*
     * Parallelizing the outer loop is not possible.
     * Each iteration depends on the value of previous iteration
     * so we will parallelize one level below
     */
    for(n = 0; n < ITERNUM; n++) {
        #pragma omp parallel private(i, j)
        {
            /* Update red points */

            /*
             * @TODO: Insert OpenMP pragma here,
             * hint: omp for, collapse, schedule, nowait
             */
            for(i = 1; i < DSIZE_X - 1; i += 2)
                for(j = 1; j < DSIZE_Y - 1; j += 2)
                    grid[i][j] = 0.25 * (grid[i-1][j] + grid[i+1][j] +
                                         grid[i][j-1] + grid[i][j+1]);

            /*
             * @TODO: Insert OpenMP pragma here,
             * hint: omp for, collapse, schedule, nowait
             */
            for(i = 2; i < DSIZE_X - 1; i += 2)
                for(j = 2; j < DSIZE_Y - 1; j += 2)
                    grid[i][j] = 0.25 * (grid[i-1][j] + grid[i+1][j] +
                                         grid[i][j-1] + grid[i][j+1]);

            /* Update black points */

            /*
             * @TODO: Insert OpenMP pragma here,
             * hint: omp for, collapse, schedule, nowait
             */
            for(i = 1; i < DSIZE_X - 1; i += 2)
                for(j = 2; j < DSIZE_Y - 1; j += 2)
                    grid[i][j] = 0.25 * (grid[i-1][j] + grid[i+1][j] +
                                         grid[i][j-1] + grid[i][j+1]);

            /*
             * @TODO: Insert OpenMP pragma here,
             * hint: omp for, collapse, schedule, nowait
             */
            for(i = 2; i < DSIZE_X - 1; i += 2)
                for(j = 1; j < DSIZE_Y - 1; j += 2)
                    grid[i][j] = 0.25 * (grid[i-1][j] + grid[i+1][j] +
                                         grid[i][j-1] + grid[i][j+1]);
        }
    }

    /*
     * @TODO: Parallelize this loop, insert OpenMP pragma here,
     * hint: omp parallel for, collapse, schedule, reduction
     */
    for(i = 0; i < DSIZE_X; i++)
        for(j = 0; j < DSIZE_Y; j++)
            sum += grid[i][j];

    /* Calculating total execution time */
    final_time = omp_get_wtime() - start_time;

    printf("sum: %.2f\n", sum);
    printf("Total time: %.6f\n",final_time);
    return 0;
}
