update for saving, not finished yet

update for saving, not finished yet

#include "../include/common.h"

inline int stringCmp(__global char * buf1, __global char * buf2, int size){
        int i;
        int res = 0;
        for(i=0;i<size;i++){
                if(buf1[i] > buf2[i]){
                        res = 1;
                        break;
                }else if (buf1[i] < buf2[i]){
                        res = -1;
                        break;
                }
                if(buf1[i] == 0 && buf2[i] == 0)
                        break;
        }
        return res;
}

inline int testCon_int(int buf1, __global char* buf2, int size, int type, int rel){
        int res = 1;
	if(rel == EQ){
                res = ( buf1 == *(((int*)buf2)) );
	}else if (rel == GTH){
		res = ( buf1 > *(((int*)buf2)) );
	}else if (rel == LTH){
		res = ( buf1 < *(((int*)buf2)) );
	}else if (rel == GEQ){
		res = ( buf1 >= *(((int*)buf2)) );
	}else if (rel == LEQ){
		res = ( buf1 <= *(((int*)buf2)) );
	}
	return res;
}

inline int testCon_float(float buf1, __global char* buf2, int size, int type, int rel){
        int res = 1;
	if(rel == EQ){
		res = ( buf1 == *(((float*)buf2)) );
	}else if (rel == GTH){
		res = ( buf1 > *(((float*)buf2)) );
	}else if (rel == LTH){
		res = ( buf1 < *(((float*)buf2)) );
	}else if (rel == GEQ){
		res = ( buf1 >= *(((float*)buf2)) );
	}else if (rel == LEQ){
		res = ( buf1 <= *(((float*)buf2)) );
	}
	return res;
}

inline int testCon_string(__global char *buf1, __global char* buf2, int size, int type, int rel){
        int res = 1;

	int tmp = stringCmp(buf1,buf2,size);
	if(rel == EQ){
		res = (tmp == 0);
	}else if (rel == GTH){
		res = (tmp > 0);
	}else if (rel == LTH){
		res = (tmp < 0);
	}else if (rel == GEQ){
		res = (tmp >= 0);
	}else if (rel == LEQ){
		res = (tmp <= 0);
	}

        return res;
}

__kernel void transform_dict_filter_and(__global int * dictFilter, __global char *fact, long tupleNum, int dNum,  __global int * filter, int byteNum){

	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        int numInt = (tupleNum * byteNum +sizeof(int) - 1) / sizeof(int) ;

        for(size_t i=offset; i<numInt; i += stride){
                int tmp = ((int *)fact)[i];
                unsigned long bit = 1;

                for(int j=0; j< sizeof(int)/byteNum; j++){
                        int t = (bit << ((j+1)*byteNum*8)) -1 - ((1<<(j*byteNum*8))-1);
                        int fkey = (tmp & t)>> (j*byteNum*8) ;
                        filter[i* sizeof(int)/byteNum + j] &= dictFilter[fkey];
                }
        }
}

__kernel void transform_dict_filter_or(__global int * dictFilter, __global char *fact, long tupleNum, int dNum,  __global int * filter,int byteNum){

	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        int numInt = (tupleNum * byteNum +sizeof(int) - 1) / sizeof(int) ;

        for(size_t i=offset; i<numInt; i += stride){
                int tmp = ((int *)fact)[i];
                unsigned long bit = 1;

                for(int j=0; j< sizeof(int)/byteNum; j++){
                        int t = (bit << ((j+1)*byteNum*8)) -1 - ((1<<(j*byteNum*8))-1);
                        int fkey = (tmp & t)>> (j*byteNum*8) ;
                        filter[i* sizeof(int)/byteNum + j] |= dictFilter[fkey];
                }
        }
}

__kernel void genScanFilter_dict_or(__global char *col, int colSize, int colType, int dNum, __global struct whereExp *where, __global int *dfilter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        struct dictHeader *dheader = (struct dictHeader *) col;

        for(size_t i=tid;i<dNum;i+=stride){
                int fkey = dheader->hash[i];
                con = testCon_int(fkey,where->content,colSize,colType,where->relation);
                dfilter[i] |= con;
        }
}

__kernel void genScanFilter_dict_and(__global char *col, int colSize, int colType, int dNum, __global struct whereExp *where, __global int *dfilter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        struct dictHeader *dheader = (struct dictHeader *) col;

        for(size_t i=tid;i<dNum;i+=stride){
                int fkey = dheader->hash[i];
                con = testCon_int(fkey,where->content,colSize,colType,where->relation);
                dfilter[i] &= con;
        }
}

__kernel void genScanFilter_rle(__global char *col, int colSize, int colType, long tupleNum, long tupleOffset, __global struct whereExp *where, int andOr, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        struct rleHeader *rheader = (struct rleHeader *) col;
        int dNum = rheader->dictNum;

        for(size_t i = tid; i<dNum; i += stride){
                int fkey = ((int *)(col+sizeof(struct rleHeader)))[i];
                int fcount = ((int *)(col+sizeof(struct rleHeader)))[i + dNum];
                int fpos = ((int *)(col+sizeof(struct rleHeader)))[i + 2*dNum];

                if((fcount + fpos) < tupleOffset)
                        continue;

                if(fpos >= (tupleOffset + tupleNum))
                        break;

                con = testCon_int(fkey,where->content,colSize,colType,where->relation);

                if(fpos < tupleOffset){
                        int tcount = fcount + fpos - tupleOffset;
                        if(tcount > tupleNum)
                                tcount = tupleNum;
                        for(int k=0;k<tcount;k++){
                                if(andOr == AND)
                                        filter[k] &= con;
                                else
                                        filter[k] |= con;
                        }

                }else if((fpos + fcount) > (tupleOffset + tupleNum)){
                        int tcount = tupleOffset + tupleNum - fpos ;
                        for(int k=0;k<tcount;k++){
                                if(andOr == AND)
                                        filter[fpos+k-tupleOffset] &= con;
                                else
                                        filter[fpos+k-tupleOffset] |= con;
                        }
                }else{
                        for(int k=0;k<fcount;k++){
                                if(andOr == AND)
                                        filter[fpos+k-tupleOffset] &= con;
                                else
                                        filter[fpos+k-tupleOffset] |= con;
                        }

                }
        }
}

__kernel void genScanFilter_and(__global char *col, int colSize, int  colType, long tupleNum, __global struct whereExp * where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = testCon_string(col+colSize*i,where->content,colSize,colType,where->relation);
                filter[i] &= con;
        }
}

__kernel void genScanFilter_and_int_eq(__global char *col, long tupleNum, int where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((int*)col)[i] == where;
                filter[i] &= con;
        }
}

__kernel void genScanFilter_and_float_eq(__global char *col, long tupleNum, float where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((float*)col)[i] == where;
                filter[i] &= con;
        }
}

__kernel void genScanFilter_and_int_geq(__global char *col, long tupleNum, int where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((int*)col)[i] >= where;
                filter[i] &= con;
        }
}

__kernel void genScanFilter_and_float_geq(__global char *col, long tupleNum, float where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((float*)col)[i] >= where;
                filter[i] &= con;
        }
}

__kernel void genScanFilter_and_int_leq(__global char *col, long tupleNum, int where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((int*)col)[i] <= where;
                filter[i] &= con;
        }
}

__kernel void genScanFilter_and_float_leq(__global char *col, long tupleNum, float where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((float*)col)[i] <= where;
                filter[i] &= con;
        }
}

__kernel void genScanFilter_and_int_gth(__global char *col, long tupleNum, int where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((int*)col)[i] > where;
                filter[i] &= con;
        }
}

__kernel void genScanFilter_and_float_gth(__global char *col, long tupleNum, float where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((float*)col)[i] > where;
                filter[i] &= con;
        }
}

__kernel void genScanFilter_and_int_lth(__global char *col, long tupleNum, int where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((int*)col)[i] < where;
                filter[i] &= con;
        }
}

__kernel void genScanFilter_and_float_lth(__global char *col, long tupleNum, float where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((float*)col)[i] < where;
                filter[i] &= con;
        }
}

__kernel void genScanFilter_or(__global char *col, int colSize, int  colType, long tupleNum, __global struct whereExp * where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;
        int rel = where->relation;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = testCon_string(col+colSize*i,where->content,colSize,colType, rel);
                filter[i] |= con;
        }
}

__kernel void genScanFilter_or_int_eq(__global char *col, long tupleNum, int where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((int*)col)[i] == where;
                filter[i] |= con;
        }
}
__kernel void genScanFilter_or_float_eq(__global char *col, long tupleNum, float where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((float*)col)[i] == where;
                filter[i] |= con;
        }
}
__kernel void genScanFilter_or_int_gth(__global char *col, long tupleNum, int where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((int*)col)[i] > where;
                filter[i] |= con;
        }
}

__kernel void genScanFilter_or_float_gth(__global char *col, long tupleNum, float where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((float*)col)[i] > where;
                filter[i] |= con;
        }
}

__kernel void genScanFilter_or_int_lth(__global char *col, long tupleNum, int where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((int*)col)[i] < where;
                filter[i] |= con;
        }
}
__kernel void genScanFilter_or_float_lth(__global char *col, long tupleNum, float where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((float*)col)[i] < where;
                filter[i] |= con;
        }
}
__kernel void genScanFilter_or_int_geq(__global char *col, long tupleNum, int where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((int*)col)[i] >= where;
                filter[i] |= con;
        }
}
__kernel void genScanFilter_or_float_geq(__global char *col, long tupleNum, float where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((float*)col)[i] >= where;
                filter[i] |= con;
        }
}

