converted some CONSTANTs to #define s

converted some CONSTANTs to #define s

/*  Copyright 2014 Aaron Boxer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "ocl_platform.cl"

//////////////////////////
// dimensions of window
// WIN_SIZE_X
// WIN_SIZE_Y assume this equals number of work items in work group
///////////////////////////

//  scratch buffer (in local memory of GPU) where block of input image is stored.
//  All operations expect WIN_SIZE_Y threads.

 /**

Layout for scratch buffer

Odd and even rows are separated. (Generates less bank conflicts when using lifting scheme.)
All even rows are stored first, then all odd rows.

Left (even) boundary row
Even rows
Right (even) boundary row
Left (odd) boundary row
Odd rows
Right (odd) boundary row

 **/

#define BOUNDARY_Y 4

#define HORIZONTAL_STRIDE 64  // WIN_SIZE_X/2

// two vertical neighbours: pointer diff:
#define BUFFER_SIZE            512	// HORIZONTAL_STRIDE * WIN_SIZE_X

#define CHANNEL_BUFFER_SIZE     1024            // BUFFER_SIZE + BUFFER_SIZE

#define CHANNEL_BUFFER_SIZE_X2  2048
#define CHANNEL_BUFFER_SIZE_X3  3072

#define PIXEL_BUFFER_SIZE   4096

#define VERTICAL_EVEN_TO_PREVIOUS_ODD  511
#define VERTICAL_EVEN_TO_NEXT_ODD      512

#define VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE -513
#define VERTICAL_ODD_TO_PREVIOUS_EVEN -512
#define VERTICAL_ODD_TO_NEXT_EVEN     -511
#define VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE     -510

#define P1  -1.586134342f
#define U1  -0.05298011854f
#define P2  0.8829110762f
#define U2  0.4435068522f

// U1 * P1
#define U1P1  0.08403358545952490068f

#define scale97Mul  1.23017410491400f
#define scale97Div   0.81289306611596153187273657637352f

/*

Lifting scheme consists of four steps: Predict1 Update1 Predict2 Update2
followed by scaling (even points are scaled by scale97Div, odd points are scaled by scale97Mul)

Predict Calculation:

For S odd, we have:

plusOne_P1 = plusOne + P1*(current + plusTwo)

plusOne_P2 = plusOne_P1 +
             P2*(current_U1 + plusTwo_U1)
           =   plusOne + P1*(current + plusTwo) +
		         P2*(current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo) +
				     plusTwo + U1*(plusOne + plusThree) + U1P1*(current + 2*plusTwo + plusFour)  )

Update Calculation

For S even, we have

current_U1 = current + U1*(minusOne_P1 + plusOne_P1)
           = current + U1*(minusOne + P1*(minusTwo + current) + plusOne + P1*(current + plusTwo))
		   = current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo)

current_U2 = current_U1 + U2*(minusOne_P2 + plusOne_P2)

*/

/*
Quantization:

CONSTANT float norms[4][6] = {
	{1.000, 1.965, 4.177, 8.403, 16.90, 33.84},
	{2.022, 3.989, 8.355, 17.04, 34.27, 68.63},
	{2.022, 3.989, 8.355, 17.04, 34.27, 68.63},
	{2.080, 3.865, 8.307, 17.18, 34.71, 69.59}
};

Each resolution has four bands LL LH HL HH
Gain for the bands:            0  1  1  2
Number of bits per sample      precision + gain
base_stepsize                  (1 << (gain)) / norm

void opj_dwt_calc_explicit_stepsizes(int numresolutions, int prec) {
	int numbands, bandno;
	numbands = 3 * numresolutions - 2;
	for (bandno = 0; bandno < numbands; bandno) {

		float stepsize;
		int resno, level, orient, gain;

		resno = (bandno == 0) ? 0 : ((bandno - 1) / 3 + 1);
		orient = (bandno == 0) ? 0 : ((bandno - 1) % 3 + 1);
		level = numresolutions - 1 - resno;
		gain = (orient == 0) ? 0 : (((orient == 1) || (orient == 2)) ? 1 : 2);
		float norm = norms[orient][level];
		base_stepsize = floor(((1 << (gain)) / norm) * 8192);
		int p = int_floorlog2(base_stepsize) - 13;
		int n = 11 - int_floorlog2(base_stepsize);
		mant = (n < 0 ? stepsize >> -n : base_stepsize << n) & 0x7ff;
		expn = numbps - p;
		stepsize = (1.0 + mant / 2048.0) * pow(2.0, (numbps - expn));
	}
}

//Get logarithm of an integer and round downwards
//@return Returns log2(a)
int int_floorlog2(OPJ_INT32 a) {
	int l;
	for (l = 0; a > 1; l) {
		a >>= 1;
	}
	return l;
}

*/

///////////////////////////////////////////////////////////////////////

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

inline int getCorrectedGlobalIdY() {
      return getGlobalId(1) - 2 * BOUNDARY_Y * getGroupId(1);
}

// read pixel from local buffer
inline float4 readPixel( LOCAL float*  restrict  src) {
	return (float4)(*src, *(src+CHANNEL_BUFFER_SIZE),  *(src+CHANNEL_BUFFER_SIZE_X2),  *(src+CHANNEL_BUFFER_SIZE_X3)) ;
}

//write pixel to column
inline void writePixel(float4 pix, LOCAL float*  restrict  dest) {
	*dest = pix.x;
	dest += CHANNEL_BUFFER_SIZE;
	*dest = pix.y;
	dest += CHANNEL_BUFFER_SIZE;
	*dest = pix.z;
	dest += CHANNEL_BUFFER_SIZE;
	*dest = pix.w;
}

// write row to destination
void writeRowToOutput(LOCAL float* restrict currentScratch, write_only image2d_t odataLL, write_only image2d_t odata,
																		unsigned int firstX, unsigned int outputY, unsigned int width, unsigned int halfWidth){

	int2 posOut = {firstX>>1, outputY};
	for (int j = 0; j < WIN_SIZE_X; j+=2) {

	    // even column
		//only need to check evens, since even point will be the first out of bound point
	    if (posOut.x >= halfWidth)
			break;

		write_imagef(odataLL, posOut,scale97Div * readPixel(currentScratch));

		// odd column
		currentScratch += HORIZONTAL_STRIDE ;
		posOut.x+= halfWidth;

		write_imagef(odata, posOut,scale97Mul * readPixel(currentScratch));

		currentScratch += HORIZONTAL_STRIDE;
		posOut.x -= (halfWidth - 1);
	}
}

// initial scratch offset when transforming horizontally
inline unsigned int getScratchOffset(){
   return (getLocalId(1)>> 1) + (getLocalId(1)&1) * BUFFER_SIZE;
}

