#define PADDING         (32) 
#define GROUP_DIMX      (32) 
#define LOG_GROUP_DIMX  (5) 
#define GROUP_DIMY      (2) 
#define WIDTH           (256) 
#define HEIGHT          (4096) 
 
__kernel void
matrix_transpose(__global float *output, 
		 __global float *input,  
		 __local float *tile) 
{
  int block_x = get_group_id(0); 
  int block_y = get_group_id(1); 
  
  int local_x = get_local_id(0) & (GROUP_DIMX - 1); 
  int local_y = get_local_id(0) >> LOG_GROUP_DIMX; 
  
  int local_input  = mad24(local_y, GROUP_DIMX + 1, local_x); 
  int local_output = mad24(local_x, GROUP_DIMX + 1, local_y); 
  
  int in_x = mad24(block_x, GROUP_DIMX, local_x); 
  int in_y = mad24(block_y, GROUP_DIMX, local_y);  
  
  int input_index = mad24(in_y, WIDTH, in_x);  
  
  int out_x = mad24(block_y, GROUP_DIMX, local_x); 
  int out_y = mad24(block_x, GROUP_DIMX, local_y);  
  
  int output_index = mad24(out_y, HEIGHT + PADDING, out_x);  
  
  int global_input_stride  = WIDTH * GROUP_DIMY; 
  int global_output_stride = (HEIGHT + PADDING) * GROUP_DIMY; 
  
  int local_input_stride  = GROUP_DIMY * (GROUP_DIMX + 1); 
  int local_output_stride = GROUP_DIMY; 

  tile[local_input] = input[input_index]; 
  local_input += local_input_stride;  
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index]; 
  local_input += local_input_stride; 
  input_index += global_input_stride; 
  
  tile[local_input] = input[input_index];  
  
  barrier(CLK_LOCAL_MEM_FENCE); 
  
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
  
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
  
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
  
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
  
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
  
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
  
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
  
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
  
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
  
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
 
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
 
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
 
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
 
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
 
  output[output_index] = tile[local_output]; 
  local_output += local_output_stride; 
  output_index += global_output_stride; 
 
  output[output_index] = tile[local_output];  
}