__kernel void genScanFilter_or_int_leq(__global char *col, long tupleNum, int where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((int*)col)[i] <= where;
                filter[i] |= con;
        }
}
__kernel void genScanFilter_or_float_leq(__global char *col, long tupleNum, float where, __global int * filter){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int con;

        for(size_t i = tid; i<tupleNum;i+=stride){
                con = ((float*)col)[i] <= where;
                filter[i] |= con;
        }
}

__kernel void countScanNum(__global int *filter, long tupleNum, __global int * count){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int localCount = 0;

        for(size_t i = tid; i<tupleNum; i += stride){
                localCount += filter[i];
        }

        count[tid] = localCount;

}

__kernel void scan_dict_other(__global char *col, __global char * dict, int byteNum,int colSize, long tupleNum, __global int *psum, long resultNum, __global int * filter, __global char * result){

	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        struct dictHeader *dheader = (struct dictHeader*)dict;
        int pos = psum[tid] * colSize;

        for(size_t i = tid; i<tupleNum; i+= stride){
                if(filter[i] == 1){
                        int key = 0;
			char * buf = (char *)&key;

			for(int k=0;k<dheader->bitNum/8;k++)
				buf[k] = (col + sizeof(struct dictHeader) + i* dheader->bitNum/8)[k];

			buf = (char *) &dheader->hash[key];
			for(int k=0;k<colSize;k++)
				(result+pos)[k] = buf[k];
                        pos += colSize;
                }
        }
}

__kernel void scan_dict_int(__global char *col, __global char * dict,int byteNum,int colSize, long tupleNum, __global int *psum, long resultNum, __global int * filter, __global char * result){

	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int localCount = psum[tid];
        struct dictHeader *dheader = (struct dictHeader*)dict;

        for(size_t i = tid; i<tupleNum; i+= stride){
                if(filter[i] == 1){
                        int key = 0;
			char * buf = (char *)&key;
			for(int k=0;k<byteNum;k++)
				buf[k] = (col+i*byteNum)[k];
                        ((int *)result)[localCount] = dheader->hash[key];
                        localCount ++;
                }
        }
}

__kernel void scan_other(__global char *col, int colSize, long tupleNum, __global int *psum, long resultNum, __global int * filter, __global char * result){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int pos = psum[tid]  * colSize;

        for(size_t i = tid; i<tupleNum;i+=stride){

                if(filter[i] == 1){
			for(int k=0;k<colSize;k++)
				(result+pos)[k] = (col+i*colSize)[k];
                        pos += colSize;
                }
        }
}

__kernel void scan_int(__global char *col, int colSize, long tupleNum, __global int *psum, long resultNum, __global int * filter, __global char * result){
	size_t stride = get_global_size(0);
	size_t tid = get_global_id(0);
        int localCount = psum[tid] ;

        for(size_t i = tid; i<tupleNum;i+=stride){

                if(filter[i] == 1){
                        ((int*)result)[localCount] = ((int*)col)[i];
                        localCount ++;
                }
        }
}

__kernel void unpack_rle(__global char * fact, __global char * rle, long tupleNum, long tupleOffset, int dNum){

	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        for(size_t i=offset; i<dNum; i+=stride){

                int fvalue = ((int *)(fact+sizeof(struct rleHeader)))[i];
                int fcount = ((int *)(fact+sizeof(struct rleHeader)))[i + dNum];
                int fpos = ((int *)(fact+sizeof(struct rleHeader)))[i + 2*dNum];

                if((fcount + fpos) < tupleOffset)
                        continue;

                if(fpos >= (tupleOffset + tupleNum))
                        break;

                if(fpos < tupleOffset){
                        int tcount = fcount + fpos - tupleOffset;
                        if(tcount > tupleNum)
                                tcount = tupleNum;
                        for(int k=0;k<tcount;k++){
                                ((int*)rle)[k] = fvalue;
                        }

                }else if ((fpos + fcount) > (tupleOffset + tupleNum)){
                        int tcount = tupleNum  + tupleOffset - fpos;
                        for(int k=0;k<tcount;k++){
                                ((int*)rle)[fpos-tupleOffset + k] = fvalue;
                        }

                }else{
                        for(int k=0;k<fcount;k++){
                                ((int*)rle)[fpos-tupleOffset + k] = fvalue;
                        }
                }
        }
}

//The following kernels are for traditional hash joins

__kernel void count_hash_num(__global char *dim, long  inNum, __global int *num){
	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        for(size_t i=offset;i<inNum;i+=stride){
                int joinKey = ((int *)dim)[i];
                int hKey = joinKey & (HSIZE-1);
                atomic_add(&(num[hKey]),1);
        }
}

__kernel void build_hash_table(__global char *dim, long inNum, __global int *psum, __global char * bucket){

	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        for(size_t i=offset;i<inNum;i+=stride){
                int joinKey = ((int *) dim)[i];
                int hKey = joinKey & (HSIZE-1);
                int pos = atomic_add(&psum[hKey],1) * 2;
                ((int*)bucket)[pos] = joinKey;
                pos += 1;
                int dimId = i+1;
                ((int*)bucket)[pos] = dimId;
        }

}

__kernel void count_join_result_dict(__global int *num, __global int* psum, __global char* bucket, __global char* fact, int dNum, __global int* dictFilter){

	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        struct dictHeader *dheader;
        dheader = (struct dictHeader *) fact;

        for(size_t i=offset;i<dNum;i+=stride){
                int fkey = dheader->hash[i];
                int hkey = fkey &(HSIZE-1);
                int keyNum = num[hkey];

                for(int j=0;j<keyNum;j++){
                        int pSum = psum[hkey];
                        int dimKey = ((int *)(bucket))[2*j + 2*pSum];
                        int dimId = ((int *)(bucket))[2*j + 2*pSum + 1];
                        if( dimKey == fkey){
                                dictFilter[i] = dimId;
                                break;
                        }
                }
        }

}

__kernel void transform_dict_filter(__global int * dictFilter, __global char *fact, long tupleNum, int dNum,  __global int * filter){

	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        struct dictHeader *dheader;
        dheader = (struct dictHeader *) fact;

        int byteNum = dheader->bitNum/8;
        int numInt = (tupleNum * byteNum +sizeof(int) - 1) / sizeof(int)  ;

        for(size_t i=offset; i<numInt; i += stride){
                int tmp = ((int *)(fact + sizeof(struct dictHeader)))[i];

                for(int j=0; j< sizeof(int)/byteNum; j++){
                        int fkey = 0;
			char *buf = (char *)&fkey;
			for(int k=0;k<byteNum;k++)
				buf[k] = (((char *)&tmp) + j*byteNum)[k];

                        filter[i* sizeof(int)/byteNum + j] = dictFilter[fkey];
                }
        }
}

__kernel void filter_count(long tupleNum, __global int * count, __global int * factFilter){

        int lcount = 0;
	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        for(size_t i=offset; i<tupleNum; i+=stride){
                if(factFilter[i] !=0)
                        lcount ++;
        }
        count[offset] = lcount;
}

__kernel void count_join_result_rle(__global int* num, __global int* psum, __global char* bucket, __global char* fact, long tupleNum, long tupleOffset,  __global int * factFilter){

	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        struct rleHeader *rheader = (struct rleHeader *)fact;
        int dNum = rheader->dictNum;

        for(size_t i=offset; i<dNum; i += stride){
                int fkey = ((int *)(fact+sizeof(struct rleHeader)))[i];
                int fcount = ((int *)(fact+sizeof(struct rleHeader)))[i + dNum];
                int fpos = ((int *)(fact+sizeof(struct rleHeader)))[i + 2*dNum];

                if((fcount + fpos) < tupleOffset)
                        continue;

                if(fpos >= (tupleOffset + tupleNum))
                        break;

                int hkey = fkey &(HSIZE-1);
                int keyNum = num[hkey];

                for(int j=0;j<keyNum;j++){

                        int pSum = psum[hkey];
                        int dimKey = ((int *)(bucket))[2*j + 2*pSum];
                        int dimId = ((int *)(bucket))[2*j + 2*pSum + 1];

                        if( dimKey == fkey){

                                if(fpos < tupleOffset){
                                        int tcount = fcount + fpos - tupleOffset;
                                        if(tcount > tupleNum)
                                                tcount = tupleNum;
                                        for(int k=0;k<tcount;k++)
                                                factFilter[k] = dimId;

                                }else if((fpos + fcount) > (tupleOffset + tupleNum)){
                                        int tcount = tupleOffset + tupleNum - fpos ;
                                        for(int k=0;k<tcount;k++)
                                                factFilter[fpos+k-tupleOffset] = dimId;
                                }else{
                                        for(int k=0;k<fcount;k++)
                                                factFilter[fpos+k-tupleOffset] = dimId;

                                }

                                break;
                        }
                }
        }

}