// assumptions: width and height are both even
// (we will probably have to relax these assumptions in the future)
void KERNEL run(read_only image2d_t idata, write_only image2d_t odataLL, write_only image2d_t odata,
                       const unsigned int  width, const unsigned int  height, const unsigned int steps) {

	int inputY = getCorrectedGlobalIdY();
	int outputY = -1;
	if (inputY < height && inputY >= 0)
	    outputY = (inputY >> 1) + (inputY & 1)*( height >> 1);

	bool writeRow = ((getLocalId(1) >= BOUNDARY_Y) && ( getLocalId(1) < WIN_SIZE_Y - BOUNDARY_Y) && outputY != -1);
	bool doP2 = false;
	bool doU2 = false;
	if (getLocalId(1)&1)
		doP2 = (getLocalId(1) >= BOUNDARY_Y-1) && (getLocalId(1) < WIN_SIZE_Y-BOUNDARY_Y);
	else
		doU2 = (getLocalId(1) >= BOUNDARY_Y) && (getLocalId(1) < WIN_SIZE_Y-BOUNDARY_Y) ;

    const unsigned int halfWidth = width >> 1;
	LOCAL float scratch[PIXEL_BUFFER_SIZE];
	const float xDelta = 1.0/(width-1);
	int firstX = getGlobalId(0) * (steps * WIN_SIZE_X);

	//0. Initialize: fetch first pixel (and 2 top boundary pixels)

	// read -4 point
	float2 posIn = (float2)(firstX-4, inputY) /  (float2)(width-1, height-1);
	float4 minusFour = read_imagef(idata, sampler, posIn);

	posIn.x += xDelta;
	float4 minusThree = read_imagef(idata, sampler, posIn);

	// read -2 point
	posIn.x += xDelta;
	float4 minusTwo = read_imagef(idata, sampler, posIn);

	// read -1 point
	posIn.x += xDelta;
	float4 minusOne = read_imagef(idata, sampler, posIn);

	// read 0 point
	posIn.x += xDelta;
	float4 current = read_imagef(idata, sampler, posIn);

	// +1 point
	posIn.x += xDelta;
	float4 plusOne = read_imagef(idata, sampler, posIn);

	// +2 point
	posIn.x += xDelta;
	float4 plusTwo = read_imagef(idata, sampler, posIn);

	float4 minusThree_P1 = minusThree + P1*(minusFour + minusTwo);
	float4 minusOne_P1   = minusOne   + P1*(minusTwo + current);
	float4 plusOne_P1    = plusOne    + P1*(current + plusTwo);

	float4 minusTwo_U1 = minusTwo + U1*(minusThree_P1 + minusOne_P1);
	float4 current_U1  = current + U1*(minusOne_P1 + plusOne_P1);
	float4 minusOne_P2 = minusOne_P1 + P2*(minusTwo_U1 + current_U1);

	for (int i = 0; i < steps; ++i) {

		// 1. read from source image, transform rows, and store in local scratch
		LOCAL float* currentScratch = scratch + getScratchOffset();
		for (int j = 0; j < WIN_SIZE_X; j+=2) {

	        //read next two points

			// +3 point
			posIn.x += xDelta;
			float4 plusThree = read_imagef(idata, sampler, posIn);

	   		// +4 point
			posIn.x += xDelta;
	   		if (posIn.x > 1 + 3*xDelta)
				break;
			float4 plusFour = read_imagef(idata, sampler, posIn);

			float4 plusThree_P1    = plusThree  + P1*(plusTwo + plusFour);
			float4 plusTwo_U1      = plusTwo + U1*(plusOne_P1 + plusThree_P1);
			float4 plusOne_P2      = plusOne_P1 + P2*(current_U1 + plusTwo_U1);

			//write current U2 (even)
			writePixel(scale97Div * (current_U1 +  U2 * (minusOne_P2 + plusOne_P2)), currentScratch);

			//advance scratch pointer
			currentScratch += HORIZONTAL_STRIDE;

			//write current P2 (odd)
			writePixel(scale97Mul* plusOne_P2 , currentScratch);

			//advance scratch pointer
			currentScratch += HORIZONTAL_STRIDE;

			// shift registers up by two
			minusFour = minusTwo;
			minusThree = minusOne;
			minusTwo = current;
			minusOne = plusOne;
			current = plusTwo;
			plusOne = plusThree;
			plusTwo = plusFour;
			//update P1s
			minusThree_P1 = minusOne_P1;
			minusOne_P1 = plusOne_P1;
			plusOne_P1 = plusThree_P1;

			//update U1s
			minusTwo_U1 = current_U1;
			current_U1 = plusTwo_U1;

			//update P2s
			minusOne_P2 = plusOne_P2;
		}

		//4. transform vertically
		currentScratch = scratch + getScratchOffset();

		localMemoryFence();
		// P2 - predict odd columns (skip left three boundary columns and all right boundary columns)
		if ( doP2 ) {
			for (int j = 0; j < WIN_SIZE_X; j++) {
				float4 minusOne = readPixel(currentScratch -1);
				float4 plusOne = readPixel(currentScratch);
				float4 plusThree = readPixel(currentScratch + 1);

				float4 minusTwo, current,plusTwo, plusFour;

				minusTwo.x = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.x  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.x  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.x = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.y = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.y  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.y  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.y = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.z = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.z  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.z  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.z = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.w = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.w  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.w  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.w = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch -= CHANNEL_BUFFER_SIZE_X3;

				float4 current_U1 = current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo);

				// write P2
				writePixel( scale97Mul*(plusOne + P1*(current + plusTwo) +
		         				      P2*(current_U1 + plusTwo + U1*(plusOne + plusThree) +
									  U1P1*(current + 2*plusTwo + plusFour)  )),
									  currentScratch);
				// write U1, for use by even loop
				writePixel(current_U1, currentScratch + VERTICAL_ODD_TO_PREVIOUS_EVEN);

				currentScratch += HORIZONTAL_STRIDE;
			}
		}

		currentScratch = scratch + getScratchOffset();
		localMemoryFence();
		//U2 - update even columns (skip left and right boundary columns)
		if ( doU2 ) {
			for (int j = 0; j < WIN_SIZE_X; j++) {

				float4 current = readPixel(currentScratch);

				// read previous and next odd
				float4 prevOdd, nextOdd;

				prevOdd.x = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.x  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.y = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.y  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.z = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.z  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.w = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.w  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch -= CHANNEL_BUFFER_SIZE_X3;
				//////////////////////////////////////////////////////////////////

				// write U2
				writePixel( scale97Div*(current + U2*(prevOdd + nextOdd)), currentScratch);
				currentScratch += HORIZONTAL_STRIDE;
			}
		}
		localMemoryFence();

		//5. write local buffer column to destination image
		// (only write non-boundary columns that are within the image bounds)
		if (writeRow) {
		   if (inputY &1)
			   writeRowToOutput(scratch + getScratchOffset(), odata, odata, firstX, outputY, width, halfWidth);
			else
			   writeRowToOutput(scratch + getScratchOffset(), odataLL, odata, firstX, outputY, width, halfWidth);

		}
		// move to next step
		firstX += WIN_SIZE_X;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// DWT with Quantization
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// write quantized low and high bands (relative to horizontal axis)
void writeQuantizedRowToOutput(LOCAL float* restrict currentScratch, write_only image2d_t odata,
													unsigned int firstX, unsigned int outputY, unsigned int width, unsigned int halfWidth,
													 const float quantLow, const float quantHigh){

	int2 posOut = {firstX>>1, outputY};
	for (int j = 0; j < WIN_SIZE_X; j+=2) {

	    // even column
		//only need to check evens, since even point will be the first out of bound point
	    if (posOut.x >= halfWidth)
			break;

		write_imagei(odata, posOut, convert_int4_rtz(ceil(quantLow * readPixel(currentScratch))) );

		// odd column
		currentScratch += HORIZONTAL_STRIDE ;
		posOut.x+= halfWidth;

		write_imagei(odata, posOut, convert_int4_rtz(ceil(quantHigh * readPixel(currentScratch))) );

		currentScratch += HORIZONTAL_STRIDE;
		posOut.x -= (halfWidth - 1);
	}
}

// write low and high bands (relative to horizontal axis)
// low band is not quantized, but high band is
// odata is integer buffer (use quantization), while odataLL is float buffer (no quantization)
void writeMixedQuantizedRowToOutput(LOCAL float* restrict currentScratch, write_only image2d_t odataLL,
										 write_only image2d_t odata, unsigned int firstX, unsigned int outputY, unsigned int width, unsigned int halfWidth,
										  const float quantLow, const float quantHigh){

	int2 posOut = {firstX>>1, outputY};
	for (int j = 0; j < WIN_SIZE_X; j+=2) {

	    // even column
		//only need to check evens, since even point will be the first out of bound point
	    if (posOut.x >= halfWidth)
			break;

		write_imagef(odataLL, posOut,quantLow * readPixel(currentScratch));

		// odd column
		currentScratch += HORIZONTAL_STRIDE ;
		posOut.x+= halfWidth;

		write_imagei(odata, posOut, convert_int4_rtz(ceil(quantHigh * readPixel(currentScratch))) );

		currentScratch += HORIZONTAL_STRIDE;
		posOut.x -= (halfWidth - 1);
	}
}

// assumptions: width and height are both even
// (we will probably have to relax these assumptions in the future)
void KERNEL runWithQuantization(read_only image2d_t idata,  write_only image2d_t odataLL, write_only image2d_t odata,
                       const unsigned int  width, const unsigned int height, const unsigned int steps,
					   const unsigned int  level, const unsigned int levels,
					   const float quantLL, const float quantLH, const float quantHH) {

	int inputY = getCorrectedGlobalIdY();
	int outputY = -1;
	if (inputY < height && inputY >= 0)
	    outputY = (inputY >> 1) + (inputY & 1)*( height >> 1);

	bool writeRow = ((getLocalId(1) >= BOUNDARY_Y) && ( getLocalId(1) < WIN_SIZE_Y - BOUNDARY_Y) && outputY != -1);
	bool doP2 = false;
	bool doU2 = false;
	if (getLocalId(1)&1)
		doP2 = (getLocalId(1) >= BOUNDARY_Y-1) && (getLocalId(1) < WIN_SIZE_Y-BOUNDARY_Y);
	else
		doU2 = (getLocalId(1) >= BOUNDARY_Y) && (getLocalId(1) < WIN_SIZE_Y-BOUNDARY_Y) ;

    bool oddInputY = inputY &1;
	const float quantLow = (oddInputY ? quantLH : quantLL) * scale97Div;
	const float quantHigh = (oddInputY ? quantHH : quantLH) * scale97Mul;

    const unsigned int halfWidth = width >> 1;
	LOCAL float scratch[PIXEL_BUFFER_SIZE];
	const float xDelta = 1.0/(width-1);
	int firstX = getGlobalId(0) * (steps * WIN_SIZE_X);

	//0. Initialize: fetch first pixel (and 2 top boundary pixels)

	// read -4 point
	float2 posIn = (float2)(firstX-4, inputY) /  (float2)(width-1, height-1);
	float4 minusFour = read_imagef(idata, sampler, posIn);

	posIn.x += xDelta;
	float4 minusThree = read_imagef(idata, sampler, posIn);

	// read -2 point
	posIn.x += xDelta;
	float4 minusTwo = read_imagef(idata, sampler, posIn);

	// read -1 point
	posIn.x += xDelta;
	float4 minusOne = read_imagef(idata, sampler, posIn);

	// read 0 point
	posIn.x += xDelta;
	float4 current = read_imagef(idata, sampler, posIn);

	// +1 point
	posIn.x += xDelta;
	float4 plusOne = read_imagef(idata, sampler, posIn);

	// +2 point
	posIn.x += xDelta;
	float4 plusTwo = read_imagef(idata, sampler, posIn);

	float4 minusThree_P1 = minusThree + P1*(minusFour + minusTwo);
	float4 minusOne_P1   = minusOne   + P1*(minusTwo + current);
	float4 plusOne_P1    = plusOne    + P1*(current + plusTwo);

	float4 minusTwo_U1 = minusTwo + U1*(minusThree_P1 + minusOne_P1);
	float4 current_U1  = current + U1*(minusOne_P1 + plusOne_P1);
	float4 minusOne_P2 = minusOne_P1 + P2*(minusTwo_U1 + current_U1);

	for (int i = 0; i < steps; ++i) {

		// 1. read from source image, transform rows, and store in local scratch
		LOCAL float* currentScratch = scratch + getScratchOffset();
		for (int j = 0; j < WIN_SIZE_X; j+=2) {

	        //read next two points

			// +3 point
			posIn.x += xDelta;
			float4 plusThree = read_imagef(idata, sampler, posIn);

	   		// +4 point
			posIn.x += xDelta;
	   		if (posIn.x > 1 + 3*xDelta)
				break;
			float4 plusFour = read_imagef(idata, sampler, posIn);

			float4 plusThree_P1    = plusThree  + P1*(plusTwo + plusFour);
			float4 plusTwo_U1      = plusTwo + U1*(plusOne_P1 + plusThree_P1);
			float4 plusOne_P2      = plusOne_P1 + P2*(current_U1 + plusTwo_U1);

			//write current U2 (even)
			writePixel(scale97Div * (current_U1 +  U2 * (minusOne_P2 + plusOne_P2)), currentScratch);

			//advance scratch pointer
			currentScratch += HORIZONTAL_STRIDE;

			//write current P2 (odd)
			writePixel(scale97Mul* plusOne_P2 , currentScratch);

			//advance scratch pointer
			currentScratch += HORIZONTAL_STRIDE;

			// shift registers up by two
			minusFour = minusTwo;
			minusThree = minusOne;
			minusTwo = current;
			minusOne = plusOne;
			current = plusTwo;
			plusOne = plusThree;
			plusTwo = plusFour;
			//update P1s
			minusThree_P1 = minusOne_P1;
			minusOne_P1 = plusOne_P1;
			plusOne_P1 = plusThree_P1;

			//update U1s
			minusTwo_U1 = current_U1;
			current_U1 = plusTwo_U1;

			//update P2s
			minusOne_P2 = plusOne_P2;
		}

		//4. transform vertically
		currentScratch = scratch + getScratchOffset();

		localMemoryFence();
		// P2 - predict odd columns (skip left three boundary columns and all right boundary columns)
		if ( doP2 ) {
			for (int j = 0; j < WIN_SIZE_X; j++) {
				float4 minusOne = readPixel(currentScratch -1);
				float4 plusOne = readPixel(currentScratch);
				float4 plusThree = readPixel(currentScratch + 1);

				float4 minusTwo, current,plusTwo, plusFour;

				minusTwo.x = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.x  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.x  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.x = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.y = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.y  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.y  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.y = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.z = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.z  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.z  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.z = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.w = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.w  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.w  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.w = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch -= CHANNEL_BUFFER_SIZE_X3;

				float4 current_U1 = current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo);

				// write P2
				writePixel( scale97Mul*(plusOne + P1*(current + plusTwo) +
		         				      P2*(current_U1 + plusTwo + U1*(plusOne + plusThree) +
									  U1P1*(current + 2*plusTwo + plusFour)  )),
									  currentScratch);
				// write U1, for use by even loop
				writePixel(current_U1, currentScratch + VERTICAL_ODD_TO_PREVIOUS_EVEN);

				currentScratch += HORIZONTAL_STRIDE;
			}
		}

		currentScratch = scratch + getScratchOffset();
		localMemoryFence();
		//U2 - update even columns (skip left and right boundary columns)
		if ( doU2 ) {
			for (int j = 0; j < WIN_SIZE_X; j++) {

				float4 current = readPixel(currentScratch);

				// read previous and next odd
				float4 prevOdd, nextOdd;

				prevOdd.x = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.x  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.y = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.y  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.z = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.z  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.w = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.w  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch -= CHANNEL_BUFFER_SIZE_X3;
				//////////////////////////////////////////////////////////////////

				// write U2
				writePixel( scale97Div*(current + U2*(prevOdd + nextOdd)), currentScratch);
				currentScratch += HORIZONTAL_STRIDE;
			}
		}
		localMemoryFence();

		//5. write local buffer column to destination image
		if (writeRow) {

		   if (oddInputY || (level == levels-1) )
			   writeQuantizedRowToOutput(scratch + getScratchOffset(), odata, firstX, outputY,
			                            width, halfWidth, quantLow, quantHigh);
			else
			   writeMixedQuantizedRowToOutput(scratch + getScratchOffset(), odataLL, odata, firstX, outputY,
										width, halfWidth, quantLow, quantHigh);

		}
		// move to next step
		firstX += WIN_SIZE_X;
	}
}

/*  Copyright 2014 Aaron Boxer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "ocl_platform.cl"

//////////////////////////
// dimensions of window
// WIN_SIZE_X
// WIN_SIZE_Y assume this equals number of work items in work group
///////////////////////////

//  scratch buffer (in local memory of GPU) where block of input image is stored.
//  All operations expect WIN_SIZE_Y threads.

 /**

Layout for scratch buffer

Odd and even rows are separated. (Generates less bank conflicts when using lifting scheme.)
All even rows are stored first, then all odd rows.

Left (even) boundary row
Even rows
Right (even) boundary row
Left (odd) boundary row
Odd rows
Right (odd) boundary row

 **/

#define BOUNDARY_Y 4

#define HORIZONTAL_STRIDE 64  // WIN_SIZE_X/2

// two vertical neighbours: pointer diff:
#define BUFFER_SIZE            512	// HORIZONTAL_STRIDE * WIN_SIZE_X

#define CHANNEL_BUFFER_SIZE     1024            // BUFFER_SIZE + BUFFER_SIZE

#define CHANNEL_BUFFER_SIZE_X2  2048
#define CHANNEL_BUFFER_SIZE_X3  3072

#define PIXEL_BUFFER_SIZE   4096

#define VERTICAL_EVEN_TO_PREVIOUS_ODD  511
#define VERTICAL_EVEN_TO_NEXT_ODD      512

#define VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE -513
#define VERTICAL_ODD_TO_PREVIOUS_EVEN -512
#define VERTICAL_ODD_TO_NEXT_EVEN     -511
#define VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE     -510

