//--- by default some GPU doesn't support doubles
//--- cl_khr_fp64 directive is used to enable work with doubles
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
//+-----------------------------------------------------------+
//| OpenCL kernel for matrix multiplication                   |
//| using global work groups                                  |
//+-----------------------------------------------------------+
//| http://gpgpu-computing4.blogspot.ru/2009/09/              |
//|      /matrix-multiplication-2-opencl.html                 |
//+-----------------------------------------------------------+
__kernel void MatrixMult_GPU1(__global double *matrix_a,
                              __global double *matrix_b,
                              __global double *matrix_c,
                              int rows_a,int cols_a,int cols_b)
  {
   int i=get_global_id(0);
   int j=get_global_id(1);
   double sum=0.0;
   for(int k=0; k<cols_a; k++)
     {
      sum+=matrix_a[cols_a*i+k]*matrix_b[cols_b*k+j];
     }
   matrix_c[cols_b*i+j]=sum;
  }
#define BLOCK_SIZE 10                                          
//+-----------------------------------------------------------+
//| OpenCL kernel for matrix multiplication                   |
//| using local groups with common local memory               |
//+-----------------------------------------------------------+
//| http://gpgpu-computing4.blogspot.ru/2009/10/              |
//|      /matrix-multiplication-3-opencl.html                 |
//+-----------------------------------------------------------+
__kernel void MatrixMult_GPU2(__global double *matrix_a,
                              __global double *matrix_b,
                              __global double *matrix_c,
                              int rows_a,int cols_a,int cols_b)
  {
   int group_i=get_group_id(0);
   int group_j=get_group_id(1);

   int i=get_local_id(0);
   int j=get_local_id(1);

   int offset_b=BLOCK_SIZE*group_i;
   int offset_a_start=cols_a*BLOCK_SIZE*group_j;
   double sum=(float)0.0;

   for(int offset_a=offset_a_start;
       offset_a<offset_a_start+cols_a;
       offset_a+=BLOCK_SIZE,
       offset_b+=BLOCK_SIZE*cols_b)
     {
      __local double submatrix_a[BLOCK_SIZE][BLOCK_SIZE];
      __local double submatrix_b[BLOCK_SIZE][BLOCK_SIZE];

      submatrix_a[i][j]=matrix_a[offset_a+cols_a*i+j];
      submatrix_b[i][j]=matrix_b[offset_b+cols_b*i+j];

      barrier(CLK_LOCAL_MEM_FENCE);

      for(int k=0; k<BLOCK_SIZE; k++)
         sum+=submatrix_a[i][k]*submatrix_b[k][j];

      barrier(CLK_LOCAL_MEM_FENCE);
     }
   int offset_c=BLOCK_SIZE*(cols_b*group_j+group_i);
   matrix_c[offset_c+cols_b*i+j]=sum;
  };
//+------------------------------------------------------------------+