__kernel  void count_join_result(__global int* num, __global int* psum, __global char* bucket, __global char* fact, long inNum, __global int* count, __global int * factFilter){
        int lcount = 0;
	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        for(size_t i=offset;i<inNum;i+=stride){
                int fkey = ((int *)(fact))[i];
                int hkey = fkey &(HSIZE-1);
                int keyNum = num[hkey];

                for(int j=0;j<keyNum;j++){
                        int pSum = psum[hkey];
                        int dimKey = ((int *)(bucket))[2*j + 2*pSum];
                        int dimId = ((int *)(bucket))[2*j + 2*pSum + 1];
                        if( dimKey == fkey){
                                lcount ++;
                                factFilter[i] = dimId;
                                break;
                        }
                }
        }

        count[offset] = lcount;
}

__kernel void rle_psum(__global int *count, __global char * fact,  long  tupleNum, long tupleOffset, __global int * filter){

	size_t offset = get_global_id(0);
	size_t stride = get_global_size(0);

        struct rleHeader *rheader = (struct rleHeader *) fact;
        int dNum = rheader->dictNum;

        for(size_t i= offset; i<dNum; i+= stride){

                int fcount = ((int *)(fact+sizeof(struct rleHeader)))[i + dNum];
                int fpos = ((int *)(fact+sizeof(struct rleHeader)))[i + 2*dNum];
                int lcount= 0;

                if((fcount + fpos) < tupleOffset)
                        continue;

                if(fpos >= (tupleOffset + tupleNum))
                        break;

                if(fpos < tupleOffset){
                        int tcount = fcount + fpos - tupleOffset;
                        if(tcount > tupleNum)
                                tcount = tupleNum;
                        for(int k=0;k<tcount;k++){
                                if(filter[k]!=0)
                                        lcount++;
                        }
                        count[i] = lcount;

                }else if ((fpos + fcount) > (tupleOffset + tupleNum)){
                        int tcount = tupleNum  + tupleOffset - fpos;
                        for(int k=0;k<tcount;k++){
                                if(filter[fpos-tupleOffset + k]!=0)
                                        lcount++;
                        }
                        count[i] = lcount;

                }else{
                        for(int k=0;k<fcount;k++){
                                if(filter[fpos-tupleOffset + k]!=0)
                                        lcount++;
                        }
                        count[i] = lcount;
                }
        }

}

__kernel void joinFact_rle(__global int *resPsum, __global char * fact,  int attrSize, long  tupleNum, long tupleOffset, __global int * filter, __global char * result){

	size_t startIndex = get_global_id(0);
	size_t stride = get_global_size(0);

        struct rleHeader *rheader = (struct rleHeader *) fact;
        int dNum = rheader->dictNum;

        for(size_t i = startIndex; i<dNum; i += stride){
                int fkey = ((int *)(fact+sizeof(struct rleHeader)))[i];
                int fcount = ((int *)(fact+sizeof(struct rleHeader)))[i + dNum];
                int fpos = ((int *)(fact+sizeof(struct rleHeader)))[i + 2*dNum];

                if((fcount + fpos) < tupleOffset)
                        continue;

                if(fpos >= (tupleOffset + tupleNum))
                        break;

                if(fpos < tupleOffset){
                        int tcount = fcount + fpos - tupleOffset;
                        int toffset = resPsum[i];
                        for(int j=0;j<tcount;j++){
                                if(filter[j] != 0){
                                        ((int*)result)[toffset] = fkey ;
                                        toffset ++;
                                }
                        }

                }else if ((fpos + fcount) > (tupleOffset + tupleNum)){
                        int tcount = tupleOffset + tupleNum - fpos;
                        int toffset = resPsum[i];
                        for(int j=0;j<tcount;j++){
                                if(filter[fpos-tupleOffset+j] !=0){
                                        ((int*)result)[toffset] = fkey ;
                                        toffset ++;
                                }
                        }

                }else{
                        int toffset = resPsum[i];
                        for(int j=0;j<fcount;j++){
                                if(filter[fpos-tupleOffset+j] !=0){
                                        ((int*)result)[toffset] = fkey ;
                                        toffset ++;
                                }
                        }
                }
        }

}

__kernel void joinFact_dict_other(__global int *resPsum, __global char * fact,  __global char *dict, int byteNum,int attrSize, long  num, __global int * filter, __global char * result){

	size_t startIndex = get_global_id(0);
	size_t stride = get_global_size(0);
        long localOffset = resPsum[startIndex] * attrSize;

        struct dictHeader *dheader = (struct dictHeader*)dict;

        for(size_t i=startIndex;i<num;i+=stride){
                if(filter[i] != 0){
                        int key = 0;
			char *buf = (char *) &key;
			for(int k=0;k<byteNum;k++)
				buf[k] = (fact + i*byteNum)[k];
			buf = (char *)&dheader->hash[key];
			for(int k=0;k<attrSize;k++)
				(result + localOffset)[k] = buf[k];
                        localOffset += attrSize;
                }
        }
}

__kernel void joinFact_dict_int(__global int *resPsum, __global char * fact, __global char *dict, int byteNum, int attrSize, long  num, __global int * filter, __global char * result){

	size_t startIndex = get_global_id(0);
	size_t stride = get_global_size(0);
        long localCount = resPsum[startIndex];

        struct dictHeader *dheader = (struct dictHeader*)dict;

        for(size_t i=startIndex;i<num;i+=stride){
                if(filter[i] != 0){
                        int key = 0;
			char *buf = (char *)&key;
			for(int k=0;k<byteNum;k++)
				buf[k] = (fact + i *byteNum)[k];
                        ((int*)result)[localCount] = dheader->hash[key];
                        localCount ++;
                }
        }
}

__kernel void joinFact_other(__global int *resPsum, __global char * fact,  int attrSize, long  num, __global int * filter, __global char * result){

	size_t startIndex = get_global_id(0);
	size_t stride = get_global_size(0);
        long localOffset = resPsum[startIndex] * attrSize;

        for(size_t i=startIndex;i<num;i+=stride){
                if(filter[i] != 0){
			for(int k=0;k<attrSize;k++)
				(result+localOffset)[k] = (fact + i*attrSize)[k];
                        localOffset += attrSize;
                }
        }
}

__kernel void joinFact_int(__global int *resPsum, __global char * fact,  int attrSize, long  num, __global int * filter, __global char * result){

	size_t startIndex = get_global_id(0);
	size_t stride = get_global_size(0);
        long localCount = resPsum[startIndex];

        for(size_t i=startIndex;i<num;i+=stride){
                if(filter[i] != 0){
                        ((int*)result)[localCount] = ((int *)fact)[i];
                        localCount ++;
                }
        }
}

__kernel void joinDim_rle(__global int *resPsum, __global char * dim, int attrSize, long tupleNum, long tupleOffset, __global int * filter, __global char * result){

	size_t startIndex = get_global_id(0);
	size_t stride = get_global_size(0);
        long localCount = resPsum[startIndex];

        struct rleHeader *rheader = (struct rleHeader *) dim;
        int dNum = rheader->dictNum;

        for(size_t i = startIndex; i<tupleNum; i += stride){
                int dimId = filter[i];
                if(dimId != 0){
                        for(int j=0;j<dNum;j++){
                                int dkey = ((int *)(dim+sizeof(struct rleHeader)))[j];
                                int dcount = ((int *)(dim+sizeof(struct rleHeader)))[j + dNum];
                                int dpos = ((int *)(dim+sizeof(struct rleHeader)))[j + 2*dNum];

                                if(dpos == dimId || ((dpos < dimId) && (dpos + dcount) > dimId)){
                                        ((int*)result)[localCount] = dkey ;
                                        localCount ++;
                                        break;
                                }

                        }
                }
        }
}

__kernel void joinDim_dict_other(__global int *resPsum, __global char * dim, __global char *dict, int byteNum, int attrSize, long num, __global int * filter, __global char * result){

	size_t startIndex = get_global_id(0);
	size_t stride = get_global_size(0);
        long localOffset = resPsum[startIndex] * attrSize;

        struct dictHeader *dheader = (struct dictHeader*)dict;

        for(size_t i=startIndex;i<num;i+=stride){
                int dimId = filter[i];
                if( dimId != 0){
                        int key = 0;
			char *buf = (char *)&key;
			for(int k=0;k<byteNum;k++)
				buf[k] = (dim + (dimId-1) * byteNum)[k];
			buf = (char *)&dheader->hash[key];
			for(int k=0;k<attrSize;k++)
				(result+localOffset)[k] = buf[k];
                        localOffset += attrSize;
                }
        }
}

__kernel void joinDim_dict_int(__global int *resPsum, __global char * dim, __global char *dict, int byteNum, int attrSize, long num, __global int * filter, __global char * result){

	size_t startIndex = get_global_id(0);
	size_t stride = get_global_size(0);
        long localCount = resPsum[startIndex];

        struct dictHeader *dheader = (struct dictHeader*)dict;

        for(size_t i=startIndex;i<num;i+=stride){
                int dimId = filter[i];
                if( dimId != 0){
                        int key = 0;
			char * buf = (char *)&key;
			for(int k=0;k<byteNum;k++)
				buf[k] = (dim + (dimId-1)*byteNum)[k];
                        ((int*)result)[localCount] = dheader->hash[key];
                        localCount ++;
                }
        }
}

__kernel void joinDim_int(__global int *resPsum, __global char * dim, int attrSize, long num, __global int * filter, __global char * result){

	size_t startIndex = get_global_id(0);
	size_t stride = get_global_size(0);
        long localCount = resPsum[startIndex];

        for(size_t i=startIndex;i<num;i+=stride){
                int dimId = filter[i];
                if( dimId != 0){
                        ((int*)result)[localCount] = ((int*)dim)[dimId-1];
                        localCount ++;
                }
        }
}