CONSTANT float P1 = -1.586134342;   ///< forward 9/7 predict 1
CONSTANT float U1 = -0.05298011854;  ///< forward 9/7 update 1
CONSTANT float P2 = 0.8829110762;   ///< forward 9/7 predict 2
CONSTANT float U2 = 0.4435068522;    ///< forward 9/7 update 2
CONSTANT float U1P1 = 0.08403358545952490068;

CONSTANT float scale97Mul = 1.23017410491400f;
CONSTANT float scale97Div = 1.0 / 1.23017410491400f;

/*

Lifting scheme consists of four steps: Predict1 Update1 Predict2 Update2
followed by scaling (even points are scaled by scale97Div, odd points are scaled by scale97Mul)

Predict Calculation:

For S odd, we have:

plusOne_P1 = plusOne + P1*(current + plusTwo)

plusOne_P2 = plusOne_P1 +
             P2*(current_U1 + plusTwo_U1)
           =   plusOne + P1*(current + plusTwo) +
		         P2*(current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo) +
				     plusTwo + U1*(plusOne + plusThree) + U1P1*(current + 2*plusTwo + plusFour)  )

Update Calculation

For S even, we have

current_U1 = current + U1*(minusOne_P1 + plusOne_P1)
           = current + U1*(minusOne + P1*(minusTwo + current) + plusOne + P1*(current + plusTwo))
		   = current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo)

current_U2 = current_U1 + U2*(minusOne_P2 + plusOne_P2)

*/

/*
Quantization:

CONSTANT float norms[4][6] = {
	{1.000, 1.965, 4.177, 8.403, 16.90, 33.84},
	{2.022, 3.989, 8.355, 17.04, 34.27, 68.63},
	{2.022, 3.989, 8.355, 17.04, 34.27, 68.63},
	{2.080, 3.865, 8.307, 17.18, 34.71, 69.59}
};

Each resolution has four bands LL LH HL HH
Gain for the bands:            0  1  1  2
Number of bits per sample      precision + gain
base_stepsize                  (1 << (gain)) / norm

void opj_dwt_calc_explicit_stepsizes(int numresolutions, int prec) {
	int numbands, bandno;
	numbands = 3 * numresolutions - 2;
	for (bandno = 0; bandno < numbands; bandno) {

		float stepsize;
		int resno, level, orient, gain;

		resno = (bandno == 0) ? 0 : ((bandno - 1) / 3 + 1);
		orient = (bandno == 0) ? 0 : ((bandno - 1) % 3 + 1);
		level = numresolutions - 1 - resno;
		gain = (orient == 0) ? 0 : (((orient == 1) || (orient == 2)) ? 1 : 2);
		float norm = norms[orient][level];
		base_stepsize = floor(((1 << (gain)) / norm) * 8192);
		int p = int_floorlog2(base_stepsize) - 13;
		int n = 11 - int_floorlog2(base_stepsize);
		mant = (n < 0 ? stepsize >> -n : base_stepsize << n) & 0x7ff;
		expn = numbps - p;
		stepsize = (1.0 + mant / 2048.0) * pow(2.0, (numbps - expn));
	}
}

//Get logarithm of an integer and round downwards
//@return Returns log2(a)
int int_floorlog2(OPJ_INT32 a) {
	int l;
	for (l = 0; a > 1; l) {
		a >>= 1;
	}
	return l;
}

*/

///////////////////////////////////////////////////////////////////////

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

inline int getCorrectedGlobalIdY() {
      return getGlobalId(1) - 2 * BOUNDARY_Y * getGroupId(1);
}

// read pixel from local buffer
inline float4 readPixel( LOCAL float*  restrict  src) {
	return (float4)(*src, *(src+CHANNEL_BUFFER_SIZE),  *(src+CHANNEL_BUFFER_SIZE_X2),  *(src+CHANNEL_BUFFER_SIZE_X3)) ;
}

//write pixel to column
inline void writePixel(float4 pix, LOCAL float*  restrict  dest) {
	*dest = pix.x;
	dest += CHANNEL_BUFFER_SIZE;
	*dest = pix.y;
	dest += CHANNEL_BUFFER_SIZE;
	*dest = pix.z;
	dest += CHANNEL_BUFFER_SIZE;
	*dest = pix.w;
}

// write row to destination
void writeRowToOutput(LOCAL float* restrict currentScratch, write_only image2d_t odataLL, write_only image2d_t odata,
																		unsigned int firstX, unsigned int outputY, unsigned int width, unsigned int halfWidth){

	int2 posOut = {firstX>>1, outputY};
	for (int j = 0; j < WIN_SIZE_X; j+=2) {

	    // even column
		//only need to check evens, since even point will be the first out of bound point
	    if (posOut.x >= halfWidth)
			break;

		write_imagef(odataLL, posOut,scale97Div * readPixel(currentScratch));

		// odd column
		currentScratch += HORIZONTAL_STRIDE ;
		posOut.x+= halfWidth;

		write_imagef(odata, posOut,scale97Mul * readPixel(currentScratch));

		currentScratch += HORIZONTAL_STRIDE;
		posOut.x -= (halfWidth - 1);
	}
}

// initial scratch offset when transforming horizontally
inline unsigned int getScratchOffset(){
   return (getLocalId(1)>> 1) + (getLocalId(1)&1) * BUFFER_SIZE;
}

