#include <stdio.h>
#include <cuda.h>
#define N 10
#define THREADS_PER_BLOCK 512

// device kernel
__global__ void matrixAddKernel(int *d_a, int *d_b, int *d_c, int n){
	int col = threadIdx.x + blockDim.x * blockIdx.x; 
	int row = threadIdx.y + blockDim.y * blockIdx.y;
	int index = row * N + col;
	if(col < N && row < N){
		c[index] = a[index]+b[index];
		}
}

void matrixAdd(int *a, int *b, int *c, int N)
{
int index;
for(int col=0; col<N; col++){
	for(int row=0; row<N; row++){
		index = row * N + col;
		c[index] = a[index] + b[index];
	}
	}
}


int main(int argc, char *argv[]){

// grid and block sizes
dim3 grid(N/THREADS_PER_BLOCK, 1, 1);
dim3 block(THREADS_PER_BLOCK, 1, 1); 

printf("Number of threads: %i (%ix%i)\n", block.x*block.y, block.x, block.y);
printf("Number of blocks: %i (%ix%i)\n", grid.x*grid.y, grid.x, grid.y); 

// dynamically allocate host memory and load arrays with some data
int size = N * N * sizeof(int);
int *a_h = (int*) malloc(size);
int *b_h = (int*) malloc(size);
int *c_h = (int*) malloc(size);
int *d_h = (int*) malloc(size);

for(int i=0; i<N; i++){
	for(int j=0; j<N; j++){
		a_h[i * N + j] = i;
		b_h[i * N + j] = i;
		}
	}

// device copies
int *a_d;
int *b_d;
int *c_d;

// generate data on host
for(int i=0; i<N; i++){
	a_h[i] = i+1;
	b_h[i] = i+1;
	}

// alloc size
cudaMalloc((void**)&a_d, size);
cudaMalloc((void**)&b_d, size);
cudaMalloc((void**)&c_d, size);

cudaMemcpy(a_d, a_h, size, cudaMemcpyHostToDevice);
cudaMemcpy(b_d, b_h, size, cudaMemcpyHostToDevice);
cudaMemcpy(c_d, c_h, size, cudaMemcpyHostToDevice);

cudaEvent_t start;
cudaEvent_t stop;
float elapsedTime; 

cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);

// launch kernel on GPU
matrixAddKernel<<<grid, block>>>(a_d,b_d,c_d,N);

cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);

cudaMemcpy(c_h, c_d, size, cudaMemcpyDeviceToHost);
for(int i=0; i<N; i++){
	printf("%i+%i = %i\n",a_h[i], b_h[i], c_h[i]);
	}

printf("Time to calculate results: %f ms.\n", elapsedTime);

cudaFree(a_h);
cudaFree(b_h);
cudaFree(c_h);
cudaEventDestroy(start);
cudaEventDestroy(stop);
return 0;
}