__kernel void joinDim_other(__global int *resPsum, __global char * dim, int attrSize, long num, __global int * filter, __global char * result){

	size_t startIndex = get_global_id(0);
	size_t stride = get_global_size(0);
        long localOffset = resPsum[startIndex] * attrSize;

        for(size_t i=startIndex;i<num;i+=stride){
                int dimId = filter[i];
                if( dimId != 0){
			for(int k=0;k<attrSize;k++)
				(result+localOffset)[k] = (dim + (dimId-1)*attrSize)[k];
                        localOffset += attrSize;
                }
        }
}

// for groupBy

char * gpuItoa(int value, char* result, int base){

        if (base < 2 || base > 36) {
                *result = '\0';
                return result;
        }

        char* ptr = result, *ptr1 = result, tmp_char;
        int tmp_value;

        do {
                tmp_value = value;
                value /= base;
                *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
        } while ( value );

        if (tmp_value < 0)
                *ptr++ = '-';

        *ptr-- = '\0';

        while(ptr1 < ptr) {
                tmp_char = *ptr;
                *ptr--= *ptr1;
                *ptr1++ = tmp_char;
        }
        return result;

}

char * gpuStrcpy(char * dst, const char * src){

        char * orig = dst;
        while(*src)
                *dst++ = *src++;
        *dst = '\0';

        return orig;
}

char* gpuStrncat(char *dest, const char *src, size_t n)
{
        int dest_len = 0;
        int i;

        char * tmp = dest;
        while(*tmp != '\0'){
                tmp++;
                dest_len ++;
        }

        for (i = 0 ; i < n && src[i] != '\0' ; i++)
                dest[dest_len + i] = src[i];
        dest[dest_len + i] = '\0';
        return dest;
}

char * gpuStrcat(char * dest, const char * src){
        char *tmp =dest;
        int dest_len = 0;
        int i;

        while (*tmp!= '\0'){
                tmp++ ;
                dest_len ++;
        }

        for(i=0; src[i] !='\0'; i++){
                dest[dest_len + i] = src[i];
        }

        dest[dest_len + i] = '\0';

        return dest;
}

unsigned int StringHash(const char* s)
{
    unsigned int hash = 0;
    int c;

    while((c = *s++))
    {
        hash = ((hash << 5) + hash) ^ c;
    }

    return hash;
}

__kernel void build_groupby_key(__global char * content, __global long * colOffset, int gbColNum, __global int * gbIndex, __global int * gbType, __global int * gbSize, long tupleNum, __global int * key, __global int *num){

	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);

        for(size_t i = offset; i< tupleNum; i+= stride){
                char buf[128] = {0};
                for (int j=0; j< gbColNum; j++){
                        char tbuf[32]={0};
                        int index = gbIndex[j];
			long offset = colOffset[index];

                        if (index == -1){
                                gpuItoa(1,tbuf,10);
                                gpuStrncat(buf,tbuf,1);

                        }else if (gbType[j] == STRING){
				for(int k=0;k<gbSize[j];k++)
					tbuf[k] = (content+offset+i*gbSize[j])[k];
                                gpuStrncat(buf, tbuf, gbSize[j]);

                        }else if (gbType[j] == INT){
                                int key = ((int *)(content+offset))[i];
                                gpuItoa(key,tbuf,10);
                                gpuStrcat(buf,tbuf);
                        }
                }
                int hkey = StringHash(buf) % HSIZE;
                key[i]= hkey;
                num[hkey] = 1;
        }
}

__kernel void count_group_num(__global int *num, int tupleNum, __global int *totalCount){
	size_t stride = get_global_size(0);
	size_t offset = get_global_id(0);
        int localCount = 0;

        for(size_t i=offset; i<tupleNum; i+= stride){
                if(num[i] == 1){
                        localCount ++;
                }
        }

        atomic_add(totalCount,localCount);
}

float getExp(__global char *content, __global int * colOffset,struct mathExp exp, __global struct mathExp *mexp, int pos){
	float res = 0;;
        if(exp.op == NOOP){
                if (exp.opType == CONS)
                        res = exp.opValue;
                else{
                        int index = exp.opValue;
                        res = ((int *)(content+colOffset[index]))[pos];
                }
	}
	return res;
}

float calMathExp(__global char *content, __global int * colOffset,struct mathExp exp, __global struct mathExp *mexp, int pos){
        float res ;

        if(exp.op == NOOP){
                if (exp.opType == CONS)
                        res = exp.opValue;
                else{
                        int index = exp.opValue;
                        res = ((int *)(content+colOffset[index]))[pos];
                }

        }else if(exp.op == PLUS ){
                res = getExp(content,colOffset,mexp[2*pos],mexp,pos) + getExp(content, colOffset,mexp[2*pos+1], mexp,pos);

        }else if (exp.op == MINUS){
                res = getExp(content,colOffset,mexp[2*pos],mexp,pos) - getExp(content, colOffset,mexp[2*pos+1], mexp,pos);

        }else if (exp.op == MULTIPLY){
                res = getExp(content,colOffset,mexp[2*pos],mexp,pos) * getExp(content, colOffset,mexp[2*pos+1], mexp,pos);

        }else if (exp.op == DIVIDE){
                res = getExp(content,colOffset,mexp[2*pos],mexp,pos) / getExp(content, colOffset,mexp[2*pos+1], mexp,pos);
        }

        return res;
}

// for atomic add on float type data

inline void AtomicAdd(__global float *source, float operand) {
    union {
        unsigned int intVal;
        float floatVal;
    } newVal;
    union {
        unsigned int intVal;
        float floatVal;
    } prevVal;
    do {
        prevVal.floatVal = *source;
        newVal.floatVal = prevVal.floatVal + operand;
    } while (atomic_cmpxchg((volatile __global unsigned int *)source, prevVal.intVal, newVal.intVal) != prevVal.intVal);
}

__kernel void agg_cal_cons(__global char * content, __global int* colOffset, int colNum, __global struct groupByExp* exp, __global struct mathExp *mexp, __global int * gbType, __global int * gbSize, long tupleNum, __global int * key, __global int *psum,  __global char * result, __global int * resOffset){

	size_t stride = get_global_size(0);
	size_t index = get_global_id(0);

        float buf[32];
        for(int i=0;i<32;i++)
                buf[i] = 0;

        for(size_t i=index;i<tupleNum;i+=stride){

                for(int j=0;j<colNum;j++){
                        int func = exp[j].func;
                        if (func == SUM){
                                float tmpRes = calMathExp(content, colOffset,exp[j].exp, mexp, i);
                                buf[j] += tmpRes;
                        }
                }
        }

        for(int i=0;i<colNum;i++)
                AtomicAdd((__global float *)&((float *)(result+resOffset[i]))[0], buf[i]);
}

__kernel void agg_cal(__global char * content, __global int *colOffset, int colNum, __global struct groupByExp* exp, __global struct mathExp *mexp, __global int * gbType, __global int * gbSize, long tupleNum, __global int * key, __global int *psum,  __global char * result, __global int * resOffset){

	size_t stride = get_global_size(0);
	size_t index = get_global_id(0);

        for(int i=index;i<tupleNum;i+=stride){

                int hKey = key[i];
                int offset = psum[hKey];

                for(int j=0;j<colNum;j++){
                        int func = exp[j].func;
                        if(func ==NOOP){
                                int type = exp[j].exp.opType;

                                if(type == CONS){
                                        int value = exp[j].exp.opValue;
                                        ((int *)(result + resOffset[j]))[offset] = value;
                                }else{
                                        int index = exp[j].exp.opValue;
                                        int attrSize = gbSize[index];
                                        if(attrSize == sizeof(int))
                                                ((int *)(result+resOffset[j]))[offset] = ((int*)(content + colOffset[index]))[i];
                                        else{
						for(int k=0;k<attrSize;k++)
							(result+resOffset[j] + offset*attrSize)[k] = (content + colOffset[index] + i*attrSize)[k];
					}
                                }

                        }else if (func == SUM){
                                float tmpRes = calMathExp(content, colOffset, exp[j].exp,mexp, i);
                                AtomicAdd((__global float *)& ((float *)(result+resOffset[j]))[offset], tmpRes);
                        }
                }
        }
}


Added Vector2 typedef, fixed bug in kmeans.cl

typedef float2 Vector2;

void vectorInitZero(__global Vector2 *a)
{
   a->x = 0;
   a->y = 0;
}

void vectorAdd(__global Vector2 *a, __global const Vector2 *b)
{
   a->x += b->x;
   a->y += b->y;
}

void vectorScale(__global Vector2 *a, float c)
{
   a->x *= c;
   a->y *= c;
}

float vectorDiffNorm(__global const Vector2 *a, __global const Vector2 *b)
{
   float dist = 0;
   dist += (a->x - b->x) * (a->x - b->x);
   dist += (a->y - b->y) * (a->y - b->y);

   return sqrt(dist);
}

/**
 * Implementation of the MWC64X random number generator.
 *
 * @param state
 */