// assumptions: width and height are both even
// (we will probably have to relax these assumptions in the future)
void KERNEL run(read_only image2d_t idata, write_only image2d_t odataLL, write_only image2d_t odata,
                       const unsigned int  width, const unsigned int  height, const unsigned int steps) {

	int inputY = getCorrectedGlobalIdY();
	int outputY = -1;
	if (inputY < height && inputY >= 0)
	    outputY = (inputY >> 1) + (inputY & 1)*( height >> 1);

	bool writeRow = ((getLocalId(1) >= BOUNDARY_Y) && ( getLocalId(1) < WIN_SIZE_Y - BOUNDARY_Y) && outputY != -1);
	bool doP2 = false;
	bool doU2 = false;
	if (getLocalId(1)&1)
		doP2 = (getLocalId(1) >= BOUNDARY_Y-1) && (getLocalId(1) < WIN_SIZE_Y-BOUNDARY_Y);
	else
		doU2 = (getLocalId(1) >= BOUNDARY_Y) && (getLocalId(1) < WIN_SIZE_Y-BOUNDARY_Y) ;

    const unsigned int halfWidth = width >> 1;
	LOCAL float scratch[PIXEL_BUFFER_SIZE];
	const float xDelta = 1.0/(width-1);
	int firstX = getGlobalId(0) * (steps * WIN_SIZE_X);

	//0. Initialize: fetch first pixel (and 2 top boundary pixels)

	// read -4 point
	float2 posIn = (float2)(firstX-4, inputY) /  (float2)(width-1, height-1);
	float4 minusFour = read_imagef(idata, sampler, posIn);

	posIn.x += xDelta;
	float4 minusThree = read_imagef(idata, sampler, posIn);

	// read -2 point
	posIn.x += xDelta;
	float4 minusTwo = read_imagef(idata, sampler, posIn);

	// read -1 point
	posIn.x += xDelta;
	float4 minusOne = read_imagef(idata, sampler, posIn);

	// read 0 point
	posIn.x += xDelta;
	float4 current = read_imagef(idata, sampler, posIn);

	// +1 point
	posIn.x += xDelta;
	float4 plusOne = read_imagef(idata, sampler, posIn);

	// +2 point
	posIn.x += xDelta;
	float4 plusTwo = read_imagef(idata, sampler, posIn);

	float4 minusThree_P1 = minusThree + P1*(minusFour + minusTwo);
	float4 minusOne_P1   = minusOne   + P1*(minusTwo + current);
	float4 plusOne_P1    = plusOne    + P1*(current + plusTwo);

	float4 minusTwo_U1 = minusTwo + U1*(minusThree_P1 + minusOne_P1);
	float4 current_U1  = current + U1*(minusOne_P1 + plusOne_P1);
	float4 minusOne_P2 = minusOne_P1 + P2*(minusTwo_U1 + current_U1);

	for (int i = 0; i < steps; ++i) {

		// 1. read from source image, transform rows, and store in local scratch
		LOCAL float* currentScratch = scratch + getScratchOffset();
		for (int j = 0; j < WIN_SIZE_X; j+=2) {

	        //read next two points

			// +3 point
			posIn.x += xDelta;
			float4 plusThree = read_imagef(idata, sampler, posIn);

	   		// +4 point
			posIn.x += xDelta;
	   		if (posIn.x > 1 + 3*xDelta)
				break;
			float4 plusFour = read_imagef(idata, sampler, posIn);

			float4 plusThree_P1    = plusThree  + P1*(plusTwo + plusFour);
			float4 plusTwo_U1      = plusTwo + U1*(plusOne_P1 + plusThree_P1);
			float4 plusOne_P2      = plusOne_P1 + P2*(current_U1 + plusTwo_U1);

			//write current U2 (even)
			writePixel(scale97Div * (current_U1 +  U2 * (minusOne_P2 + plusOne_P2)), currentScratch);

			//advance scratch pointer
			currentScratch += HORIZONTAL_STRIDE;

			//write current P2 (odd)
			writePixel(scale97Mul* plusOne_P2 , currentScratch);

			//advance scratch pointer
			currentScratch += HORIZONTAL_STRIDE;

			// shift registers up by two
			minusFour = minusTwo;
			minusThree = minusOne;
			minusTwo = current;
			minusOne = plusOne;
			current = plusTwo;
			plusOne = plusThree;
			plusTwo = plusFour;
			//update P1s
			minusThree_P1 = minusOne_P1;
			minusOne_P1 = plusOne_P1;
			plusOne_P1 = plusThree_P1;

			//update U1s
			minusTwo_U1 = current_U1;
			current_U1 = plusTwo_U1;

			//update P2s
			minusOne_P2 = plusOne_P2;
		}

		//4. transform vertically
		currentScratch = scratch + getScratchOffset();

		localMemoryFence();
		// P2 - predict odd columns (skip left three boundary columns and all right boundary columns)
		if ( doP2 ) {
			for (int j = 0; j < WIN_SIZE_X; j++) {
				float4 minusOne = readPixel(currentScratch -1);
				float4 plusOne = readPixel(currentScratch);
				float4 plusThree = readPixel(currentScratch + 1);

				float4 minusTwo, current,plusTwo, plusFour;

				minusTwo.x = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.x  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.x  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.x = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.y = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.y  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.y  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.y = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.z = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.z  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.z  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.z = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.w = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.w  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.w  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.w = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch -= CHANNEL_BUFFER_SIZE_X3;

				float4 current_U1 = current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo);

				// write P2
				writePixel( scale97Mul*(plusOne + P1*(current + plusTwo) +
		         				      P2*(current_U1 + plusTwo + U1*(plusOne + plusThree) +
									  U1P1*(current + 2*plusTwo + plusFour)  )),
									  currentScratch);
				// write U1, for use by even loop
				writePixel(current_U1, currentScratch + VERTICAL_ODD_TO_PREVIOUS_EVEN);

				currentScratch += HORIZONTAL_STRIDE;
			}
		}

		currentScratch = scratch + getScratchOffset();
		localMemoryFence();
		//U2 - update even columns (skip left and right boundary columns)
		if ( doU2 ) {
			for (int j = 0; j < WIN_SIZE_X; j++) {

				float4 current = readPixel(currentScratch);

				// read previous and next odd
				float4 prevOdd, nextOdd;

				prevOdd.x = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.x  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.y = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.y  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.z = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.z  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.w = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.w  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch -= CHANNEL_BUFFER_SIZE_X3;
				//////////////////////////////////////////////////////////////////

				// write U2
				writePixel( scale97Div*(current + U2*(prevOdd + nextOdd)), currentScratch);
				currentScratch += HORIZONTAL_STRIDE;
			}
		}
		localMemoryFence();

		//5. write local buffer column to destination image
		// (only write non-boundary columns that are within the image bounds)
		if (writeRow) {
		   if (inputY &1)
			   writeRowToOutput(scratch + getScratchOffset(), odata, odata, firstX, outputY, width, halfWidth);
			else
			   writeRowToOutput(scratch + getScratchOffset(), odataLL, odata, firstX, outputY, width, halfWidth);

		}
		// move to next step
		firstX += WIN_SIZE_X;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// DWT with Quantization
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// write quantized low and high bands (relative to horizontal axis)
void writeQuantizedRowToOutput(LOCAL float* restrict currentScratch, write_only image2d_t odata,
													unsigned int firstX, unsigned int outputY, unsigned int width, unsigned int halfWidth,
													 const float quantLow, const float quantHigh){

	int2 posOut = {firstX>>1, outputY};
	for (int j = 0; j < WIN_SIZE_X; j+=2) {

	    // even column
		//only need to check evens, since even point will be the first out of bound point
	    if (posOut.x >= halfWidth)
			break;

		write_imagei(odata, posOut, convert_int4_rtz(ceil(quantLow * readPixel(currentScratch))) );

		// odd column
		currentScratch += HORIZONTAL_STRIDE ;
		posOut.x+= halfWidth;

		write_imagei(odata, posOut, convert_int4_rtz(ceil(quantHigh * readPixel(currentScratch))) );

		currentScratch += HORIZONTAL_STRIDE;
		posOut.x -= (halfWidth - 1);
	}
}

// write low and high bands (relative to horizontal axis)
// low band is not quantized, but high band is
// odata is integer buffer (use quantization), while odataLL is float buffer (no quantization)
void writeMixedQuantizedRowToOutput(LOCAL float* restrict currentScratch, write_only image2d_t odataLL,
										 write_only image2d_t odata, unsigned int firstX, unsigned int outputY, unsigned int width, unsigned int halfWidth,
										  const float quantLow, const float quantHigh){

	int2 posOut = {firstX>>1, outputY};
	for (int j = 0; j < WIN_SIZE_X; j+=2) {

	    // even column
		//only need to check evens, since even point will be the first out of bound point
	    if (posOut.x >= halfWidth)
			break;

		write_imagef(odataLL, posOut,quantLow * readPixel(currentScratch));

		// odd column
		currentScratch += HORIZONTAL_STRIDE ;
		posOut.x+= halfWidth;

		write_imagei(odata, posOut, convert_int4_rtz(ceil(quantHigh * readPixel(currentScratch))) );

		currentScratch += HORIZONTAL_STRIDE;
		posOut.x -= (halfWidth - 1);
	}
}

// assumptions: width and height are both even
// (we will probably have to relax these assumptions in the future)
void KERNEL runWithQuantization(read_only image2d_t idata,  write_only image2d_t odataLL, write_only image2d_t odata,
                       const unsigned int  width, const unsigned int height, const unsigned int steps,
					   const unsigned int  level, const unsigned int levels,
					   const float quantLL, const float quantLH, const float quantHH) {

	int inputY = getCorrectedGlobalIdY();
	int outputY = -1;
	if (inputY < height && inputY >= 0)
	    outputY = (inputY >> 1) + (inputY & 1)*( height >> 1);

	bool writeRow = ((getLocalId(1) >= BOUNDARY_Y) && ( getLocalId(1) < WIN_SIZE_Y - BOUNDARY_Y) && outputY != -1);
	bool doP2 = false;
	bool doU2 = false;
	if (getLocalId(1)&1)
		doP2 = (getLocalId(1) >= BOUNDARY_Y-1) && (getLocalId(1) < WIN_SIZE_Y-BOUNDARY_Y);
	else
		doU2 = (getLocalId(1) >= BOUNDARY_Y) && (getLocalId(1) < WIN_SIZE_Y-BOUNDARY_Y) ;

    bool oddInputY = inputY &1;
	const float quantLow = (oddInputY ? quantLH : quantLL) * scale97Div;
	const float quantHigh = (oddInputY ? quantHH : quantLH) * scale97Mul;

    const unsigned int halfWidth = width >> 1;
	LOCAL float scratch[PIXEL_BUFFER_SIZE];
	const float xDelta = 1.0/(width-1);
	int firstX = getGlobalId(0) * (steps * WIN_SIZE_X);

	//0. Initialize: fetch first pixel (and 2 top boundary pixels)

	// read -4 point
	float2 posIn = (float2)(firstX-4, inputY) /  (float2)(width-1, height-1);
	float4 minusFour = read_imagef(idata, sampler, posIn);

	posIn.x += xDelta;
	float4 minusThree = read_imagef(idata, sampler, posIn);

	// read -2 point
	posIn.x += xDelta;
	float4 minusTwo = read_imagef(idata, sampler, posIn);

	// read -1 point
	posIn.x += xDelta;
	float4 minusOne = read_imagef(idata, sampler, posIn);

	// read 0 point
	posIn.x += xDelta;
	float4 current = read_imagef(idata, sampler, posIn);

	// +1 point
	posIn.x += xDelta;
	float4 plusOne = read_imagef(idata, sampler, posIn);

	// +2 point
	posIn.x += xDelta;
	float4 plusTwo = read_imagef(idata, sampler, posIn);

	float4 minusThree_P1 = minusThree + P1*(minusFour + minusTwo);
	float4 minusOne_P1   = minusOne   + P1*(minusTwo + current);
	float4 plusOne_P1    = plusOne    + P1*(current + plusTwo);

	float4 minusTwo_U1 = minusTwo + U1*(minusThree_P1 + minusOne_P1);
	float4 current_U1  = current + U1*(minusOne_P1 + plusOne_P1);
	float4 minusOne_P2 = minusOne_P1 + P2*(minusTwo_U1 + current_U1);

	for (int i = 0; i < steps; ++i) {

		// 1. read from source image, transform rows, and store in local scratch
		LOCAL float* currentScratch = scratch + getScratchOffset();
		for (int j = 0; j < WIN_SIZE_X; j+=2) {

	        //read next two points

			// +3 point
			posIn.x += xDelta;
			float4 plusThree = read_imagef(idata, sampler, posIn);

	   		// +4 point
			posIn.x += xDelta;
	   		if (posIn.x > 1 + 3*xDelta)
				break;
			float4 plusFour = read_imagef(idata, sampler, posIn);

			float4 plusThree_P1    = plusThree  + P1*(plusTwo + plusFour);
			float4 plusTwo_U1      = plusTwo + U1*(plusOne_P1 + plusThree_P1);
			float4 plusOne_P2      = plusOne_P1 + P2*(current_U1 + plusTwo_U1);

			//write current U2 (even)
			writePixel(scale97Div * (current_U1 +  U2 * (minusOne_P2 + plusOne_P2)), currentScratch);

			//advance scratch pointer
			currentScratch += HORIZONTAL_STRIDE;

			//write current P2 (odd)
			writePixel(scale97Mul* plusOne_P2 , currentScratch);

			//advance scratch pointer
			currentScratch += HORIZONTAL_STRIDE;

			// shift registers up by two
			minusFour = minusTwo;
			minusThree = minusOne;
			minusTwo = current;
			minusOne = plusOne;
			current = plusTwo;
			plusOne = plusThree;
			plusTwo = plusFour;
			//update P1s
			minusThree_P1 = minusOne_P1;
			minusOne_P1 = plusOne_P1;
			plusOne_P1 = plusThree_P1;

			//update U1s
			minusTwo_U1 = current_U1;
			current_U1 = plusTwo_U1;

			//update P2s
			minusOne_P2 = plusOne_P2;
		}

		//4. transform vertically
		currentScratch = scratch + getScratchOffset();

		localMemoryFence();
		// P2 - predict odd columns (skip left three boundary columns and all right boundary columns)
		if ( doP2 ) {
			for (int j = 0; j < WIN_SIZE_X; j++) {
				float4 minusOne = readPixel(currentScratch -1);
				float4 plusOne = readPixel(currentScratch);
				float4 plusThree = readPixel(currentScratch + 1);

				float4 minusTwo, current,plusTwo, plusFour;

				minusTwo.x = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.x  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.x  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.x = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.y = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.y  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.y  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.y = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.z = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.z  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.z  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.z = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch += CHANNEL_BUFFER_SIZE;
				minusTwo.w = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN_MINUS_ONE];
				current.w  = currentScratch[VERTICAL_ODD_TO_PREVIOUS_EVEN];
				plusTwo.w  = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN];
				plusFour.w = currentScratch[VERTICAL_ODD_TO_NEXT_EVEN_PLUS_ONE];

				currentScratch -= CHANNEL_BUFFER_SIZE_X3;

				float4 current_U1 = current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo);

				// write P2
				writePixel( scale97Mul*(plusOne + P1*(current + plusTwo) +
		         				      P2*(current_U1 + plusTwo + U1*(plusOne + plusThree) +
									  U1P1*(current + 2*plusTwo + plusFour)  )),
									  currentScratch);
				// write U1, for use by even loop
				writePixel(current_U1, currentScratch + VERTICAL_ODD_TO_PREVIOUS_EVEN);

				currentScratch += HORIZONTAL_STRIDE;
			}
		}

		currentScratch = scratch + getScratchOffset();
		localMemoryFence();
		//U2 - update even columns (skip left and right boundary columns)
		if ( doU2 ) {
			for (int j = 0; j < WIN_SIZE_X; j++) {

				float4 current = readPixel(currentScratch);

				// read previous and next odd
				float4 prevOdd, nextOdd;

				prevOdd.x = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.x  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.y = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.y  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.z = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.z  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch += CHANNEL_BUFFER_SIZE;
				prevOdd.w = currentScratch[VERTICAL_EVEN_TO_PREVIOUS_ODD];
				nextOdd.w  = currentScratch[VERTICAL_EVEN_TO_NEXT_ODD];

				currentScratch -= CHANNEL_BUFFER_SIZE_X3;
				//////////////////////////////////////////////////////////////////

				// write U2
				writePixel( scale97Div*(current + U2*(prevOdd + nextOdd)), currentScratch);
				currentScratch += HORIZONTAL_STRIDE;
			}
		}
		localMemoryFence();

		//5. write local buffer column to destination image
		if (writeRow) {

		   if (oddInputY || (level == levels-1) )
			   writeQuantizedRowToOutput(scratch + getScratchOffset(), odata, firstX, outputY,
			                            width, halfWidth, quantLow, quantHigh);
			else
			   writeMixedQuantizedRowToOutput(scratch + getScratchOffset(), odataLL, odata, firstX, outputY,
										width, halfWidth, quantLow, quantHigh);

		}
		// move to next step
		firstX += WIN_SIZE_X;
	}
}


working srhd towards grhd

working srhd towards grhd

/*
Font "Numerical Hydrodynamics and Magnetohydrodynamics in General Relativity" 2008
*/

//everything matches the default except the params passed through to calcCellMinMaxEigenvalues
__kernel void calcDT(
	__global real* dtBuf,
	const __global cons_t* UBuf,
	const __global prim_t* primBuf
) {
	SETBOUNDS(0,0);
	if (OOB(2,2)) {
		dtBuf[index] = INFINITY;
		return;
	}

	cons_t U = UBuf[index];
	prim_t prim = primBuf[index];
	real rho = prim.rho;
	real eInt = prim.eInt;
	real vSq = coordLenSq(prim.v);
	real P = calc_P(rho, eInt);
	real h = calc_h(rho, P, eInt);
	real csSq = gamma * P / (rho * h);
	real cs = sqrt(csSq);

	real dt = INFINITY;
	//for (int side = 0; side < dim; ++side) {
	<? for side=0,solver.dim-1 do ?>{
		//for the particular direction
		real vi = prim.v.s<?=side?>;
		real viSq = vi * vi;

		// Marti 1998 eqn 19
		// also Marti & Muller 2008 eqn 68
		// also Font 2008 eqn 106
		const real betaUi = betaU.s<?=side?>;
		real discr = sqrt((1. - vSq) * (gammaUx.x * (1. - vSq * csSq) - viSq * (1. - csSq)));
		real lambdaMin = (vi * (1. - csSq) - cs * discr) / (1. - vSq * csSq) * alpha - betaUi;
		real lambdaMax = (vi * (1. - csSq) + cs * discr) / (1. - vSq * csSq) * alpha - betaUi;
		lambdaMin = min((real)0., lambdaMin);
		lambdaMax = max((real)0., lambdaMax);
		dt = min(dt, dx<?=side?>_at(i) / (fabs(lambdaMax - lambdaMin) + (real)1e-9));
	}<? end ?>

	dtBuf[index] = dt;
}