uint rand(uint2 *state)
{
   enum { A = 4294883355U };
   uint x = (*state).x, c = (*state).y;  // Unpack the state
   uint res = x ^ c;                     // Calculate the result
   uint hi = mul_hi(x, A);               // Step the RNG
   x = x * A + c;
   c = hi + (x < c);
   *state = (uint2)(x, c);               // Pack the state back up

   return res;                           // Return the next result
}

/**
 * Fetch and build data matrix X for a pair of genes, skipping any expressions
 * that are missing for either gene.
 *
 * @param expressions
 * @param size
 * @param indexA
 * @param indexB
 * @param X
 * @return number of rows in X
 */
int fetchData(
   __global const float *expressions, int size,
   int indexA, int indexB,
   __global Vector2 *X)
{
   int numSamples = 0;

   indexA *= size;
   indexB *= size;

   // build data matrix from expressions and indices
   int i;
   for ( i = 0; i < size; ++i )
   {
      if ( !isnan(expressions[indexA + i]) && !isnan(expressions[indexB + i]) )
      {
         // if both expressions exist add expressions to new lists and increment
         X[numSamples] = (Vector2) (
            expressions[indexA + i],
            expressions[indexB + i]
         );
         numSamples++;
      }
   }

   // return size of X
   return numSamples;
}

/**
 * Compute the log-likelihood of a K-means model given data X.
 *
 * @param X
 * @param N
 * @param y
 * @param Mu
 * @param K
 */
float computeLogLikelihood(
   __global const Vector2 *X, int N,
   __global const int *y,
   __global const Vector2 *Mu, int K)
{
   // compute within-class scatter
   float S = 0;

   for ( int k = 0; k < K; ++k )
   {
      for ( int i = 0; i < N; ++i )
      {
         if ( y[i] != k )
         {
            continue;
         }

         float dist = vectorDiffNorm(&X[i], &Mu[k]);

         S += dist * dist;
      }
   }

   return -S;
}

/**
 * Compute a K-means clustering model from a dataset.
 *
 * @param X
 * @param N
 * @param y
 * @param y_next
 * @param Mu
 * @param K
 * @param logL
 */
void computeKmeans(
   __global const Vector2 *X, int N,
   __global int *y,
   __global int *y_next,
   __global Vector2 *Mu, int K,
   float *logL)
{
   uint2 state = (get_global_id(0), get_global_id(1));

   // initialize means randomly from X
   int k;
   for ( k = 0; k < K; ++k )
   {
      int i = rand(&state) % N;
      Mu[k] = X[i];
   }

   // iterate K means until convergence
   while ( true )
   {
      // compute new labels
      int i;
      for ( i = 0; i < N; ++i )
      {
         // find k that minimizes norm(x_i - mu_k)
         int min_k = -1;
         float min_dist;

         for ( k = 0; k < K; ++k )
         {
            float dist = vectorDiffNorm(&X[i], &Mu[k]);

            if ( min_k == -1 || dist < min_dist )
            {
               min_k = k;
               min_dist = dist;
            }
         }

         y_next[i] = min_k;
      }

      // check for convergence
      bool converged = true;

      for ( i = 0; i < N; ++i )
      {
         if ( y[i] != y_next[i] )
         {
            converged = false;
            break;
         }
      }

      if ( converged )
      {
         break;
      }

      // update labels
      for ( i = 0; i < N; ++i )
      {
         y[i] = y_next[i];
      }

      // update means
      for ( k = 0; k < K; ++k )
      {
         // compute mu_k = mean of all x_i in cluster k
         int n_k = 0;

         vectorInitZero(&Mu[k]);

         for ( i = 0; i < N; ++i )
         {
            if ( y[i] == k )
            {
               vectorAdd(&Mu[k], &X[i]);
               n_k++;
            }
         }

         vectorScale(&Mu[k], 1.0f / n_k);
      }
   }

   *logL = computeLogLikelihood(X, N, y, Mu, K);
}

/**
 * Compute the Bayes information criterion of a K-means model.
 *
 * @param K
 * @param logL
 * @param N
 * @param D
 */
float computeBIC(int K, float logL, int N, int D)
{
   int p = K * D;

   return log((float) N) * p - 2 * logL;
}

/**
 * Compute a block of K-means models given a block of gene pairs.
 *
 * For each gene pair, several models are computed and the best model
 * is selected according to a criterion (BIC). The selected K and the
 * resulting sample mask for each pair is returned.
 *
 * @param expressions
 * @param size
 * @param pairs
 * @param minSamples
 * @param minClusters
 * @param maxClusters
 * @param workX
 * @param workMu
 * @param worky1
 * @param worky2
 * @param resultKs
 * @param resultLabels
 */
__kernel void computeKmeansBlock(
   __global const float *expressions, int size,
   __global const int2 *pairs,
   int minSamples, int minClusters, int maxClusters,
   __global Vector2 *workX,
   __global Vector2 *workMu,
   __global int *worky1,
   __global int *worky2,
   __global int *resultKs,
   __global int *resultLabels)
{
   // initialize workspace variables
   int i = get_global_id(0);
   __global Vector2 *X = &workX[i * size];
   __global Vector2 *Mu = &workMu[i * maxClusters];
   __global int *y1 = &worky1[i * size];
   __global int *y2 = &worky2[i * size];
   __global int *bestK = &resultKs[i];
   __global int *bestLabels = &resultLabels[i * size];

   // fetch data matrix X from expression matrix
   int numSamples = fetchData(expressions, size, pairs[i].x, pairs[i].y, X);

   // make sure minimum number of related samples is reached
   if ( numSamples >= minSamples )
   {
      float bestValue = INFINITY;

      int K;
      for ( K = minClusters; K <= maxClusters; K++ )
      {
         // run each clustering model
         float logL;
         computeKmeans(X, numSamples, y1, y2, Mu, K, &logL);

         // evaluate model
         float value = computeBIC(K, logL, numSamples, 2);

         // save the best model
         if ( value < bestValue )
         {
            *bestK = K;
            bestValue = value;

            int i;
            for ( i = 0; i < numSamples; i++ )
            {
               bestLabels[i] = y1[i];
            }
         }
      }
   }
   else
   {
      bestLabels[0] = -1;
   }
}

void vectorInitZero(__global float2 *a)
{
   a->x = 0;
   a->y = 0;
}

void vectorAdd(__global float2 *a, const __global float2 *b)
{
   a->x += b->x;
   a->y += b->y;
}

void vectorScale(__global float2 *a, float c)
{
   a->x *= c;
   a->y *= c;
}

float vectorDiffNorm(const __global float2 *a, const __global float2 *b)
{
   float dist = 0;
   dist += (a->x - b->x) * (a->x - b->x);
   dist += (a->y - b->y) * (a->y - b->y);

   return sqrt(dist);
}

/**
 * Implementation of the MWC64X random number generator.
 *
 * @param state
 */
uint rand(uint2 *state)
{
   enum { A = 4294883355U };
   uint x = (*state).x, c = (*state).y;  // Unpack the state
   uint res = x ^ c;                     // Calculate the result
   uint hi = mul_hi(x, A);               // Step the RNG
   x = x * A + c;
   c = hi + (x < c);
   *state = (uint2)(x, c);               // Pack the state back up

   return res;                           // Return the next result
}

/**
 * Fetch and build data matrix X for a pair of genes, skipping any expressions
 * that are missing for either gene.
 *
 * @param expressions
 * @param size
 * @param indexA
 * @param indexB
 * @param X
 * @return number of rows in X
 */
int fetchData(
   __global const float *expressions, int size,
   int indexA, int indexB,
   __global float2 *X)
{
   int numSamples = 0;

   indexA *= size;
   indexB *= size;

   // build data matrix from expressions and indices
   int i;
   for ( i = 0; i < size; ++i )
   {
      if ( !isnan(expressions[indexA + i]) && !isnan(expressions[indexB + i]) )
      {
         // if both expressions exist add expressions to new lists and increment
         X[numSamples] = (float2) (
            expressions[indexA + i],
            expressions[indexB + i]
         );
         numSamples++;
      }
   }

   // return size of X
   return numSamples;
}

/**
 * Compute the log-likelihood of a K-means model given data X.
 *
 * @param X
 * @param N
 * @param y
 * @param Mu
 * @param K
 */
float computeLogLikelihood(
   __global const float2 *X, int N,
   __global const int *y,
   __global const float2 *Mu, int K)
{
   // compute within-class scatter
   float S = 0;

   for ( int k = 0; k < K; ++k )
   {
      for ( int i = 0; i < N; ++i )
      {
         if ( y[i] != k )
         {
            continue;
         }

         float dist = vectorDiffNorm(&X[i], &Mu[k]);

         S += dist * dist;
      }
   }

   return -S;
}

/**
 * Compute a K-means clustering model from a dataset.
 *
 * @param X
 * @param N
 * @param y
 * @param y_next
 * @param Mu
 * @param K
 * @param logL
 */