__kernel void calcEigenBasis(
	__global real* waveBuf,
	__global eigen_t* eigenBuf,
	const __global prim_t* primBuf
) {
	SETBOUNDS(2,1);

	int indexR = index;
	prim_t primR = primBuf[indexR];

	//for (int side = 0; side < dim; ++side) {
	<? for side=0,solver.dim-1 do ?>{
		const int side = <?=side?>;

		int indexL = index - stepsize[side];
		prim_t primL = primBuf[indexL];

<? if true then -- arithmetic averaging ?>
		prim_t avg = (prim_t){
			.rho = .5 * (primL.rho + primR.rho),
			.v = real3_scale(real3_add(primL.v, primR.v), .5),
			.eInt = .5 * (primL.eInt + primR.eInt),
		};
<? -- else -- Roe-averaging, Font 2008 eqn 38 ?>
<? end ?>

		real rho = avg.rho;
		real3 v = avg.v;
		real eInt = avg.eInt;

		<? if side == 1 then ?>
		v = _real3(v.y, -v.x, v.z);	// -90' rotation to put the y axis contents into the x axis
		<? elseif side == 2 then ?>
		v = _real3(v.z, v.y, -v.x);	//-90' rotation to put the z axis in the x axis
		<? end ?>

		real vSq = coordLenSq(v);
		real oneOverW2 = 1. - vSq;
		real oneOverW = sqrt(oneOverW2);
		real W = 1. / oneOverW;
		real W2 = 1. / oneOverW2;
		real P = gamma_1 * rho * eInt;
		real h = 1. + eInt + P / rho;
		real P_over_rho_h = P / (rho * h);

		real hW = h * W;
		real hSq = h * h;

		//TODO check out Font's paper on where the metric coefficients go ...
		real vxSq = v.x * v.x;
		real csSq = gamma * P_over_rho_h;
		real cs = sqrt(csSq);

		const real betaUi = betaU.s<?=side?>;
		real discr = sqrt((1. - vSq) * ((1. - vSq * csSq) - vxSq * (1. - csSq)));
		real lambdaMin = (v.x * (1. - csSq) - cs * discr) / (1. - vSq * csSq) * alpha * alpha - betaUi;
		real lambdaMax = (v.x * (1. - csSq) + cs * discr) / (1. - vSq * csSq) * alpha * alpha - betaUi;

		int intindex = side + dim * index;
		__global real* wave = waveBuf + numWaves * intindex;
		wave[0] = lambdaMin;
		wave[1] = v.x * alpha - betaUi;
		wave[2] = v.x * alpha - betaUi;
		wave[3] = v.x * alpha - betaUi;
		wave[4] = lambdaMax;

		__global eigen_t* eigen = eigenBuf + intindex;

		real3 vL = _real3(
			real3_dot(gamma_x, v),
			real3_dot(gamma_y, v),
			real3_dot(gamma_z, v));

		real LambdaMin = (lambdaMin + betaUi) / alpha;	//2008 Font eqn 114
		real LambdaMax = (lambdaMax + betaUi) / alpha;	//2008 Font eqn 114
		real ATildeMinus = (gammaUx.x - vxSq) / (gammaUx.x - v.x * LambdaMin);	//2008 Font eqn 113
		real ATildePlus  = (gammaUx.x - vxSq) / (gammaUx.x - v.x * LambdaMax);		//2008 Font eqn 113

		real VMinus = (v.x - LambdaMin) / (gammaUx.x - v.x * LambdaMin);	//2008 Font eqn 113
		real VPlus = (v.x - LambdaMax) / (gammaUx.x - v.x * LambdaMax);	//2008 Font eqn 113
		real CMinus = vL.x - VMinus;	//2008 Font eqn 112
		real CPlus = vL.x - VPlus;	//2008 Font eqn 112

		real Kappa = h;		//TODO 2008 Font eqn 112.  Kappa = h approx for ideal gas

		__global real* evR = eigen->evR;
		//min col	2008 Font eqn 111, r-
		evR[0 + numStates * 0] = 1.;
		evR[1 + numStates * 0] = hW * CMinus;
		evR[2 + numStates * 0] = hW * vL.y;
		evR[3 + numStates * 0] = hW * vL.z;
		evR[4 + numStates * 0] = hW * ATildeMinus - 1.;
		//mid col (normal)  2008 Font eqn 108: r0,1
		evR[0 + numStates * 1] = Kappa / hW;
		evR[1 + numStates * 1] = vL.x;
		evR[2 + numStates * 1] = vL.y;
		evR[3 + numStates * 1] = vL.z;
		evR[4 + numStates * 1] = 1. - Kappa / hW;
		//mid col (tangent A)	2008 Font eqn 109: r0,2
		evR[0 + numStates * 2] = W * vL.y;
		evR[1 + numStates * 2] = h * (gamma_y.x + 2. * W2 * vL.y * vL.x);
		evR[2 + numStates * 2] = h * (gamma_y.y + 2. * W2 * vL.y * vL.y);
		evR[3 + numStates * 2] = h * (gamma_y.z + 2. * W2 * vL.y * vL.z);
		evR[4 + numStates * 2] = W * vL.y * (2. * hW - 1.);
		//mid col (tangent B)	2008 Font eqn 110: r0,3
		evR[0 + numStates * 3] = W * vL.z;
		evR[1 + numStates * 3] = h * (gamma_z.x + 2. * W2 * vL.x * vL.z);
		evR[2 + numStates * 3] = h * (gamma_z.y + 2. * W2 * vL.y * vL.z);
		evR[3 + numStates * 3] = h * (gamma_z.z + 2. * W2 * vL.z * vL.z);
		evR[4 + numStates * 3] = W * vL.z * (2. * hW - 1.);
		//max col
		evR[0 + numStates * 4] = 1.;
		evR[1 + numStates * 4] = hW * CPlus;
		evR[2 + numStates * 4] = hW * vL.y;
		evR[3 + numStates * 4] = hW * vL.z;
		evR[4 + numStates * 4] = hW * ATildePlus - 1.;

		real Gamma_xx = gamma_y.y * gamma_z.z - gamma_y.z * gamma_y.z;	//2008 Font eqn 123. looks like gamma gamma^xx
		real Gamma_xy = gamma_y.z * gamma_x.z - gamma_x.y * gamma_z.z;	//2008 Font eqn 123. looks like gamma gamma^xx
		real Gamma_xz = gamma_x.y * gamma_y.z - gamma_x.z * gamma_y.y;	//2008 Font eqn 123. looks like gamma gamma^xx
		real xi = Gamma_xx - gammaDet * vxSq;//2008 Font eqn 121
		real Delta = hSq * hW * (Kappa - 1.) * (CPlus - CMinus) * xi;	//2008 Font eqn 121

		__global real* evL = eigen->evL;
		//min row	2008 Font eqn 118
		real scale;
		scale = hSq / Delta;
#if 1 // working, from SRHD papers Marti & Muller
		evL[0 + numStates * 0] = scale * (hW * ATildePlus * (v.x - lambdaMax) - v.x - W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (v.x - ATildePlus * lambdaMax) + Kappa * ATildePlus * lambdaMax);
		evL[0 + numStates * 1] = scale * (1. + W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (1. - ATildePlus) - Kappa * ATildePlus);
		evL[0 + numStates * 2] = scale * (W2 * v.y * (2. * Kappa - 1.) * ATildePlus * (v.x - lambdaMax));
		evL[0 + numStates * 3] = scale * (W2 * v.z * (2. * Kappa - 1.) * ATildePlus * (v.x - lambdaMax));
		evL[0 + numStates * 4] = scale * (-v.x - W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (v.x - ATildePlus * lambdaMax) + Kappa * ATildePlus * lambdaMax);
#else	//not working, from 2008 Font
		real l5minus = (1 - Kappa) * (-gammaDet * v.x + VPlus * (W2 * xi - Gamma_xx)) - Kappa * W2 * VPlus * xi;
		evL[0 + numStates * 0] = scale * (hW * VPlus * xi + l5minus);
		evL[0 + numStates * 1] = scale * (Gamma_xx * (1 - Kappa * ATildePlus) + (2. * Kappa - 1.) * VPlus * (W2 * v.x * xi - Gamma_xx * v.x));
		evL[0 + numStates * 2] = scale * (Gamma_xy * (1 - Kappa * ATildePlus) + (2. * Kappa - 1.) * VPlus * (W2 * v.y * xi - Gamma_xy * v.x));
		evL[0 + numStates * 3] = scale * (Gamma_xz * (1 - Kappa * ATildePlus) + (2. * Kappa - 1.) * VPlus * (W2 * v.z * xi - Gamma_xz * v.x));
		evL[0 + numStates * 4] = scale * l5minus;
#endif
		//mid normal row	2008 Font eqn 115
		scale = W / (Kappa - 1.);
		evL[1 + numStates * 0] = scale * (h - W);
		evL[1 + numStates * 1] = scale * (W * v.x);
		evL[1 + numStates * 2] = scale * (W * v.y);
		evL[1 + numStates * 3] = scale * (W * v.z);
		evL[1 + numStates * 4] = scale * (-W);
		//mid tangent A row	2008 Font eqn 116
		scale = 1. / (h * xi);
		evL[2 + numStates * 0] = scale * (-gamma_z.z * vL.y + gamma_y.z * vL.z);
		evL[2 + numStates * 1] = scale * v.x * (gamma_z.z * vL.y - gamma_y.z * vL.z);
		evL[2 + numStates * 2] = scale * (gamma_z.z * (1. - v.x * vL.x) + gamma_x.z * vL.z * v.x);
		evL[2 + numStates * 3] = scale * (-gamma_y.z * (1. - vL.x * v.x) - gamma_x.z * vL.y * v.x);
		evL[2 + numStates * 4] = scale * (-gamma_z.z * vL.y + gamma_y.z * vL.z);
		//mid tangent B row	2008 Font eqn 117
		evL[3 + numStates * 0] = scale * (-gamma_y.y * vL.z + gamma_y.z * vL.y);
		evL[3 + numStates * 1] = scale * v.x * (gamma_y.y * vL.z - gamma_y.z * vL.y);
		evL[3 + numStates * 2] = scale * (-gamma_y.z * (1. - vL.x * v.x) - gamma_x.y * vL.z * v.x);
		evL[3 + numStates * 3] = scale * (gamma_y.y * (1. - vL.x * v.x) + gamma_x.y * vL.y * v.x);
		evL[3 + numStates * 4] = scale * (-gamma_y.y * vL.z + gamma_y.z * vL.y);
		//max row	2008 Font eqn 118
		scale = -hSq / Delta;
#if 1 //working, from Marti & Muller
		evL[4 + numStates * 0] = scale * (hW * ATildeMinus * (v.x - lambdaMin) - v.x - W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (v.x - ATildeMinus * lambdaMin) + Kappa * ATildeMinus * lambdaMin);
		evL[4 + numStates * 1] = scale * (1. + W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (1. - ATildeMinus) - Kappa * ATildeMinus);
		evL[4 + numStates * 2] = scale * (W2 * v.y * (2. * Kappa - 1.) * ATildeMinus * (v.x - lambdaMin));
		evL[4 + numStates * 3] = scale * (W2 * v.z * (2. * Kappa - 1.) * ATildeMinus * (v.x - lambdaMin));
		evL[4 + numStates * 4] = scale * (-v.x - W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (v.x - ATildeMinus * lambdaMin) + Kappa * ATildeMinus * lambdaMin);
#else	//not working, from 2008 Font
		real l5plus = (1 - Kappa) * (-gammaDet * v.x + VMinus * (W2 * xi - Gamma_xx)) - Kappa * W2 * VMinus * xi;
		evL[4 + numStates * 0] = scale * (h * W * VMinus * xi - l5plus);
		evL[4 + numStates * 1] = scale * (Gamma_xx * (1 - Kappa * ATildeMinus) + (2. * Kappa - 1.) * VMinus * (W2 * v.x * xi - Gamma_xx * v.x));
		evL[4 + numStates * 2] = scale * (Gamma_xy * (1 - Kappa * ATildeMinus) + (2. * Kappa - 1.) * VMinus * (W2 * v.y * xi - Gamma_xy * v.x));
		evL[4 + numStates * 3] = scale * (Gamma_xz * (1 - Kappa * ATildeMinus) + (2. * Kappa - 1.) * VMinus * (W2 * v.z * xi - Gamma_xz * v.x));
		evL[4 + numStates * 4] = scale * l5plus;
#endif

		<? if side == 1 then ?>
		for (int i = 0; i < numStates; ++i) {
			real tmp;
			//-90' rotation applied to the LHS of incoming velocity vectors, to move their y axis into the x axis
			// is equivalent of a -90' rotation applied to the RHS of the flux jacobian A
			// and A = Q V Q-1 for Q = the right eigenvectors and Q-1 the left eigenvectors
			// so a -90' rotation applied to the RHS of A is a +90' rotation applied to the RHS of Q-1 the left eigenvectors
			//and while a rotation applied to the LHS of a vector rotates the elements of its column vectors, a rotation applied to the RHS rotates the elements of its row vectors
			//each row's y <- x, x <- -y
			tmp = evL[i + numStates * cons_Sx];
			evL[i + numStates * cons_Sx] = -evL[i + numStates * cons_Sy];
			evL[i + numStates * cons_Sy] = tmp;
			//a -90' rotation applied to the RHS of A must be corrected with a 90' rotation on the LHS of A
			//this rotates the elements of the column vectors by 90'
			//each column's x <- y, y <- -x
			tmp = evR[cons_Sx + numStates * i];
			evR[cons_Sx + numStates * i] = -evR[cons_Sy + numStates * i];
			evR[cons_Sy + numStates * i] = tmp;
		}
		<? elseif side == 2 then ?>
		for (int i = 0; i < numStates; ++i) {
			real tmp;
			tmp = evL[i + numStates * cons_Sx];
			evL[i + numStates * cons_Sx] = -evL[i + numStates * cons_Sz];
			evL[i + numStates * cons_Sz] = tmp;
			tmp = evR[cons_Sx + numStates * i];
			evR[cons_Sx + numStates * i] = -evR[cons_Sz + numStates * i];
			evR[cons_Sz + numStates * i] = tmp;
		}
		<? end ?>
	}<? end ?>
}