void computeKmeans(
   __global const float2 *X, int N,
   __global int *y,
   __global int *y_next,
   __global float2 *Mu, int K,
   float *logL)
{
   uint2 state = (get_global_id(0), get_global_id(1));

   // initialize means randomly from X
   int k;
   for ( k = 0; k < K; ++k )
   {
      int i = rand(&state) % N;
      Mu[k] = X[i];
   }

   // iterate K means until convergence
   while ( true )
   {
      // compute new labels
      int i;
      for ( i = 0; i < N; ++i )
      {
         // find k that minimizes norm(x_i - mu_k)
         int min_k = -1;
         float min_dist;

         for ( k = 0; k < K; ++k )
         {
            float dist = vectorDiffNorm(&X[i], &Mu[k]);

            if ( min_k == -1 || dist < min_dist )
            {
               min_k = k;
               min_dist = dist;
            }
         }

         y_next[i] = min_k;
      }

      // check for convergence
      bool converged = true;

      for ( i = 0; i < N; ++i )
      {
         if ( y[i] != y_next[i] )
         {
            converged = false;
            break;
         }
      }

      if ( converged )
      {
         break;
      }

      // update labels
      for ( i = 0; i < N; ++i )
      {
         y[i] = y_next[i];
      }

      // update means
      for ( k = 0; k < K; ++k )
      {
         // compute mu_k = mean of all x_i in cluster k
         int n_k = 0;

         vectorInitZero(&Mu[k]);

         for ( i = 0; i < N; ++i )
         {
            if ( y[i] == k )
            {
               vectorAdd(&Mu[k], &X[i]);
               n_k++;
            }
         }

         vectorScale(&Mu[k], 1.0f / n_k);
      }
   }

   *logL = computeLogLikelihood(X, N, y, Mu, K);
}

/**
 * Compute the Bayes information criterion of a K-means model.
 *
 * @param K
 * @param logL
 * @param N
 * @param D
 */
float computeBIC(int K, float logL, int N, int D)
{
   int p = K * D;

   return log((float) N) * p - 2 * logL;
}

/**
 * Compute a block of K-means models given a block of gene pairs.
 *
 * For each gene pair, several models are computed and the best model
 * is selected according to a criterion (BIC). The resulting sample mask
 * for each pair is returned.
 *
 * @param expressions
 * @param size
 * @param pairs
 * @param minSamples
 * @param minClusters
 * @param maxClusters
 * @param workX
 * @param workMu
 * @param worky1
 * @param worky2
 * @param results
 */
__kernel void computeKmeansBlock(
   __global const float *expressions, int size,
   __global const int2 *pairs,
   int minSamples, int minClusters, int maxClusters,
   __global float2 *workX,
   __global float2 *workMu,
   __global int *worky1,
   __global int *worky2,
   __global int *resultKs,
   __global int *resultLabels)
{
   // initialize workspace variables
   int i = get_global_id(0);
   __global float2 *X = &workX[i * size];
   __global float2 *Mu = &workMu[i * maxClusters];
   __global int *y1 = &worky1[i * size];
   __global int *y2 = &worky2[i * size];
   __global int *bestK = &resultKs[i];
   __global int *bestLabels = &resultLabels[i * size];

   // fetch data matrix X from expression matrix
   int numSamples = fetchData(expressions, size, pairs[i].x, pairs[i].y, X);

   // make sure minimum number of related samples is reached
   if ( numSamples >= minSamples )
   {
      float bestValue = INFINITY;

      int K;
      for ( K = minClusters; K <= maxClusters; K++ )
      {
         // run each clustering model
         float logL;
         computeKmeans(X, numSamples, y1, y2, Mu, K, &logL);

         // evaluate model
         float value = computeBIC(K, logL, numSamples, 2);

         // save the best model
         if ( value < bestValue )
         {
            *bestK = K;
            bestValue = value;

            int i;
            for ( i = 0; i < numSamples; i++ )
            {
               bestLabels[i] = y1[i];
            }
         }
      }
   }
   else
   {
      bestLabels[0] = NAN;
   }
}


Gamma decode/encode

Gamma decode/encode

/* Deathray - An Avisynth plug-in filter for spatial/temporal non-local means de-noising.
 *
 * version 1.01
 *
 * Copyright 2013, Jawed Ashraf - Deathray@cupidity.f9.co.uk
 */

#define TILE_SIDE 53

__kernel void Zero(__global float4 *A) {
    int pos = get_global_id(0);
	A[pos] = 0.f;
}

// fast_power
// OpenCL on AMD GPUs doesn't expose the fast
// pow. The OpenCL pow function and its variants
// are all maximum precision. It's also more than
// twice as slow as this, which will have 1 or 2 ULP
// worse precision, I guess (not measured)
float fast_power(float x, float y) {
	return exp2(y * log2(x));
}

float4 fast_power4(float4 x, float y) {
	float4 power;

	float4 log_2 = log2(x);
#if 0 // slowest
	power.x = exp2(y * log2(x.x));
	power.y = exp2(y * log2(x.y));
	power.z = exp2(y * log2(x.z));
	power.w = exp2(y * log2(x.w));
#else
#if 0 // faster
	power = exp2(y * log_2);
#else // fastest
	power.x = exp2(y * log_2.x);
	power.y = exp2(y * log_2.y);
	power.z = exp2(y * log_2.z);
	power.w = exp2(y * log_2.w);
#endif
#endif

	return power;
}

// gamma_decode
// Converts input from gamma space into linear space.
float gamma_decode(float x) {
#if 1
	if (x <= 0.081f) return x/4.5f;
	return fast_power(((x + 0.099f)/1.099f), 2.22222222f);
#else
	return fast_power(x, 2.22222222f);
#endif
}

float4 gamma_decode4(float4 x) {
	float4 de_gamma;

	de_gamma.x = gamma_decode(x.x);
	de_gamma.y = gamma_decode(x.y);
	de_gamma.z = gamma_decode(x.z);
	de_gamma.w = gamma_decode(x.w);

	return de_gamma;
}

// gamma_encode
// Converts input from linear space into gamma space.
float gamma_encode(float x) {
#if 1
	if (x <= 0.018f) return 4.5f * x;
	return 1.099f * fast_power(x, 0.45f) - 0.099f;
#else
	return fast_power(x, 0.45f);
#endif
}

float4 gamma_encode4(float4 x) {
	float4 en_gamma;

	en_gamma.x = gamma_encode(x.x);
	en_gamma.y = gamma_encode(x.y);
	en_gamma.z = gamma_encode(x.z);
	en_gamma.w = gamma_encode(x.w);

	return en_gamma;
}

void WriteTile4(
	float4 store,
	int x,					// coordinates of a strip ...
	int y,					// ... of 4 pixels to be written
	local float *tile) {	// 48x48 portion of plane in local memory

	vstore4(store, 0, tile + ((y * TILE_SIDE) + x));
	write_mem_fence(CLK_LOCAL_MEM_FENCE);
}

float4 ReadTile4(
	int x,					// coordinates of a strip ...
	int y,					// ... of 4 pixels to be fetched
	local float *tile) {	// 48x48 portion of plane in local memory

	float4 result;

	result = vload4(0, tile + ((y * TILE_SIDE) + x));
	read_mem_fence(CLK_LOCAL_MEM_FENCE);
	return result;
}

float16 ReadTile16(
	int x,					// coordinates of a strip ...
	int y,					// ... of 16 pixels to be fetched
	local float *tile) {	// 48x48 portion of plane in local memory

	float16 result;

	result = vload16(0, tile + ((y * TILE_SIDE) + x));
	read_mem_fence(CLK_LOCAL_MEM_FENCE);
	return result;
}

float16 Unpack10PixelsAndNormalise(uint4 sixteen_pixels) {
	// Unpack 10 pixels, which are packed into 128 bits as uchars,
	// into 10 normalised pixels as floats.
	//
	// Only 10 pixels are required from each row of a window.

	uchar4 four_pixels;
	float16 result;

	four_pixels = as_uchar4(sixteen_pixels.x);

	result.s0 = (float)four_pixels.x * 0.0039215686f;
	result.s1 = (float)four_pixels.y * 0.0039215686f;
	result.s2 = (float)four_pixels.z * 0.0039215686f;
	result.s3 = (float)four_pixels.w * 0.0039215686f;

	four_pixels = as_uchar4(sixteen_pixels.y);

	result.s4 = (float)four_pixels.x * 0.0039215686f;
	result.s5 = (float)four_pixels.y * 0.0039215686f;
	result.s6 = (float)four_pixels.z * 0.0039215686f;
	result.s7 = (float)four_pixels.w * 0.0039215686f;

	four_pixels = as_uchar4(sixteen_pixels.z);

	result.s8 = (float)four_pixels.x * 0.0039215686f;
	result.s9 = (float)four_pixels.y * 0.0039215686f;

	return result;
}

uint4 Pack10PixelsIntoTargetRow(float16 sixteen_pixels) {
	// Pack 10 float pixels into 10 uchars, stored as a uint4.
	//
	// Only 10 pixels are required for target window's row.

	uchar4 four_pixels;
	uint4 result;

	four_pixels.x = (uchar)(sixteen_pixels.s0 * 255.f);
	four_pixels.y = (uchar)(sixteen_pixels.s1 * 255.f);
	four_pixels.z = (uchar)(sixteen_pixels.s2 * 255.f);
	four_pixels.w = (uchar)(sixteen_pixels.s3 * 255.f);

	result.x = as_uint(four_pixels);

	four_pixels.x = (uchar)(sixteen_pixels.s4 * 255.f);
	four_pixels.y = (uchar)(sixteen_pixels.s5 * 255.f);
	four_pixels.z = (uchar)(sixteen_pixels.s6 * 255.f);
	four_pixels.w = (uchar)(sixteen_pixels.s7 * 255.f);

	result.y = as_uint(four_pixels);

	four_pixels.x = (uchar)(sixteen_pixels.s8 * 255.f);
	four_pixels.y = (uchar)(sixteen_pixels.s9 * 255.f);

	result.z = as_uint(four_pixels);

	return result;
}