__kernel void constrainU(
	__global cons_t* UBuf
) {
	SETBOUNDS(0,0);

	__global cons_t* U = UBuf + index;

	U->D = max(U->D, (real)DMin);
	U->tau = max(U->tau, (real)tauMin);

	U->D = min(U->D, (real)DMax);
	U->tau = min(U->tau, (real)tauMax);
}

//TODO update to include alphas, betas, and gammas
__kernel void updatePrims(
	__global prim_t* primBuf,
	const __global cons_t* UBuf
) {
	SETBOUNDS(2,1);

	const __global cons_t* U = UBuf + index;
	real D = U->D;
	real3 S = U->S;
	real tau = U->tau;

	__global prim_t* prim = primBuf + index;
	real3 v = prim->v;

	real SLen = coordLen(S);
	real PMin = max(SLen - tau - D + SLen * solvePrimVelEpsilon, solvePrimPMinEpsilon);
	real PMax = gamma_1 * tau;
	PMax = max(PMax, PMin);
	real P = .5 * (PMin + PMax);

	for (int iter = 0; iter < solvePrimMaxIter; ++iter) {
		real vLen = SLen / (tau + D + P);
		real vSq = vLen * vLen;
		real W = 1. / sqrt(1. - vSq);
		real eInt = (tau + D * (1. - W) + P * (1. - W*W)) / (D * W);
		real rho = D / W;
		real f = gamma_1 * rho * eInt - P;
		real csSq = gamma_1 * (tau + D * (1. - W) + P) / (tau + D + P);
		real df_dP = vSq * csSq - 1.;
		real newP = P - f / df_dP;
		newP = max(newP, PMin);
		real PError = fabs(1. - newP / P);
		P = newP;
		if (PError < solvePrimStopEpsilon) {
			v = real3_scale(S, 1. / (tau + D + P));
			vSq = coordLenSq(v);
			W = 1. / sqrt(1. - vSq);
			rho = D / W;
			rho = max(rho, (real)rhoMin);
			rho = min(rho, (real)rhoMax);
			eInt = P / (rho * gamma_1);
			eInt = min(eInt, (real)eIntMax);
			prim->rho = rho;
			prim->v = v;
			prim->eInt = eInt;
//printf("cell %d finished with prims = %f %f %f\n", index, rho, v.x, eInt);
			return;
		}
	}
}

/*
Font "Numerical Hydrodynamics and Magnetohydrodynamics in General Relativity" 2008
*/

//everything matches the default except the params passed through to calcCellMinMaxEigenvalues
__kernel void calcDT(
	__global real* dtBuf,
	const __global cons_t* UBuf,
	const __global prim_t* primBuf
) {
	SETBOUNDS(0,0);
	if (OOB(2,2)) {
		dtBuf[index] = INFINITY;
		return;
	}

	cons_t U = UBuf[index];
	prim_t prim = primBuf[index];
	real rho = prim.rho;
	real eInt = prim.eInt;
	real vSq = coordLenSq(prim.v);
	real P = calc_P(rho, eInt);
	real h = calc_h(rho, P, eInt);
	real csSq = gamma * P / (rho * h);
	real cs = sqrt(csSq);

	real dt = INFINITY;
	//for (int side = 0; side < dim; ++side) {
	<? for side=0,solver.dim-1 do ?>{
		//for the particular direction
		real vi = prim.v.s<?=side?>;
		real viSq = vi * vi;

		// Marti 1998 eqn 19
		// also Marti & Muller 2008 eqn 68
		// also Font 2008 eqn 106
		const real betaUi = betaU.s<?=side?>;
		real discr = sqrt((1. - vSq) * (gammaUx.x * (1. - vSq * csSq) - viSq * (1. - csSq)));
		real lambdaMin = (vi * (1. - csSq) - cs * discr) / (1. - vSq * csSq) * alpha - betaUi;
		real lambdaMax = (vi * (1. - csSq) + cs * discr) / (1. - vSq * csSq) * alpha - betaUi;
		lambdaMin = min((real)0., lambdaMin);
		lambdaMax = max((real)0., lambdaMax);
		dt = min(dt, dx<?=side?>_at(i) / (fabs(lambdaMax - lambdaMin) + (real)1e-9));
	}<? end ?>

	dtBuf[index] = dt;
}

__kernel void calcEigenBasis(
	__global real* waveBuf,
	__global eigen_t* eigenBuf,
	const __global prim_t* primBuf
) {
	SETBOUNDS(2,1);

	int indexR = index;
	prim_t primR = primBuf[indexR];

	//for (int side = 0; side < dim; ++side) {
	<? for side=0,solver.dim-1 do ?>{
		const int side = <?=side?>;

		int indexL = index - stepsize[side];
		prim_t primL = primBuf[indexL];

<? if true then -- arithmetic averaging ?>
		prim_t avg = (prim_t){
			.rho = .5 * (primL.rho + primR.rho),
			.v = real3_scale(real3_add(primL.v, primR.v), .5),
			.eInt = .5 * (primL.eInt + primR.eInt),
		};
<? -- else -- Roe-averaging, Font 2008 eqn 38 ?>
<? end ?>

		real rho = avg.rho;
		real3 v = avg.v;
		real eInt = avg.eInt;

		<? if side == 1 then ?>
		v = _real3(v.y, -v.x, v.z);	// -90' rotation to put the y axis contents into the x axis
		<? elseif side == 2 then ?>
		v = _real3(v.z, v.y, -v.x);	//-90' rotation to put the z axis in the x axis
		<? end ?>

		real vSq = coordLenSq(v);
		real oneOverW2 = 1. - vSq;
		real oneOverW = sqrt(oneOverW2);
		real W = 1. / oneOverW;
		real W2 = 1. / oneOverW2;
		real P = gamma_1 * rho * eInt;
		real h = 1. + eInt + P / rho;
		real P_over_rho_h = P / (rho * h);

		real hW = h * W;
		real hSq = h * h;

		//TODO check out Font's paper on where the metric coefficients go ...
		real vxSq = v.x * v.x;
		real csSq = gamma * P_over_rho_h;
		real cs = sqrt(csSq);

		const real betaUi = betaU.s<?=side?>;
		real discr = sqrt((1. - vSq) * ((1. - vSq * csSq) - vxSq * (1. - csSq)));
		real lambdaMin = (v.x * (1. - csSq) - cs * discr) / (1. - vSq * csSq) * alpha * alpha - betaUi;
		real lambdaMax = (v.x * (1. - csSq) + cs * discr) / (1. - vSq * csSq) * alpha * alpha - betaUi;

		int intindex = side + dim * index;
		__global real* wave = waveBuf + numWaves * intindex;
		wave[0] = lambdaMin;
		wave[1] = v.x * alpha - betaUi;
		wave[2] = v.x * alpha - betaUi;
		wave[3] = v.x * alpha - betaUi;
		wave[4] = lambdaMax;

		__global eigen_t* eigen = eigenBuf + intindex;

		real LambdaMin = lambdaMin + betaUi / alpha;
		real LambdaMax = lambdaMax + betaUi / alpha;
		real AMinus = (gammaUx.x - vxSq) / (gammaUx.x - v.x * LambdaMin);
		real APlus  = (gammaUx.x - vxSq) / (gammaUx.x - v.x * LambdaMax);
		real CMinus = AMinus * lambdaMin;	//TODO 2008 Font eqn 112
		real CPlus = APlus * lambdaMax;		//TODO 2008 Font eqn 112
		real Kappa = h;		//TODO 2008 Font eqn 112.  Kappa = h approx for ideal gas

		real3 vL = _real3(
			real3_dot(gamma_x, v),
			real3_dot(gamma_y, v),
			real3_dot(gamma_z, v));

		__global real* evR = eigen->evR;
		//min col	2008 Font eqn 111, r-
		evR[0 + numStates * 0] = 1.;
		evR[1 + numStates * 0] = hW * CMinus;
		evR[2 + numStates * 0] = hW * vL.y;
		evR[3 + numStates * 0] = hW * vL.z;
		evR[4 + numStates * 0] = hW * AMinus - 1.;
		//mid col (normal)  2008 Font eqn 108: r0,1
		evR[0 + numStates * 1] = Kappa / hW;
		evR[1 + numStates * 1] = vL.x;
		evR[2 + numStates * 1] = vL.y;
		evR[3 + numStates * 1] = vL.z;
		evR[4 + numStates * 1] = 1. - Kappa / hW;
		//mid col (tangent A)	2008 Font eqn 109: r0,2
		evR[0 + numStates * 2] = W * vL.y;
		evR[1 + numStates * 2] = h * (gamma_y.x + 2. * W2 * vL.y * vL.x);
		evR[2 + numStates * 2] = h * (gamma_y.y + 2. * W2 * vL.y * vL.y);
		evR[3 + numStates * 2] = h * (gamma_y.z + 2. * W2 * vL.y * vL.z);
		evR[4 + numStates * 2] = W * vL.y * (2. * hW - 1.);
		//mid col (tangent B)	2008 Font eqn 110: r0,3
		evR[0 + numStates * 3] = W * vL.z;
		evR[1 + numStates * 3] = h * (gamma_z.x + 2. * W2 * vL.x * vL.z);
		evR[2 + numStates * 3] = h * (gamma_z.y + 2. * W2 * vL.y * vL.z);
		evR[3 + numStates * 3] = h * (gamma_z.z + 2. * W2 * vL.z * vL.z);
		evR[4 + numStates * 3] = W * vL.z * (2. * hW - 1.);
		//max col
		evR[0 + numStates * 4] = 1.;
		evR[1 + numStates * 4] = hW * CPlus;
		evR[2 + numStates * 4] = hW * vL.y;
		evR[3 + numStates * 4] = hW * vL.z;
		evR[4 + numStates * 4] = hW * APlus - 1.;

		__global real* evL = eigen->evL;
		real Delta = hSq * hW * (Kappa - 1.) * (1. - vxSq) * (APlus * lambdaMax - AMinus * lambdaMin);
		//min row
		real scale;
		scale = hSq / Delta;
		evL[0 + numStates * 0] = scale * (hW * APlus * (v.x - lambdaMax) - v.x - W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (v.x - APlus * lambdaMax) + Kappa * APlus * lambdaMax);
		evL[0 + numStates * 1] = scale * (1. + W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (1. - APlus) - Kappa * APlus);
		evL[0 + numStates * 2] = scale * (W2 * v.y * (2. * Kappa - 1.) * APlus * (v.x - lambdaMax));
		evL[0 + numStates * 3] = scale * (W2 * v.z * (2. * Kappa - 1.) * APlus * (v.x - lambdaMax));
		evL[0 + numStates * 4] = scale * (-v.x - W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (v.x - APlus * lambdaMax) + Kappa * APlus * lambdaMax);
		//mid normal row
		scale = W / (Kappa - 1.);
		evL[1 + numStates * 0] = scale * (h - W);
		evL[1 + numStates * 1] = scale * (W * v.x);
		evL[1 + numStates * 2] = scale * (W * v.y);
		evL[1 + numStates * 3] = scale * (W * v.z);
		evL[1 + numStates * 4] = scale * (-W);
		//mid tangent A row
		scale = 1. / (h * (1. - vxSq));
		evL[2 + numStates * 0] = scale * (-v.y);
		evL[2 + numStates * 1] = scale * (v.x * v.y);
		evL[2 + numStates * 2] = scale * (1. - vxSq);
		evL[2 + numStates * 3] = 0.;
		evL[2 + numStates * 4] = scale * (-v.y);
		//mid tangent B row
		evL[3 + numStates * 0] = scale * (-v.z);
		evL[3 + numStates * 1] = scale * (v.x * v.z);
		evL[3 + numStates * 2] = 0.;
		evL[3 + numStates * 3] = scale * (1. - vxSq);
		evL[3 + numStates * 4] = scale * (-v.z);
		//max row
		scale = -hSq / Delta;
		evL[4 + numStates * 0] = scale * (hW * AMinus * (v.x - lambdaMin) - v.x - W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (v.x - AMinus * lambdaMin) + Kappa * AMinus * lambdaMin);
		evL[4 + numStates * 1] = scale * (1. + W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (1. - AMinus) - Kappa * AMinus);
		evL[4 + numStates * 2] = scale * (W2 * v.y * (2. * Kappa - 1.) * AMinus * (v.x - lambdaMin));
		evL[4 + numStates * 3] = scale * (W2 * v.z * (2. * Kappa - 1.) * AMinus * (v.x - lambdaMin));
		evL[4 + numStates * 4] = scale * (-v.x - W2 * (vSq - vxSq) * (2. * Kappa - 1.) * (v.x - AMinus * lambdaMin) + Kappa * AMinus * lambdaMin);

		<? if side == 1 then ?>
		for (int i = 0; i < numStates; ++i) {
			real tmp;
			//-90' rotation applied to the LHS of incoming velocity vectors, to move their y axis into the x axis
			// is equivalent of a -90' rotation applied to the RHS of the flux jacobian A
			// and A = Q V Q-1 for Q = the right eigenvectors and Q-1 the left eigenvectors
			// so a -90' rotation applied to the RHS of A is a +90' rotation applied to the RHS of Q-1 the left eigenvectors
			//and while a rotation applied to the LHS of a vector rotates the elements of its column vectors, a rotation applied to the RHS rotates the elements of its row vectors
			//each row's y <- x, x <- -y
			tmp = evL[i + numStates * cons_Sx];
			evL[i + numStates * cons_Sx] = -evL[i + numStates * cons_Sy];
			evL[i + numStates * cons_Sy] = tmp;
			//a -90' rotation applied to the RHS of A must be corrected with a 90' rotation on the LHS of A
			//this rotates the elements of the column vectors by 90'
			//each column's x <- y, y <- -x
			tmp = evR[cons_Sx + numStates * i];
			evR[cons_Sx + numStates * i] = -evR[cons_Sy + numStates * i];
			evR[cons_Sy + numStates * i] = tmp;
		}
		<? elseif side == 2 then ?>
		for (int i = 0; i < numStates; ++i) {
			real tmp;
			tmp = evL[i + numStates * cons_Sx];
			evL[i + numStates * cons_Sx] = -evL[i + numStates * cons_Sz];
			evL[i + numStates * cons_Sz] = tmp;
			tmp = evR[cons_Sx + numStates * i];
			evR[cons_Sx + numStates * i] = -evR[cons_Sz + numStates * i];
			evR[cons_Sz + numStates * i] = tmp;
		}
		<? end ?>
	}<? end ?>
}

__kernel void constrainU(
	__global cons_t* UBuf
) {
	SETBOUNDS(0,0);

	__global cons_t* U = UBuf + index;

	U->D = max(U->D, (real)DMin);
	U->tau = max(U->tau, (real)tauMin);

	U->D = min(U->D, (real)DMax);
	U->tau = min(U->tau, (real)tauMax);
}

__kernel void updatePrims(
	__global prim_t* primBuf,
	const __global cons_t* UBuf
) {
	SETBOUNDS(2,1);

	const __global cons_t* U = UBuf + index;
	real D = U->D;
	real3 S = U->S;
	real tau = U->tau;

	__global prim_t* prim = primBuf + index;
	real3 v = prim->v;

	real SLen = coordLen(S);
	real PMin = max(SLen - tau - D + SLen * solvePrimVelEpsilon, solvePrimPMinEpsilon);
	real PMax = gamma_1 * tau;
	PMax = max(PMax, PMin);
	real P = .5 * (PMin + PMax);

	for (int iter = 0; iter < solvePrimMaxIter; ++iter) {
		real vLen = SLen / (tau + D + P);
		real vSq = vLen * vLen;
		real W = 1. / sqrt(1. - vSq);
		real eInt = (tau + D * (1. - W) + P * (1. - W*W)) / (D * W);
		real rho = D / W;
		real f = gamma_1 * rho * eInt - P;
		real csSq = gamma_1 * (tau + D * (1. - W) + P) / (tau + D + P);
		real df_dP = vSq * csSq - 1.;
		real newP = P - f / df_dP;
		newP = max(newP, PMin);
		real PError = fabs(1. - newP / P);
		P = newP;
		if (PError < solvePrimStopEpsilon) {
			v = real3_scale(S, 1. / (tau + D + P));
			vSq = coordLenSq(v);
			W = 1. / sqrt(1. - vSq);
			rho = D / W;
			rho = max(rho, (real)rhoMin);
			rho = min(rho, (real)rhoMax);
			eInt = P / (rho * gamma_1);
			eInt = min(eInt, (real)eIntMax);
			prim->rho = rho;
			prim->v = v;
			prim->eInt = eInt;
//printf("cell %d finished with prims = %f %f %f\n", index, rho, v.x, eInt);
			return;
		}
	}
}


fixed bug in init kernel

fixed bug in init kernel

__constant sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;

__kernel void GVF2DInit(__read_only image2d_t volume, __write_only image2d_t vector_field ) {
    // Calculate gradient using a 1D central difference for each dimension, with spacing 1
    int2 writePos = {get_global_id(0), get_global_id(1)};

    int2 size = {get_global_size(0), get_global_size(1)};
    int2 pos = writePos;
    pos = select(pos, (int2)(2,2), pos == (int2)(0,0));
    pos = select(pos, size-3, pos >= size-1);

    float f10 = read_imagef(volume, sampler, pos + (int2)(1,0)).x;
    float f_10 = read_imagef(volume, sampler, pos - (int2)(1,0)).x;
    float f01 = read_imagef(volume, sampler, pos + (int2)(0,1)).x;
    float f0_1 = read_imagef(volume, sampler, pos - (int2)(0,1)).x;

    float4 gradient = {
        0.5f*(f10-f_10),
        0.5f*(f01-f0_1),
        0,
        0
    };

    write_imagef(vector_field, writePos, gradient);
}

#define LA2D(x,y) (x + (y<<4))
__kernel __attribute__((reqd_work_group_size(16,16,1))) void GVF2DIteration(__read_only image2d_t init_vector_field, __read_only image2d_t read_vector_field, __write_only image2d_t write_vector_field, __private float mu) {

    int2 writePos = {
        get_global_id(0)-(get_group_id(0)*2+1),
        get_global_id(1)-(get_group_id(1)*2+1)
    };
    int2 localPos = {get_local_id(0), get_local_id(1)};

    // TODO: Enforce mirror boundary conditions
    int2 size = {get_image_width(init_vector_field), get_image_height(init_vector_field)};
    int2 pos = writePos;
    pos = select(pos, (int2)(2,2), pos == (int2)(0,0));
    pos = select(pos, size-3, pos >= size-1);

    // Allocate shared memory
    __local float2 sharedMemory[256];

    // Read into shared memory
    float2 v = read_imagef(read_vector_field, sampler, pos).xy;
    sharedMemory[LA2D(localPos.x,localPos.y)]= v;

    // Ensure that it don't write outside of the image
    int2 comp = (localPos == (int2)(0,0)) +
        (localPos == (int2)(15,15)) + (writePos > size-1);

    // Synchronize the threads in the group
    barrier(CLK_LOCAL_MEM_FENCE);

    if(comp.x+comp.y ==  0) {
        // Load data from shared memory and do calculations
        float2 init_vector = read_imagef(init_vector_field, sampler, pos).xy;

        float2 fx1, fx_1, fy1, fy_1;
        fx1 = sharedMemory[LA2D(localPos.x+1,localPos.y)];
        fy1 = sharedMemory[LA2D(localPos.x,localPos.y+1)];
        fx_1 = sharedMemory[LA2D(localPos.x-1,localPos.y)];
        fy_1 = sharedMemory[LA2D(localPos.x,localPos.y-1)];

        // Update the vector field: Calculate Laplacian using a 3D central difference scheme
        float2 laplacian = -4*v + fx1 + fx_1 + fy1 + fy_1;

        v += mu * laplacian - (v - init_vector)*(init_vector.x*init_vector.x+init_vector.y*init_vector.y);

        write_imagef(write_vector_field, writePos, (float4)(v.x,v.y,0,0));
    }
}

__kernel void GVF2DResult(__write_only image2d_t result, __read_only image2d_t vectorField) {
    int2 pos = {get_global_id(0), get_global_id(1)};
    float4 vector = read_imagef(vectorField, sampler, pos);
    vector.z = 0;
    vector.w = 0;
    write_imagef(result, pos, length(vector));
}

__constant sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;

__kernel void GVF2DInit(__read_only image2d_t volume, __write_only image2d_t vector_field ) {
    // Calculate gradient using a 1D central difference for each dimension, with spacing 1
    int2 pos = {get_global_id(0), get_global_id(1)};

    float f10 = read_imagef(volume, sampler, pos + (int2)(1,0)).x;
    float f_10 = read_imagef(volume, sampler, pos - (int2)(1,0)).x;
    float f01 = read_imagef(volume, sampler, pos + (int2)(0,1)).x;
    float f0_1 = read_imagef(volume, sampler, pos - (int2)(0,1)).x;

    float4 gradient = {
        0.5f*(f10-f_10),
        0.5f*(f01-f0_1),
        0,
        0
    };

    write_imagef(vector_field, pos, gradient);
}

#define LA2D(x,y) (x + (y<<4))
__kernel __attribute__((reqd_work_group_size(16,16,1))) void GVF2DIteration(__read_only image2d_t init_vector_field, __read_only image2d_t read_vector_field, __write_only image2d_t write_vector_field, __private float mu) {

    int2 writePos = {
        get_global_id(0)-(get_group_id(0)*2+1),
        get_global_id(1)-(get_group_id(1)*2+1)
    };
    int2 localPos = {get_local_id(0), get_local_id(1)};

    // TODO: Enforce mirror boundary conditions
    int2 size = {get_image_width(init_vector_field), get_image_height(init_vector_field)};
    int2 pos = writePos;
    pos = select(pos, (int2)(2,2), pos == (int2)(0,0));
    pos = select(pos, size-3, pos >= size-1);

    // Allocate shared memory
    __local float2 sharedMemory[256];

    // Read into shared memory
    float2 v = read_imagef(read_vector_field, sampler, pos).xy;
    sharedMemory[LA2D(localPos.x,localPos.y)]= v;

    // Ensure that it don't write outside of the image
    int2 comp = (localPos == (int2)(0,0)) +
        (localPos == (int2)(15,15)) + (writePos > size-1);

    // Synchronize the threads in the group
    barrier(CLK_LOCAL_MEM_FENCE);

    if(comp.x+comp.y ==  0) {
        // Load data from shared memory and do calculations
        float2 init_vector = read_imagef(init_vector_field, sampler, pos).xy;

        float2 fx1, fx_1, fy1, fy_1;
        fx1 = sharedMemory[LA2D(localPos.x+1,localPos.y)];
        fy1 = sharedMemory[LA2D(localPos.x,localPos.y+1)];
        fx_1 = sharedMemory[LA2D(localPos.x-1,localPos.y)];
        fy_1 = sharedMemory[LA2D(localPos.x,localPos.y-1)];

        // Update the vector field: Calculate Laplacian using a 3D central difference scheme
        float2 laplacian = -4*v + fx1 + fx_1 + fy1 + fy_1;

        v += mu * laplacian - (v - init_vector)*(init_vector.x*init_vector.x+init_vector.y*init_vector.y);

        write_imagef(write_vector_field, writePos, (float4)(v.x,v.y,0,0));
    }
}