void Coordinates32x32(
	int2 *local_id,	// Work item
	int2 *source) {	// Each work item processes 4 pixels from source as a single row uchar4/float4

	// Generate coordinates for use by a variety of kernels based upon
	// a tile size of 32x32 pixels.

	// The 32x32 tile's top-left position in the plane, in uchar4s
	int2 tile_top_left = (int2)(get_group_id(0) << 3, get_group_id(1) << 5);

	*local_id = (int2)(get_local_id(0), get_local_id(1));
	*source = tile_top_left + *local_id;
}

void FetchAndMirror48x48(
	read_only 	image2d_t 	target_plane,	// input image
	const		int			width,			// width in pixels
	const		int			height,			// height in pixels
	const		int2		local_id,		// Work item
	const		int2		source,			// coordinates
	local		float		*tile) {		// existing block of local memory populated with 48x48 pixels
	// Fetch pixels from source and populate a 48x48 tile of pixels.
	//
	// The tile is defined as a 32x32 region to be filtered plus an
	// apron of 8 pixels on all four sides, making 48x48.
	//
	// The apron is filled with pixels from adjacent regions. At the
	// four edges of the frame the apron is filled, instead, with
	// pixels mirrored from just inside the frame.

	const sampler_t plane = CLK_NORMALIZED_COORDS_FALSE |
							CLK_ADDRESS_CLAMP |
							CLK_FILTER_NEAREST;

	float4 source_pixels = read_imagef(target_plane, plane, source);

	write_mem_fence(CLK_LOCAL_MEM_FENCE);

	// Scalar address for left-most pixel of 4 pixels that are put into tile
	int2 target = (int2)((local_id.x << 2) + 8, local_id.y + 8);

	WriteTile4(source_pixels, target.x, target.y, tile);

	if (local_id.y < 8 || local_id.y > 23) { // 8 top and bottom rows of apron are filled from adjacent tiles
		int offset = (local_id.y < 8) ? -8 : 8;
		source_pixels = read_imagef(target_plane, plane, source + (int2)(0, offset));
		WriteTile4(source_pixels, target.x, target.y + offset, tile);
	}
	if (local_id.x < 2 || local_id.x > 5) { // 8 columns at left and right
		int offset = (local_id.x < 2) ? -2 : 2;
		source_pixels = read_imagef(target_plane, plane, source + (int2)(offset, 0));
		WriteTile4(source_pixels, target.x + (offset << 2), target.y, tile);
	}
	if (local_id.y < 8 || local_id.y > 23) { // 4 corners, each 8x8
		int2 offset;
		offset.y = (local_id.y < 8) ? -8 : 8;
		if (local_id.x < 2 || local_id.x > 5) {
			offset.x = (local_id.x < 2) ? -2 : 2;
			source_pixels = read_imagef(target_plane, plane, source + offset);
			WriteTile4(source_pixels, target.x + (offset.x << 2), target.y + offset.y, tile);
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);

	// Mirror into apron when at frame border
	float4 mirror;
	bool left_group  = (get_group_id(0) == 0);
	bool right_group = (get_group_id(0) == (get_num_groups(0) - 1));
	bool top_group   = (get_group_id(1) == 0);
	bool bot_group   = (get_group_id(1) == (get_num_groups(1) - 1));

	if (top_group && local_id.y < 8) { 	// top 8 rows
		mirror = ReadTile4(target.x, target.y + 1, tile);
		WriteTile4(mirror, target.x, 8 - local_id.y - 1, tile);
	}
	barrier(CLK_LOCAL_MEM_FENCE);

	if (bot_group && local_id.y < 8) { // bottom 8 rows
		int centre = ((height - 1) & 31) + 8;

		mirror = ReadTile4(target.x, centre - local_id.y - 1, tile);
		WriteTile4(mirror, target.x, centre + local_id.y + 1, tile);
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	// left 8 columns + top-left and bottom-left apron corners per tile
	int read_x  = (local_id.x == 1) ? 13 : 9;
	int write_x = (local_id.x == 1) ? 0 : 4;
	if (local_id.x < 2) {
		if (left_group) { // left 8 columns, 32 rows
			mirror = ReadTile4(read_x, target.y, tile);
			WriteTile4(mirror.wzyx, write_x, target.y, tile);
		}

		// TODO - cleanup IF structure - not sure if the compiler will jinx this
		if (local_id.y < 8) {
			if (left_group) { // left edge of frame apron corners, top and bottom
				mirror = ReadTile4(read_x, local_id.y, tile);
				WriteTile4(mirror.wzyx, write_x, local_id.y, tile);
				mirror = ReadTile4(read_x, 40 + local_id.y, tile);
				WriteTile4(mirror.wzyx, write_x, 40 + local_id.y, tile);
			}
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	if (local_id.x < 2 && local_id.y < 8) {
		int x = (local_id.x == 1) ? 4 : 0;
		if (top_group) { // apron at top edge of frame, left corners
			mirror = ReadTile4(x, 8 + local_id.y + 1, tile);
			WriteTile4(mirror, x, 8 - local_id.y - 1, tile);
		}
		if (bot_group) { // apron at bottom edge of frame, left corners
			int bottom = ((height - 1) & 31) + 8;
			mirror = ReadTile4(x, bottom - local_id.y - 1, tile);
			WriteTile4(mirror, x, bottom + local_id.y + 1, tile);
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);

	if (local_id.x > 5) { // right 8 columns + top-right and bottom-right apron corners per tile

		if (right_group) { // right edge of frame
			int centre  = ((width - 1) & 31) + 8;
			int read_x  = (local_id.x == 7) ? centre - 4 : centre - 8;
			int write_x = (local_id.x == 7) ? centre + 1 : centre + 5;

			mirror = ReadTile4(read_x, target.y, tile);
			WriteTile4(mirror.wzyx, write_x, target.y, tile);

			if (local_id.y < 8) { // top right and bottom right corner of every tile on right edge
				mirror = ReadTile4(read_x, local_id.y, tile);
				WriteTile4(mirror.wzyx, write_x, local_id.y, tile);
				mirror = ReadTile4(read_x, 40 + local_id.y, tile);
				WriteTile4(mirror.wzyx, write_x, 40 + local_id.y, tile);

				if (bot_group) { // bottom right corner of frame
					int bottom = ((height - 1) & 31) + 8;
					mirror = ReadTile4(read_x, bottom + local_id.y + 1, tile);
					WriteTile4(mirror.wzyx, write_x, bottom + local_id.y + 1, tile);
				}
			}
		} else if ((top_group || bot_group) && local_id.y < 8) {
			if (top_group) {
				int x = (local_id.x == 7) ? 44 : 40; // ATI 2.3: compiler bug requires this here if TILE_SIDE is not 48
				mirror = ReadTile4(x, 8 + local_id.y + 1, tile);
				WriteTile4(mirror, x, 8 - local_id.y - 1, tile);
			}
			if (bot_group) {
				int x = (local_id.x == 7) ? 44 : 40; // ATI 2.3: compiler bug requires this here if TILE_SIDE is not 48
				int bottom = ((height - 1) & 31) + 8;
				mirror = ReadTile4(x, bottom - local_id.y - 1, tile);
				WriteTile4(mirror, x, bottom + local_id.y + 1, tile);
			}
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);
}

/* Deathray - An Avisynth plug-in filter for spatial/temporal non-local means de-noising.
 *
 * version 1.01
 *
 * Copyright 2013, Jawed Ashraf - Deathray@cupidity.f9.co.uk
 */

#define TILE_SIDE 53

__kernel void Zero(__global float4 *A) {
    int pos = get_global_id(0);
	A[pos] = 0.f;
}

void WriteTile4(
	float4 store,
	int x,					// coordinates of a strip ...
	int y,					// ... of 4 pixels to be written
	local float *tile) {	// 48x48 portion of plane in local memory

	vstore4(store, 0, tile + ((y * TILE_SIDE) + x));
	write_mem_fence(CLK_LOCAL_MEM_FENCE);
}

float4 ReadTile4(
	int x,					// coordinates of a strip ...
	int y,					// ... of 4 pixels to be fetched
	local float *tile) {	// 48x48 portion of plane in local memory

	float4 result;

	result = vload4(0, tile + ((y * TILE_SIDE) + x));
	read_mem_fence(CLK_LOCAL_MEM_FENCE);
	return result;
}

float16 ReadTile16(
	int x,					// coordinates of a strip ...
	int y,					// ... of 16 pixels to be fetched
	local float *tile) {	// 48x48 portion of plane in local memory

	float16 result;

	result = vload16(0, tile + ((y * TILE_SIDE) + x));
	read_mem_fence(CLK_LOCAL_MEM_FENCE);
	return result;
}

float16 Unpack10PixelsAndNormalise(uint4 sixteen_pixels) {
	// Unpack 10 pixels, which are packed into 128 bits as uchars,
	// into 10 normalised pixels as floats.
	//
	// Only 10 pixels are required from each row of a window.

	uchar4 four_pixels;
	float16 result;

	four_pixels = as_uchar4(sixteen_pixels.x);

	result.s0 = (float)four_pixels.x * 0.0039215686f;
	result.s1 = (float)four_pixels.y * 0.0039215686f;
	result.s2 = (float)four_pixels.z * 0.0039215686f;
	result.s3 = (float)four_pixels.w * 0.0039215686f;

	four_pixels = as_uchar4(sixteen_pixels.y);

	result.s4 = (float)four_pixels.x * 0.0039215686f;
	result.s5 = (float)four_pixels.y * 0.0039215686f;
	result.s6 = (float)four_pixels.z * 0.0039215686f;
	result.s7 = (float)four_pixels.w * 0.0039215686f;

	four_pixels = as_uchar4(sixteen_pixels.z);

	result.s8 = (float)four_pixels.x * 0.0039215686f;
	result.s9 = (float)four_pixels.y * 0.0039215686f;

	return result;
}

uint4 Pack10PixelsIntoTargetRow(float16 sixteen_pixels) {
	// Pack 10 float pixels into 10 uchars, stored as a uint4.
	//
	// Only 10 pixels are required for target window's row.

	uchar4 four_pixels;
	uint4 result;

	four_pixels.x = (uchar)(sixteen_pixels.s0 * 255.f);
	four_pixels.y = (uchar)(sixteen_pixels.s1 * 255.f);
	four_pixels.z = (uchar)(sixteen_pixels.s2 * 255.f);
	four_pixels.w = (uchar)(sixteen_pixels.s3 * 255.f);

	result.x = as_uint(four_pixels);

	four_pixels.x = (uchar)(sixteen_pixels.s4 * 255.f);
	four_pixels.y = (uchar)(sixteen_pixels.s5 * 255.f);
	four_pixels.z = (uchar)(sixteen_pixels.s6 * 255.f);
	four_pixels.w = (uchar)(sixteen_pixels.s7 * 255.f);

	result.y = as_uint(four_pixels);

	four_pixels.x = (uchar)(sixteen_pixels.s8 * 255.f);
	four_pixels.y = (uchar)(sixteen_pixels.s9 * 255.f);

	result.z = as_uint(four_pixels);

	return result;
}

void Coordinates32x32(
	int2 *local_id,	// Work item
	int2 *source) {	// Each work item processes 4 pixels from source as a single row uchar4/float4

	// Generate coordinates for use by a variety of kernels based upon
	// a tile size of 32x32 pixels.

	// The 32x32 tile's top-left position in the plane, in uchar4s
	int2 tile_top_left = (int2)(get_group_id(0) << 3, get_group_id(1) << 5);

	*local_id = (int2)(get_local_id(0), get_local_id(1));
	*source = tile_top_left + *local_id;
}

void FetchAndMirror48x48(
	read_only 	image2d_t 	target_plane,	// input image
	const		int			width,			// width in pixels
	const		int			height,			// height in pixels
	const		int2		local_id,		// Work item
	const		int2		source,			// coordinates
	local		float		*tile) {		// existing block of local memory populated with 48x48 pixels
	// Fetch pixels from source and populate a 48x48 tile of pixels.
	//
	// The tile is defined as a 32x32 region to be filtered plus an
	// apron of 8 pixels on all four sides, making 48x48.
	//
	// The apron is filled with pixels from adjacent regions. At the
	// four edges of the frame the apron is filled, instead, with
	// pixels mirrored from just inside the frame.

	const sampler_t plane = CLK_NORMALIZED_COORDS_FALSE |
							CLK_ADDRESS_CLAMP |
							CLK_FILTER_NEAREST;

	float4 source_pixels = read_imagef(target_plane, plane, source);

	write_mem_fence(CLK_LOCAL_MEM_FENCE);

	// Scalar address for left-most pixel of 4 pixels that are put into tile
	int2 target = (int2)((local_id.x << 2) + 8, local_id.y + 8);

	WriteTile4(source_pixels, target.x, target.y, tile);

	if (local_id.y < 8 || local_id.y > 23) { // 8 top and bottom rows of apron are filled from adjacent tiles
		int offset = (local_id.y < 8) ? -8 : 8;
		source_pixels = read_imagef(target_plane, plane, source + (int2)(0, offset));
		WriteTile4(source_pixels, target.x, target.y + offset, tile);
	}
	if (local_id.x < 2 || local_id.x > 5) { // 8 columns at left and right
		int offset = (local_id.x < 2) ? -2 : 2;
		source_pixels = read_imagef(target_plane, plane, source + (int2)(offset, 0));
		WriteTile4(source_pixels, target.x + (offset << 2), target.y, tile);
	}
	if (local_id.y < 8 || local_id.y > 23) { // 4 corners, each 8x8
		int2 offset;
		offset.y = (local_id.y < 8) ? -8 : 8;
		if (local_id.x < 2 || local_id.x > 5) {
			offset.x = (local_id.x < 2) ? -2 : 2;
			source_pixels = read_imagef(target_plane, plane, source + offset);
			WriteTile4(source_pixels, target.x + (offset.x << 2), target.y + offset.y, tile);
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);

	// Mirror into apron when at frame border
	float4 mirror;
	bool left_group  = (get_group_id(0) == 0);
	bool right_group = (get_group_id(0) == (get_num_groups(0) - 1));
	bool top_group   = (get_group_id(1) == 0);
	bool bot_group   = (get_group_id(1) == (get_num_groups(1) - 1));

	if (top_group && local_id.y < 8) { 	// top 8 rows
		mirror = ReadTile4(target.x, target.y + 1, tile);
		WriteTile4(mirror, target.x, 8 - local_id.y - 1, tile);
	}
	barrier(CLK_LOCAL_MEM_FENCE);

	if (bot_group && local_id.y < 8) { // bottom 8 rows
		int centre = ((height - 1) & 31) + 8;

		mirror = ReadTile4(target.x, centre - local_id.y - 1, tile);
		WriteTile4(mirror, target.x, centre + local_id.y + 1, tile);
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	// left 8 columns + top-left and bottom-left apron corners per tile
	int read_x  = (local_id.x == 1) ? 13 : 9;
	int write_x = (local_id.x == 1) ? 0 : 4;
	if (local_id.x < 2) {
		if (left_group) { // left 8 columns, 32 rows
			mirror = ReadTile4(read_x, target.y, tile);
			WriteTile4(mirror.wzyx, write_x, target.y, tile);
		}

		// TODO - cleanup IF structure - not sure if the compiler will jinx this
		if (local_id.y < 8) {
			if (left_group) { // left edge of frame apron corners, top and bottom
				mirror = ReadTile4(read_x, local_id.y, tile);
				WriteTile4(mirror.wzyx, write_x, local_id.y, tile);
				mirror = ReadTile4(read_x, 40 + local_id.y, tile);
				WriteTile4(mirror.wzyx, write_x, 40 + local_id.y, tile);
			}
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);
	if (local_id.x < 2 && local_id.y < 8) {
		int x = (local_id.x == 1) ? 4 : 0;
		if (top_group) { // apron at top edge of frame, left corners
			mirror = ReadTile4(x, 8 + local_id.y + 1, tile);
			WriteTile4(mirror, x, 8 - local_id.y - 1, tile);
		}
		if (bot_group) { // apron at bottom edge of frame, left corners
			int bottom = ((height - 1) & 31) + 8;
			mirror = ReadTile4(x, bottom - local_id.y - 1, tile);
			WriteTile4(mirror, x, bottom + local_id.y + 1, tile);
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);

	if (local_id.x > 5) { // right 8 columns + top-right and bottom-right apron corners per tile

		if (right_group) { // right edge of frame
			int centre  = ((width - 1) & 31) + 8;
			int read_x  = (local_id.x == 7) ? centre - 4 : centre - 8;
			int write_x = (local_id.x == 7) ? centre + 1 : centre + 5;

			mirror = ReadTile4(read_x, target.y, tile);
			WriteTile4(mirror.wzyx, write_x, target.y, tile);

			if (local_id.y < 8) { // top right and bottom right corner of every tile on right edge
				mirror = ReadTile4(read_x, local_id.y, tile);
				WriteTile4(mirror.wzyx, write_x, local_id.y, tile);
				mirror = ReadTile4(read_x, 40 + local_id.y, tile);
				WriteTile4(mirror.wzyx, write_x, 40 + local_id.y, tile);

				if (bot_group) { // bottom right corner of frame
					int bottom = ((height - 1) & 31) + 8;
					mirror = ReadTile4(read_x, bottom + local_id.y + 1, tile);
					WriteTile4(mirror.wzyx, write_x, bottom + local_id.y + 1, tile);
				}
			}
		} else if ((top_group || bot_group) && local_id.y < 8) {
			if (top_group) {
				int x = (local_id.x == 7) ? 44 : 40; // ATI 2.3: compiler bug requires this here if TILE_SIDE is not 48
				mirror = ReadTile4(x, 8 + local_id.y + 1, tile);
				WriteTile4(mirror, x, 8 - local_id.y - 1, tile);
			}
			if (bot_group) {
				int x = (local_id.x == 7) ? 44 : 40; // ATI 2.3: compiler bug requires this here if TILE_SIDE is not 48
				int bottom = ((height - 1) & 31) + 8;
				mirror = ReadTile4(x, bottom - local_id.y - 1, tile);
				WriteTile4(mirror, x, bottom + local_id.y + 1, tile);
			}
		}
	}
	barrier(CLK_LOCAL_MEM_FENCE);
}