__kernel void GVF2DResult(__write_only image2d_t result, __read_only image2d_t vectorField) {
    int2 pos = {get_global_id(0), get_global_id(1)};
    float4 vector = read_imagef(vectorField, sampler, pos);
    vector.z = 0;
    vector.w = 0;
    write_imagef(result, pos, length(vector));
}


cleanup useless variables in painter.cl

cleanup useless variables in painter.cl

// simple kernel that copy in into out

#define radius 3
#define power_radius (radius * radius)
#define intensity_level 20

__kernel void video_image(
    __read_only image2d_t in,
    __write_only image2d_t out)
{
    const sampler_t format =
        CLK_NORMALIZED_COORDS_FALSE |
        CLK_FILTER_NEAREST |
        CLK_ADDRESS_CLAMP;
    const int2 d = (int2)(get_global_id(0), get_global_id(1));
    int intensity_count[intensity_level];
    float4 average_color[intensity_level];
    // cleanup
    for (int i = 0; i < intensity_level; ++i) {
        intensity_count[i] = 0;
        average_color[i] = (float4)(0.0f, 0.0f, 0.0f, 1.0f);
    }
    // step 1
    // for each pixel within radius of pixel
    for (int x = d.x - radius; x < d.x + radius; ++x) {
        for (int y = d.y - radius; y < d.y + radius; ++y) {
            float2 abs_pos = (float2)(d.x - x, d.y - y);
            if (power_radius < dot(abs_pos, abs_pos))
                continue;
            float4 color_element = read_imagef(in, format, (int2)(x, y));
            int current_intensity =
                (dot(color_element, (float4)(1.0f, 1.0f, 1.0f, 0.0f)) / 3.0f) *
                intensity_level;
            current_intensity = (current_intensity >= intensity_level) ?
                current_intensity :
                current_intensity - 1;
            intensity_count[current_intensity] += 1;
            average_color[current_intensity] += color_element;
        }
    }
    // step 2
    // find the maximum level of intensity
    int max_level = 0;
    int max_index = 0;
    for (int level = 0; level < intensity_level; ++level) {
        if (intensity_count[level] > max_level) {
            max_level = intensity_count[level];
            max_index = level;
        }
    }
    // step 3
    // write the final color
    float4 out_col = average_color[max_index] / max_level;
    out_col.w = 1.0f;
    write_imagef(out, d, out_col);
}

// simple kernel that copy in into out

#define radius 4
#define power_radius (radius * radius)
#define intensity_level 20

__kernel void video_image(
    __read_only image2d_t in,
    __write_only image2d_t out)
{
    const sampler_t format =
        CLK_NORMALIZED_COORDS_FALSE |
        CLK_FILTER_NEAREST |
        CLK_ADDRESS_CLAMP;
    const int2 d = (int2)(get_global_id(0), get_global_id(1));
    const int2 range = (int2)(get_global_size(0), get_global_size(1));
    float4 out_col;
    int intensity_count[intensity_level];
    float4 average_color[intensity_level];
    // cleanup
    for (int i = 0; i < intensity_level; ++i) {
        intensity_count[i] = 0;
        average_color[i] = (float4)(0.0f, 0.0f, 0.0f, 1.0f);
    }
    // step 1
    // for each pixel within radius of pixel
    for (int x = d.x - radius; x < d.x + radius; ++x) {
        for (int y = d.y - radius; y < d.y + radius; ++y) {
            float2 abs_pos = (float2)(d.x - x, d.y - y);
            if (power_radius < dot(abs_pos, abs_pos))
                continue;
            float4 color_element = read_imagef(in, format, (int2)(x, y));
            int current_intensity =
                (dot(color_element, (float4)(1.0f, 1.0f, 1.0f, 0.0f)) / 3.0f) *
                intensity_level;
            current_intensity = (current_intensity >= intensity_level) ?
                current_intensity :
                current_intensity - 1;
            intensity_count[current_intensity] += 1;
            average_color[current_intensity] += color_element;
        }
    }
    // step 2
    // find the maximum level of intensity
    int max_level = 0;
    int max_index = 0;
    for (int level = 0; level < intensity_level; ++level) {
        if (intensity_count[level] > max_level) {
            max_level = intensity_count[level];
            max_index = level;
        }
    }
    // step 3
    // write the final color
    out_col = average_color[max_index] / max_level;
    out_col.w = 1.0f;
    write_imagef(out, d, out_col);
}


Improve fresnel material sampler

Improve fresnel material sampler

#ifndef MATERIAL_SAMPLER_CL
#define MATERIAL_SAMPLER_CL

#define MAT_BLEND_FUNC_MIX 0
#define MAT_BLEND_FUNC_FRESNEL 1

void matSelectNode(Surface *surface, float3 inRayDir, MaterialNode *selectedMaterial, __global MaterialNode* materialNodes, uint2 *rndState, __global TextureMetadata *texMeta, __global uchar *texData );
float3 matGetSample3f(float2 uv, float3 defaultValue, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData);
float matGetSample1f(float2 uv, float defaultValue, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData);
float3 matGetNormalSample3f(float3 normal, float2 uv, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData);

// Traverse the layered material tree for this surface and select a leaf node
void matSelectNode(Surface *surface, float3 inRayDir, MaterialNode *selectedMaterial, __global MaterialNode* materialNodes, uint2 *rndState, __global TextureMetadata *texMeta, __global uchar *texData ){
	__global MaterialNode* node = materialNodes + surface->matNodeIndex;
	float2 sample;
	float nval;
	while( node->isNode ){
		sample = randomGetSample2f(rndState);
		nval = matGetSample1f(surface->uv, node->nval, node->nvalTex, texMeta, texData);

		if(node->blendFunc == MAT_BLEND_FUNC_FRESNEL){
			// inRayDir point *away* from surface
			float cosI = dot(inRayDir, surface->normal);

			float etaI = 1.0f;
			float etaT = nval;

			// If we are exiting the area we need to flip the normal and incident/transmission etas
			if( cosI < 0.0f ) {
				etaI = etaT;
				etaT = 1.0f;
				cosI = -cosI;
			}

			// The following formulas are taken from: https://en.wikipedia.org/wiki/Refractive_index
			float eta = etaI / etaT;
			float sqISinI = 1.0f - cosI * cosI;
			float sqSinT = eta * eta * sqISinI;

			// Calculate the fresnel value if TIR does not occur (TIR when sqSinT > 1.0)
			nval = 1.0f;
			if( sqSinT <= 1.0f ){
				// Uae Schlick's approximation for the fresnel:
				// R = r0 + (1-r0) * (1-cosI)^5
				float r0 = ((1.0f - eta) * (1.0f - eta)) / ((1.0f + eta) * (1.0f + eta));
				nval = r0 + (1.0f - r0) * native_powr(fabs(1.0f - cosI), 5);
			}
		}

		// nval contains the selection probability for the left child
		node = materialNodes + ((sample.x < nval) ? node->leftChild : node->rightChild);
	}

	*selectedMaterial = *node;
}

// Sample texture using the supplied uv coordinates and return a float3 vector.
// If texIndex is -1 then fall-back to the supplied default value.
float3 matGetSample3f(float2 uv, float3 defaultValue, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData){
	if( texIndex == -1 ){
		return defaultValue;
	}

	return texGetSample3f( uv, texIndex, texMeta, texData );
}

// Sample texture using the supplied uv coordinates and return a float value.
// If texIndex is -1 then fall-back to the supplied default value.
float matGetSample1f(float2 uv, float defaultValue, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData){
	if( texIndex == -1 ){
		return defaultValue;
	}

	return texGetSample1f( uv, texIndex, texMeta, texData );
}

// Apply normal map to intersection normal.
float3 matGetNormalSample3f(float3 normal, float2 uv, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData){
	// Generate tangent, bi-tangent vectors
	float3 u = normalize(cross((fabs(normal.x) > .1f ? (float3)(0.0f, 1.0f, 0.0f) : (float3)(1.0f, 0.0f, 0.0f)), normal));
	float3 v = cross(normal,u);

	// Sample normal map and convert it into the [-1, 1] range.
	// R, G components encode the range [-1, 1] into a value [0, 255]
	// B component encodes the range [0, 1] into [128, 255]
	float3 sample = (texGetSample3f( uv, texIndex, texMeta, texData ) * 2.0f) - 1.0f;
	return normalize(u * sample.x + v * sample.y + 0.5f * normal * sample.z);
}

#endif

#ifndef MATERIAL_SAMPLER_CL
#define MATERIAL_SAMPLER_CL

#define MAT_BLEND_FUNC_MIX 0
#define MAT_BLEND_FUNC_FRESNEL 1

void matSelectNode(Surface *surface, float3 inRayDir, MaterialNode *selectedMaterial, __global MaterialNode* materialNodes, uint2 *rndState, __global TextureMetadata *texMeta, __global uchar *texData );
float3 matGetSample3f(float2 uv, float3 defaultValue, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData);
float matGetSample1f(float2 uv, float defaultValue, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData);
float3 matGetNormalSample3f(float3 normal, float2 uv, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData);

// Traverse the layered material tree for this surface and select a leaf node
void matSelectNode(Surface *surface, float3 inRayDir, MaterialNode *selectedMaterial, __global MaterialNode* materialNodes, uint2 *rndState, __global TextureMetadata *texMeta, __global uchar *texData ){
	__global MaterialNode* node = materialNodes + surface->matNodeIndex;
	float2 sample;
	float nval;
	while( node->isNode ){
		sample = randomGetSample2f(rndState);
		nval = matGetSample1f(surface->uv, node->nval, node->nvalTex, texMeta, texData);

		if(node->blendFunc == MAT_BLEND_FUNC_FRESNEL){
			// inRayDir point *away* from surface
			float cosI = dot(inRayDir, surface->normal);
			bool entering = cosI > 0.0f;

			float etaI = 1.0f;
			float etaT = nval;
			float3 surfNormal = surface->normal;

			// If we are exiting the area we need to flip the normal and incident/transmission etas
			if( !entering ) {
				etaI = etaT;
				etaT = 1.0f;
				surfNormal = -surfNormal;
				cosI = -cosI;
			}

			// The following formulas are taken from: https://en.wikipedia.org/wiki/Refractive_index
			float eta = etaI / etaT;
			float sqISinI = 1.0f - cosI * cosI;
			float sqSinT = eta * eta * sqISinI;

			// Calculate the fresnel value if TIR does not occur (TIR when sqSinT > 1.0)
			nval = 1.0f;
			if( sqSinT <= 1.0f ){
				// Uae Schlick's approximation for the fresnel:
				// R = r0 + (1-r0) * (1-cosI)^5
				float r0 = ((1.0f - eta) * (1.0f - eta)) / ((1.0f + eta) * (1.0f + eta));
				nval = r0 + (1.0f - r0) * native_powr(fabs(1.0f - cosI), 5);
			}
		}

		// nval contains the selection probability for the left child
		node = materialNodes + ((sample.x < nval) ? node->leftChild : node->rightChild);
	}

	*selectedMaterial = *node;
}

// Sample texture using the supplied uv coordinates and return a float3 vector.
// If texIndex is -1 then fall-back to the supplied default value.
float3 matGetSample3f(float2 uv, float3 defaultValue, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData){
	if( texIndex == -1 ){
		return defaultValue;
	}

	return texGetSample3f( uv, texIndex, texMeta, texData );
}

// Sample texture using the supplied uv coordinates and return a float value.
// If texIndex is -1 then fall-back to the supplied default value.
float matGetSample1f(float2 uv, float defaultValue, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData){
	if( texIndex == -1 ){
		return defaultValue;
	}

	return texGetSample1f( uv, texIndex, texMeta, texData );
}

// Apply normal map to intersection normal.
float3 matGetNormalSample3f(float3 normal, float2 uv, int texIndex, __global TextureMetadata *texMeta, __global uchar* texData){
	// Generate tangent, bi-tangent vectors
	float3 u = normalize(cross((fabs(normal.x) > .1f ? (float3)(0.0f, 1.0f, 0.0f) : (float3)(1.0f, 0.0f, 0.0f)), normal));
	float3 v = cross(normal,u);

	// Sample normal map and convert it into the [-1, 1] range.
	// R, G components encode the range [-1, 1] into a value [0, 255]
	// B component encodes the range [0, 1] into [128, 255]
	float3 sample = (texGetSample3f( uv, texIndex, texMeta, texData ) * 2.0f) - 1.0f;
	return normalize(u * sample.x + v * sample.y + 0.5f * normal * sample.z);
}

#endif