fixed a bug I introduced

fixed a bug I introduced

//// MODULE_NAME: primFromCons
//// MODULE_DEPENDS: real3 solver_t prim_t cons_t eqn.common
// eqn.common is for all the calc_* stuff

#define primFromCons(\
	/*prim_t * const */resultW,\
	/*constant solver_t const * const */solver,\
	/*cons_t const * const*/U,\
	/*real3 const */x\
) {\
	(resultW)->rho = (U)->rho;\
	(resultW)->v = real3_real_mul((U)->m, 1./(U)->rho);\
	(resultW)->P = calc_P(solver, U, x);\
	(resultW)->ePot = (U)->ePot;\
}

//// MODULE_NAME: consFromPrim
//// MODULE_DEPENDS: real3 solver_t prim_t cons_t eqn.common
// eqn.common is for all the calc_* stuff

#define consFromPrim(\
	/*cons_t * const */ result,\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */W,\
	/*real3 const */pt\
) {\
	(result)->rho = (W)->rho;\
	(result)->m = real3_real_mul((W)->v, (W)->rho);\
	(result)->ETotal = calc_ETotal(solver, W, pt);\
	(result)->ePot = (W)->ePot;\
}

//// MODULE_NAME: apply_dU_dW
//// MODULE_DEPENDS: solver_t prim_t cons_t coord_lower
// only used by PLM

#define apply_dU_dW(\
	/*cons_t * const */result,\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */WA,\
	/*prim_t const * const */W,\
	/*real3 const */x\
) {\
	real3 const WA_vL = coord_lower((WA)->v, x);\
	(result)->rho = (W)->rho;\
	(result)->m = real3_add(\
		real3_real_mul((WA)->v, (W)->rho),\
		real3_real_mul((W)->v, (WA)->rho));\
	(result)->ETotal = (W)->rho * .5 * real3_dot((WA)->v, WA_vL)\
		+ (WA)->rho * real3_dot((W)->v, WA_vL)\
		+ (W)->P / (solver->heatCapacityRatio - 1.);\
	(result)->ePot = (W)->ePot;\
}

//// MODULE_NAME: apply_dW_dU
//// MODULE_DEPENDS: solver_t prim_t cons_t coord_lower
// only used by PLM

#define apply_dW_dU(\
	/*prim_t * const */result,\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */WA,\
	/*cons_t const * const */U,\
	/*real3 const */x\
) {\
	real3 const WA_vL = coord_lower((WA)->v, x);\
	(result)->rho = (U)->rho;\
	(result)->v = real3_sub(\
		real3_real_mul((U)->m, 1. / (WA)->rho);\
		real3_real_mul((WA)->v, (U)->rho / (WA)->rho));\
	(result)->P = (solver->heatCapacityRatio - 1.) * (\
		.5 * real3_dot((WA)->v, WA_vL) * (U)->rho \
		- real3_dot((U)->m, WA_vL)\
		+ (U)->ETotal);\
	(result)->ePot = (U)->ePot;\
}

//// MODULE_NAME: eqn.common
//// MODULE_DEPENDS: cons_t prim_t waves_t eigen_t coordLenSq

#define /*real*/ calc_H(\
	/*constant solver_t const * const */solver,\
	/*real const */P\
)	((P) * (solver->heatCapacityRatio / (solver->heatCapacityRatio - 1.)))

#define /*real*/ calc_h(\
	/*constant solver_t const * const */solver,\
	/*real const */rho,\
	/*real const */P\
)	(calc_H(solver, P) / (rho))

#define /*real*/ calc_HTotal(\
	/*real const */P,\
	/*real const */ETotal\
)	((P) + (ETotal))

#define /*real*/ calc_hTotal(\
	/*real const */rho,\
	/*real const */P,\
	/*real const */ETotal\
)	(calc_HTotal(P, ETotal) / (rho))

#define /*real*/ calc_eKin(\
	/*prim_t const * const */W,\
	/*real3 const */x\
)	(.5 * coordLenSq((W)->v, x))

#define /*real*/ calc_EKin(\
	/*prim_t const * const */W,\
	/*real3 const */x\
) 	((W)->rho * calc_eKin(W, x))

#define /*real*/ calc_EInt(\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */W\
) 	((W)->P / (solver->heatCapacityRatio - 1.))

#define /*real*/ calc_eInt(\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */W\
)	(calc_EInt(solver, W) / (W)->rho)

#define /*real*/ calc_EKin_fromCons(\
	/*cons_t const * const*/U,\
	/*real3 const */x\
)	(.5 * coordLenSq((U)->m, x) / (U)->rho)

#define /*real*/ calc_ETotal(\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */W,\
	/*real3 const */x\
) 	(calc_EKin(W, x) + calc_EInt(solver, W))

#define /*real*/ calc_Cs(\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */W\
) 	(sqrt(solver->heatCapacityRatio * (W)->P / (W)->rho))

#define /*real*/ calc_P(\
	/*constant solver_t const * const */solver,\
	/*cons_t const * const*/U,\
	/*real3 const */x\
)	(solver->heatCapacityRatio - 1.) * (/*EInt=*/(U)->ETotal - /*EKin=*/calc_EKin_fromCons(U, x))

//// MODULE_NAME: applyInitCond
//// MODULE_DEPENDS: consFromPrim cartesianToCoord

/*
I've highjacked all of this.  It was a normal Euler eqn solver.
But I experimented with a curved-space solver.
To get back to the original code,
just replace all the g_ab stuff with their constant values and simplify away.
*/

kernel void applyInitCond(
	constant solver_t const * const solver,
	constant initCond_t const * const initCond,
	global cons_t* UBuf,
	const global cell_t* cellBuf
) {
	SETBOUNDS(0,0);
	real3 x = cellBuf[index].pos;

	global <?=eqn.cons_t?>* U = UBuf + index;

	real3 mids = real3_real_mul(real3_add(solver->initCondMins, solver->initCondMaxs), .5);
	bool lhs = true<?
for i=1,solver.dim do
	local xi = xNames[i]
?> && x.<?=xi?> < mids.<?=xi?><?
end
?>;

	// these are all standard for all init/euler.lua initial conditions
	real rho = 0;
	real3 v = real3_zero;
	real P = 0;
	real3 D = real3_zero;
	real3 B = real3_zero;
	real ePot = 0;

	<?=initCode()?>

	<?=eqn.prim_t?> W = {
		.rho = rho,
		.v = cartesianToCoord(v, x),
		.P = P,
		.ePot = ePot,
	};

	consFromPrim(U, solver, &W, x);
}

//// MODULE_NAME: fluxFromCons
//// MODULE_DEPENDS: solver_t primFromCons normal_t

#define fluxFromCons(\
	/*<?=eqn.cons_t?> * const */resultF,\
	/*constant solver_t const * const */solver,\
	/*cons_t const * const */U,\
	/*real3 const */x,\
	/*normal_t const */n\
) {\
	prim_t W;\
	primFromCons(&W, solver, U, x);\
	real const v_n = normal_vecDotN1(n, W.v);\
	(resultF)->rho = (U)->rho * v_n;\
	(resultF)->m = real3_add(\
		real3_real_mul((U)->m, v_n),\
		real3_real_mul(normal_u1(n), W.P)\
	);\
	real const HTotal = (U)->ETotal + W.P;\
	(resultF)->ETotal = HTotal * v_n;\
	(resultF)->ePot = 0;\
}

//// MODULE_NAME: calcCellMinMaxEigenvalues
//// MODULE_DEPENDS: real3x3 primFromCons
// added by request only, so I don't have to compile the real3x3 code.
// not used at the moment

#define calcCellMinMaxEigenvalues(\
	/*range_t * const */result,\
	/*constant solver_t const * const */solver,\
	/*global cons_t const * const */U,\
	/*real3 const */pt,\
	/*real3x3 const */nL,\
	/*real3x3 const */nU,\
	/*real const */nLen\
) {\
	prim_t W;\
	primFromCons(&W, solver, U, pt);\
	real const v_n = real3_dot(W.v, nL.x);\
	real const Cs = calc_Cs(solver, &W);\
	real const Cs_nLen = Cs * nLen;\
	(result)->min = v_n - Cs_nLen; \
	(result)->max = v_n + Cs_nLen;\
}

//// MODULE_NAME: eigen_forCell
//// MODULE_DEPENDS: normal_t coord_lower cons_t prim_t eigen_t primFromCons eqn.common
// eqn.common is for all the calc_* stuff

// used by PLM
#define eigen_forCell(\
	/*eigen_t * const */resultEig,\
	/*constant solver_t const * const */solver,\
	/*cons_t const * const */U,\
	/*real3 const */pt,\
	/*normal_t const */n\
) {\
	prim_t W;\
	primFromCons(&W, solver, U, pt);\
	real3 const vL = coord_lower(W.v, pt);\
	real const vSq = real3_dot(W.v, vL);\
	real const v_n = normal_vecDotN1(n, W.v);\
	real const eKin = .5 * vSq;\
	real const hTotal = calc_hTotal(W.rho, W.P, (U)->ETotal);\
	real const CsSq = (solver->heatCapacityRatio - 1.) * (hTotal - eKin);\
	real const Cs = sqrt(CsSq);\
	(resultEig)->rho = W.rho;\
	(resultEig)->v = W.v;\
	(resultEig)->vSq = vSq;\
	(resultEig)->vL = vL;\
	(resultEig)->hTotal = hTotal;\
	(resultEig)->Cs = Cs;\
}

//// MODULE_NAME: eigen_forInterface
//// MODULE_DEPENDS: primFromCons eigen_t normal_t coord_lower

//used by the mesh version
#define eigen_forInterface(\
	/*eigen_t * const */resultEig,\
	/*constant solver_t const * const */solver,\
	/*cons_t const * const */UL,\
	/*cons_t const * const */UR,\
	/*real3 const */pt,\
	/*normal_t const */n\
) {\
	prim_t WL;\
	primFromCons(&WL, solver, UL, pt);\
	real const sqrtRhoL = sqrt(WL.rho);\
	real3 const vLeft = WL.v;\
	real const hTotalL = calc_hTotal(WL.rho, WL.P, (UL)->ETotal);\
\
	prim_t WR;\
	primFromCons(&WR, solver, UR, pt);\
	real const sqrtRhoR = sqrt(WR.rho);\
	real3 const vR = WR.v;\
	real const hTotalR = calc_hTotal(WR.rho, WR.P, (UR)->ETotal);\
\
	real const invDenom = 1./(sqrtRhoL + sqrtRhoR);\
\
	/*Roe-averaged*/\
	real const rho = sqrtRhoL * sqrtRhoR;\
	real3 const v = real3_add(\
			real3_real_mul(vLeft, sqrtRhoL * invDenom),\
			real3_real_mul(vR, sqrtRhoR * invDenom));\
	real const hTotal = invDenom * (sqrtRhoL * hTotalL + sqrtRhoR * hTotalR);\
\
	/*derived:*/\
	real3 const vLower = coord_lower(v, pt);\
	real const vSq = real3_dot(v, vLower);\
	real const eKin = .5 * vSq;\
	real const CsSq = (solver->heatCapacityRatio - 1.) * (hTotal - eKin);\
	real const Cs = sqrt(CsSq);\
\
	(resultEig)->rho = rho;\
	(resultEig)->v = v;\
	(resultEig)->vSq = vSq;\
	(resultEig)->vL = vLower;\
	(resultEig)->hTotal = hTotal;\
	(resultEig)->Cs = Cs;\
}

//// MODULE_NAME: eigen_left/rightTransform
//// MODULE_DEPENDS: eigen_t normal_t

#define eigen_leftTransform(\
	/*waves_t * const */result,\
	/*constant solver_t const * const */solver,\
	/*eigen_t const * const */eig,\
	/*cons_t const * const */X,\
	/*real3 const */pt,\
	/*normal_t */n\
) {\
	real3 const v_n = normal_vecDotNs(n, (eig)->v);\
	real const nLen = normal_len(n);\
	real const inv_nLen = 1. / nLen;\
	real const denom = 2. * (eig)->Cs * (eig)->Cs;\
	real const invDenom = 1. / denom;\
	real const gamma_1 = solver->heatCapacityRatio - 1.;\
	(result)->ptr[0] = (\
			(X)->ptr[0] * (.5 * gamma_1 * (eig)->vSq + (eig)->Cs * v_n.x * inv_nLen)\
			+ (X)->ptr[1] * (-gamma_1 * (eig)->vL.x - (eig)->Cs * normal_l1x_over_len(n))\
			+ (X)->ptr[2] * (-gamma_1 * (eig)->vL.y - (eig)->Cs * normal_l1y_over_len(n))\
			+ (X)->ptr[3] * (-gamma_1 * (eig)->vL.z - (eig)->Cs * normal_l1z_over_len(n))\
			+ (X)->ptr[4] * gamma_1\
		) * invDenom;\
	(result)->ptr[1] =\
		(\
			(X)->ptr[0] * (denom - gamma_1 * (eig)->vSq)\
			+ (X)->ptr[1] * 2. * gamma_1 * (eig)->vL.x\
			+ (X)->ptr[2] * 2. * gamma_1 * (eig)->vL.y\
			+ (X)->ptr[3] * 2. * gamma_1 * (eig)->vL.z\
			+ (X)->ptr[4] * -2. * gamma_1\
		) * invDenom;\
	(result)->ptr[2] =\
		(X)->ptr[0] * -v_n.y\
		+ (X)->ptr[1] * normal_l2x(n)\
		+ (X)->ptr[2] * normal_l2y(n)\
		+ (X)->ptr[3] * normal_l2z(n);\
	(result)->ptr[3] =\
		(X)->ptr[0] * -v_n.z\
		+ (X)->ptr[1] * normal_l3x(n)\
		+ (X)->ptr[2] * normal_l3y(n)\
		+ (X)->ptr[3] * normal_l3z(n);\
	(result)->ptr[4] =\
		(\
			(X)->ptr[0] * (.5 * gamma_1 * (eig)->vSq - (eig)->Cs * v_n.x * inv_nLen)\
			+ (X)->ptr[1] * (-gamma_1 * (eig)->vL.x + (eig)->Cs * normal_l1x_over_len(n))\
			+ (X)->ptr[2] * (-gamma_1 * (eig)->vL.y + (eig)->Cs * normal_l1y_over_len(n))\
			+ (X)->ptr[3] * (-gamma_1 * (eig)->vL.z + (eig)->Cs * normal_l1z_over_len(n))\
			+ (X)->ptr[4] * gamma_1\
		) * invDenom;\
}

#define eigen_rightTransform(\
	/*cons_t * const */result,\
	/*constant solver_t const * const */solver,\
	/*eigen_t const * const */eig,\
	/*waves_t const * const */X,\
	/*real3 const */pt,\
	/*normal_t const */n\
) {\
	real3 const v_n = normal_vecDotNs(n, (eig)->v);\
	real const nLen = normal_len(n);\
	real const inv_nLen = 1. / nLen;\
	(result)->ptr[0] =\
		(X)->ptr[0]\
		+ (X)->ptr[1]\
		+ (X)->ptr[4];\
	(result)->ptr[1] =\
		(X)->ptr[0] * ((eig)->v.x - (eig)->Cs * normal_u1x_over_len(n))\
		+ (X)->ptr[1] * (eig)->v.x\
		+ (X)->ptr[2] * normal_u2x(n)\
		+ (X)->ptr[3] * normal_u3x(n)\
		+ (X)->ptr[4] * ((eig)->v.x + (eig)->Cs * normal_u1x_over_len(n));\
	(result)->ptr[2] =\
		(X)->ptr[0] * ((eig)->v.y - (eig)->Cs * normal_u1y_over_len(n))\
		+ (X)->ptr[1] * (eig)->v.y\
		+ (X)->ptr[2] * normal_u2y(n)\
		+ (X)->ptr[3] * normal_u3y(n)\
		+ (X)->ptr[4] * ((eig)->v.y + (eig)->Cs * normal_u1y_over_len(n));\
	(result)->ptr[3] =\
		(X)->ptr[0] * ((eig)->v.z - (eig)->Cs * normal_u1z_over_len(n))\
		+ (X)->ptr[1] * (eig)->v.z\
		+ (X)->ptr[2] * normal_u2z(n)\
		+ (X)->ptr[3] * normal_u3z(n)\
		+ (X)->ptr[4] * ((eig)->v.z + (eig)->Cs * normal_u1z_over_len(n));\
	(result)->ptr[4] =\
		(X)->ptr[0] * ((eig)->hTotal - (eig)->Cs * v_n.x * inv_nLen)\
		+ (X)->ptr[1] * .5 * (eig)->vSq\
		+ (X)->ptr[2] * v_n.y\
		+ (X)->ptr[3] * v_n.z\
		+ (X)->ptr[4] * ((eig)->hTotal + (eig)->Cs * v_n.x * inv_nLen);\
	(result)->ptr[5] =\
		0;\
}

//// MODULE_NAME: eigen_fluxTransform
//// MODULE_DEPENDS: eigen_t normal_t
// not used anymore.  was used by Roe, but I switched that to a fluxFromCons.
// is this needed anymore?
// fluxFromCons only matches eigen_fluxTransform when the eig properties are derived from X_

#define eigen_fluxTransform(\
	/*cons_t * const */resultFlux,\
	/*constant solver_t const * const */solver,\
	/*eigen_t const * const */eig,\
	/*cons_t const * const */X_,\
	/*real3 const */x,\
	/*normal_t const */n\
) {\
	real3 const v_n = normal_vecDotNs(n, (eig).v);\
	real const nLen = normal_len(n);\
	const real gamma = solver->heatCapacityRatio;\
	const real gamma_1 = gamma - 1.;\
	const real gamma_2 = gamma - 2.;\
\
	(resultFlux)->ptr[0] =\
		(X_)->ptr[1] * normal_l1x(n)\
		+ (X_)->ptr[2] * normal_l1y(n)\
		+ (X_)->ptr[3] * normal_l1z(n);\
\
	(resultFlux)->ptr[1] =\
		(X_)->ptr[0] * (-v_n.x * (eig).v.x + gamma_1 * .5 * (eig).vSq * normal_u1x(n))\
		+ (X_)->ptr[1] * ((eig).v.x * normal_l1x(n) - gamma_2 * normal_u1x(n) * (eig).vL.x + v_n.x)\
		+ (X_)->ptr[2] * ((eig).v.x * normal_l1y(n) - gamma_2 * normal_u1x(n) * (eig).vL.y)\
		+ (X_)->ptr[3] * ((eig).v.x * normal_l1z(n) - gamma_2 * normal_u1x(n) * (eig).vL.z)\
		+ (X_)->ptr[4] * gamma_1 * normal_u1x(n);\
\
	(resultFlux)->ptr[2] =\
		(X_)->ptr[0] * (-v_n.x * (eig).v.y + gamma_1 * .5 * (eig).vSq * normal_u1y(n))\
		+ (X_)->ptr[1] * ((eig).v.y * normal_l1x(n) - gamma_2 * normal_u1y(n) * (eig).vL.x)\
		+ (X_)->ptr[2] * ((eig).v.y * normal_l1y(n) - gamma_2 * normal_u1y(n) * (eig).vL.y + v_n.x)\
		+ (X_)->ptr[3] * ((eig).v.y * normal_l1z(n) - gamma_2 * normal_u1y(n) * (eig).vL.z)\
		+ (X_)->ptr[4] * gamma_1 * normal_u1y(n);\
\
	(resultFlux)->ptr[3] =\
		(X_)->ptr[0] * (-v_n.x * (eig).v.z + gamma_1 * .5 * (eig).vSq * normal_u1z(n))\
		+ (X_)->ptr[1] * ((eig).v.z * normal_l1x(n) - gamma_2 * normal_u1z(n) * (eig).vL.x)\
		+ (X_)->ptr[2] * ((eig).v.z * normal_l1y(n) - gamma_2 * normal_u1z(n) * (eig).vL.y)\
		+ (X_)->ptr[3] * ((eig).v.z * normal_l1z(n) - gamma_2 * normal_u1z(n) * (eig).vL.z + v_n.x)\
		+ (X_)->ptr[4] * gamma_1 * normal_u1z(n);\
\
	(resultFlux)->ptr[4] =\
		(X_)->ptr[0] * v_n.x * (.5 * gamma_1 * (eig).vSq - (eig).hTotal)\
		+ (X_)->ptr[1] * (normal_l1x(n) * (eig).hTotal - gamma_1 * v_n.x * (eig).vL.x)\
		+ (X_)->ptr[2] * (normal_l1y(n) * (eig).hTotal - gamma_1 * v_n.x * (eig).vL.y)\
		+ (X_)->ptr[3] * (normal_l1z(n) * (eig).hTotal - gamma_1 * v_n.x * (eig).vL.z)\
		+ (X_)->ptr[4] * gamma * v_n.x;\
\
	(resultFlux)->ptr[5] =\
		0;\
}

<? if false then -- TODO sort addSource out. ?>
//// MODULE_NAME: addSource
//// MODULE_DEPENDS: solver_t cons_t cell_t SETBOUNDS_NOGHOST

kernel void addSource(
	constant solver_t const * const solver,
	global cons_t * const derivBuf,
	global cons_t const * const UBuf,
	global cell_t const * const cellBuf
) {
	SETBOUNDS_NOGHOST();
	real3 const x = cellBuf[index].pos;

	global cons_t const * const deriv = derivBuf + index;
	global cons_t const * const U = UBuf + index;

<? if false
and solver.coord.vectorComponent == 'anholonomic'
and require 'hydro.coord.cylinder'.is(solver.coord)
then ?>
<? 	if true then -- 2009 Trangenstein, p.474, 1999 Toro, p.29, eqn.1.104, 1.105 ?>
	<? for side=0,1 do ?>{
		real3 xL = x; xL.s<?=side?> -= solver->grid_dx.s<?=side?>;
		real3 xR = x; xR.s<?=side?> += solver->grid_dx.s<?=side?>;

		global cons_t const * const UL = U - solver->stepsize.s<?=side?>;
		global cons_t const * const UR = U + solver->stepsize.s<?=side?>;
		real const PL = calc_P(solver, UL, xL);
		real const PR = calc_P(solver, UR, xR);

		deriv->m.s<?=side?> -= (PR - PL) / (2. * solver->grid_dx.s<?=side?>);
	}<? end ?>
<?	end ?>
<?	if false then -- 1999 Toro p.28 eqn.1.102, 1.103 ?>
	cons_t F;
	fluxFromConsForSide0(&F, solver, U, x);
	deriv->rho -= F.rho / x.x;
	deriv->m.x -= F.m.x / x.x;
	deriv->m.y -= F.m.y / x.x;
	deriv->m.z -= F.m.z / x.x;
	deriv->ETotal -= F.ETotal / x.x;
<?	end ?>
<? end ?>

<? do -- if not solver.coord.vectorComponent == 'anholonomic' then ?>
<? if not (require 'hydro.coord.cartesian'.is(solver.coord)
		or solver.coord.vectorComponent == 'cartesian')
then ?>
//// MODULE_DEPENDS: primFromCons coord_conn_apply23 coord_conn_trace23 coord_conn_apply13
/*
This is working for init conds with zero velocity.
Introducing constant velocity of v=[x=1,y=1]=[r=sqrt(2),theta=pi/4] in the init cond causes some numerical errors.
However the problem isn't the terms below -- because disabling this for zero-vel init conds causes things to become unsteady.
That means that the volume gradient in calcDerivFV is causing nonzero velocities to emerge, and this is cancelling them.
Maybe for an initial constant vel as large as sqrt(2) this fails, but it works only for small perturbations?
*/
	//connection coefficient source terms of covariant derivative w/contravariant velocity vectors in a holonomic coordinate system
	prim_t W;
	primFromCons(&W, solver, U, x);

	//- Conn^i_jk rho v^j v^k
	deriv->m = real3_sub(deriv->m, coord_conn_apply23(W.v, U->m, x));

	//- Conn^i_jk g^jk P
	deriv->m = real3_sub(deriv->m, real3_real_mul(coord_conn_trace23(x), W.P));

	//+ (gamma-1) rho v^k v^l Gamma_kjl g^ij
	deriv->m = real3_add(deriv->m, real3_real_mul(coord_conn_apply13(W.v, U->m, x), (solver->heatCapacityRatio - 1.) ));

	//- (gamma-1) rho v^j v^k v^l Gamma_jkl
//	deriv->ETotal -= (solver->heatCapacityRatio - 1.) * coord_conn_apply123(W.v, W.v, U->m, x);

	//+ c_jk^k * Flux^Ij
<? 	if false and solver.coord.vectorComponent == 'anholonomic' then ?>
	real3 const commTrace = coord_tr23_c(x);
	<? for i=0,solver.dim-1 do ?>{
		cons_t flux;
		calcFluxFromCons(&F, *U, x);
		for (int j = 0; j < numIntStates; ++j) {
			deriv->ptr[j] += commTrace.s<?=i?> * flux.ptr[j];
		}
	}<? end ?>
<? 	end ?>
<? end ?>
<? end -- vectorComponent == 'anholonomic' ?>
}
<? end ?>

//// MODULE_NAME: constrainU
//// MODULE_DEPENDS: solver_t cons_t cell_t primFromCons consFromPrim

kernel void constrainU(
	constant solver_t const * const solver,
	global cons_t * const UBuf,
	global cell_t const * const cellBuf
) {
	SETBOUNDS(0,0);
	real3 const x = cellBuf[index].pos;

	global cons_t * const U = UBuf + index;
	prim_t W;
	primFromCons(&W, solver, U, x);

	if (W.rho < solver->rhoMin) W.rho = solver->rhoMin;
	if (W.P < solver->PMin) W.P = solver->PMin;

	consFromPrim(U, solver, &W, x);
}

//// MODULE_NAME: primFromCons
//// MODULE_DEPENDS: real3 solver_t prim_t cons_t eqn.common
// eqn.common is for all the calc_* stuff

#define primFromCons(\
	/*prim_t * const */resultW,\
	/*constant solver_t const * const */solver,\
	/*cons_t const * const*/U,\
	/*real3 const */x\
) {\
	(resultW)->rho = (U)->rho;\
	(resultW)->v = real3_real_mul((U)->m, 1./(U)->rho);\
	(resultW)->P = calc_P(solver, U, x);\
	(resultW)->ePot = (U)->ePot;\
}

//// MODULE_NAME: consFromPrim
//// MODULE_DEPENDS: real3 solver_t prim_t cons_t eqn.common
// eqn.common is for all the calc_* stuff

#define consFromPrim(\
	/*cons_t * const */ result,\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */W,\
	/*real3 const */pt\
) {\
	(result)->rho = (W)->rho;\
	(result)->m = real3_real_mul((W)->v, (W)->rho);\
	(result)->ETotal = calc_ETotal(solver, W, pt);\
	(result)->ePot = (W)->ePot;\
}

//// MODULE_NAME: apply_dU_dW
//// MODULE_DEPENDS: solver_t prim_t cons_t coord_lower
// only used by PLM

#define apply_dU_dW(\
	/*cons_t * const */result,\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */WA,\
	/*prim_t const * const */W,\
	/*real3 const */x\
) {\
	real3 const WA_vL = coord_lower((WA)->v, x);\
	(result)->rho = (W)->rho;\
	(result)->m = real3_add(\
		real3_real_mul((WA)->v, (W)->rho),\
		real3_real_mul((W)->v, (WA)->rho));\
	(result)->ETotal = (W)->rho * .5 * real3_dot((WA)->v, WA_vL)\
		+ (WA)->rho * real3_dot((W)->v, WA_vL)\
		+ (W)->P / (solver->heatCapacityRatio - 1.);\
	(result)->ePot = (W)->ePot;\
}

//// MODULE_NAME: apply_dW_dU
//// MODULE_DEPENDS: solver_t prim_t cons_t coord_lower
// only used by PLM

#define apply_dW_dU(\
	/*prim_t * const */result,\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */WA,\
	/*cons_t const * const */U,\
	/*real3 const */x\
) {\
	real3 const WA_vL = coord_lower((WA)->v, x);\
	(result)->rho = (U)->rho;\
	(result)->v = real3_sub(\
		real3_real_mul((U)->m, 1. / (WA)->rho);\
		real3_real_mul((WA)->v, (U)->rho / (WA)->rho));\
	(result)->P = (solver->heatCapacityRatio - 1.) * (\
		.5 * real3_dot((WA)->v, WA_vL) * (U)->rho \
		- real3_dot((U)->m, WA_vL)\
		+ (U)->ETotal);\
	(result)->ePot = (U)->ePot;\
}

//// MODULE_NAME: eqn.common
//// MODULE_DEPENDS: cons_t prim_t waves_t eigen_t coordLenSq

#define /*real*/ calc_H(\
	/*constant solver_t const * const */solver,\
	/*real const */P\
)	((P) * (solver->heatCapacityRatio / (solver->heatCapacityRatio - 1.)))

#define /*real*/ calc_h(\
	/*constant solver_t const * const */solver,\
	/*real const */rho,\
	/*real const */P\
)	(calc_H(solver, P) / (rho))

#define /*real*/ calc_HTotal(\
	/*real const */P,\
	/*real const */ETotal\
)	((P) + (ETotal))

#define /*real*/ calc_hTotal(\
	/*real const */rho,\
	/*real const */P,\
	/*real const */ETotal\
)	(calc_HTotal(P, ETotal) / (rho))

#define /*real*/ calc_eKin(\
	/*prim_t const * const */W,\
	/*real3 const */x\
)	(.5 * coordLenSq((W)->v, x))

#define /*real*/ calc_EKin(\
	/*prim_t const * const */W,\
	/*real3 const */x\
) 	((W)->rho * calc_eKin(W, x))

#define /*real*/ calc_EInt(\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */W\
) 	((W)->P / (solver->heatCapacityRatio - 1.))

#define /*real*/ calc_eInt(\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */W\
)	(calc_EInt(solver, W) / (W)->rho)

#define /*real*/ calc_EKin_fromCons(\
	/*cons_t const * const*/U,\
	/*real3 const */x\
)	(.5 * coordLenSq((U)->m, x) / (U)->rho)

#define /*real*/ calc_ETotal(\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */W,\
	/*real3 const */x\
) 	(calc_EKin(W, x) + calc_EInt(solver, W))

#define /*real*/ calc_Cs(\
	/*constant solver_t const * const */solver,\
	/*prim_t const * const */W\
) 	(sqrt(solver->heatCapacityRatio * (W)->P / (W)->rho))

#define /*real*/ calc_P(\
	/*constant solver_t const * const */solver,\
	/*cons_t const * const*/U,\
	/*real3 const */x\
)	(solver->heatCapacityRatio - 1.) * (/*EInt=*/(U)->ETotal - /*EKin=*/calc_EKin_fromCons(U, x))

//// MODULE_NAME: applyInitCond
//// MODULE_DEPENDS: consFromPrim cartesianToCoord

/*
I've highjacked all of this.  It was a normal Euler eqn solver.
But I experimented with a curved-space solver.
To get back to the original code,
just replace all the g_ab stuff with their constant values and simplify away.
*/

kernel void applyInitCond(
	constant solver_t const * const solver,
	constant initCond_t const * const initCond,
	global cons_t* UBuf,
	const global cell_t* cellBuf
) {
	SETBOUNDS(0,0);
	real3 x = cellBuf[index].pos;

	global <?=eqn.cons_t?>* U = UBuf + index;

	real3 mids = real3_real_mul(real3_add(solver->initCondMins, solver->initCondMaxs), .5);
	bool lhs = true<?
for i=1,solver.dim do
	local xi = xNames[i]
?> && x.<?=xi?> < mids.<?=xi?><?
end
?>;

	// these are all standard for all init/euler.lua initial conditions
	real rho = 0;
	real3 v = real3_zero;
	real P = 0;
	real3 D = real3_zero;
	real3 B = real3_zero;
	real ePot = 0;

	<?=initCode()?>

	<?=eqn.prim_t?> W = {
		.rho = rho,
		.v = cartesianToCoord(v, x),
		.P = P,
		.ePot = ePot,
	};

	consFromPrim(U, solver, &W, x);
}

//// MODULE_NAME: fluxFromCons
//// MODULE_DEPENDS: solver_t primFromCons normal_t

#define fluxFromCons(\
	/*<?=eqn.cons_t?> * const */resultF,\
	/*constant solver_t const * const */solver,\
	/*cons_t const * const */U,\
	/*real3 const */x,\
	/*normal_t const */n\
) {\
	prim_t W;\
	primFromCons(&W, solver, U, x);\
	real const v_n = normal_vecDotN1(n, W.v);\
	(resultF)->rho = (U)->rho * v_n;\
	(resultF)->m = real3_add(\
		real3_real_mul((U)->m, v_n),\
		real3_real_mul(normal_u1(n), W.P)\
	);\
	real const HTotal = (U)->ETotal + W.P;\
	(resultF)->ETotal = HTotal * v_n;\
	(resultF)->ePot = 0;\
}

//// MODULE_NAME: calcCellMinMaxEigenvalues
//// MODULE_DEPENDS: real3x3 primFromCons
// added by request only, so I don't have to compile the real3x3 code.
// not used at the moment

#define calcCellMinMaxEigenvalues(\
	/*range_t * const */result,\
	/*constant solver_t const * const */solver,\
	/*global cons_t const * const */U,\
	/*real3 const */pt,\
	/*real3x3 const */nL,\
	/*real3x3 const */nU,\
	/*real const */nLen\
) {\
	prim_t W;\
	primFromCons(&W, solver, U, pt);\
	real const v_n = real3_dot(W.v, nL.x);\
	real const Cs = calc_Cs(solver, &W);\
	real const Cs_nLen = Cs * nLen;\
	(result)->min = v_n - Cs_nLen; \
	(result)->max = v_n + Cs_nLen;\
}

//// MODULE_NAME: eigen_forCell
//// MODULE_DEPENDS: normal_t coord_lower cons_t prim_t eigen_t primFromCons eqn.common
// eqn.common is for all the calc_* stuff

// used by PLM
#define eigen_forCell(\
	/*eigen_t * const */resultEig,\
	/*constant solver_t const * const */solver,\
	/*cons_t const * const */U,\
	/*real3 const */pt,\
	/*normal_t const */n\
) {\
	prim_t W;\
	primFromCons(&W, solver, U, pt);\
	real3 const vL = coord_lower(W.v, pt);\
	real const vSq = real3_dot(W.v, vL);\
	real const v_n = normal_vecDotN1(n, W.v);\
	real const eKin = .5 * vSq;\
	real const hTotal = calc_hTotal(W.rho, W.P, (U)->ETotal);\
	real const CsSq = (solver->heatCapacityRatio - 1.) * (hTotal - eKin);\
	real const Cs = sqrt(CsSq);\
	(resultEig)->rho = W.rho;\
	(resultEig)->v = W.v;\
	(resultEig)->vSq = vSq;\
	(resultEig)->vL = vL;\
	(resultEig)->hTotal = hTotal;\
	(resultEig)->Cs = Cs;\
}

//// MODULE_NAME: eigen_forInterface
//// MODULE_DEPENDS: primFromCons eigen_t normal_t coord_lower

//used by the mesh version
#define eigen_forInterface(\
	/*eigen_t * const */resultEig,\
	/*constant solver_t const * const */solver,\
	/*cons_t const * const */UL,\
	/*cons_t const * const */UR,\
	/*real3 const */pt,\
	/*normal_t const */n\
) {\
	prim_t WL;\
	primFromCons(&WL, solver, UL, pt);\
	real const sqrtRhoL = sqrt(WL.rho);\
	real3 const vLeft = WL.v;\
	real const hTotalL = calc_hTotal(WL.rho, WL.P, (UL)->ETotal);\
\
	prim_t WR;\
	primFromCons(&WR, solver, UR, pt);\
	real const sqrtRhoR = sqrt(WR.rho);\
	real3 const vR = WR.v;\
	real const hTotalR = calc_hTotal(WR.rho, WR.P, (UR)->ETotal);\
\
	real const invDenom = 1./(sqrtRhoL + sqrtRhoR);\
\
	/*Roe-averaged*/\
	real const rho = sqrtRhoL * sqrtRhoR;\
	real3 const v = real3_add(\
			real3_real_mul(vLeft, sqrtRhoL * invDenom),\
			real3_real_mul(vR, sqrtRhoR * invDenom));\
	real const hTotal = invDenom * (sqrtRhoL * hTotalL + sqrtRhoR * hTotalR);\
\
	/*derived:*/\
	real3 const vLower = coord_lower(v, pt);\
	real const vSq = real3_dot(v, vLower);\
	real const eKin = .5 * vSq;\
	real const CsSq = (solver->heatCapacityRatio - 1.) * (hTotal - eKin);\
	real const Cs = sqrt(CsSq);\
\
	(resultEig)->rho = rho;\
	(resultEig)->v = v;\
	(resultEig)->vSq = vSq;\
	(resultEig)->vL = vLower;\
	(resultEig)->hTotal = hTotal;\
	(resultEig)->Cs = Cs;\
}

//// MODULE_NAME: eigen_left/rightTransform
//// MODULE_DEPENDS: eigen_t normal_t

#define eigen_leftTransform(\
	/*waves_t * const */result,\
	/*constant solver_t const * const */solver,\
	/*eigen_t const * const */eig,\
	/*cons_t const * const */X,\
	/*real3 const */pt,\
	/*normal_t */n\
) {\
	real3 const v_n = normal_vecDotNs(n, (eig)->v);\
	real const nLen = normal_len(n);\
	real const inv_nLen = 1. / nLen;\
	real const denom = 2. * (eig)->Cs * (eig)->Cs;\
	real const invDenom = 1. / denom;\
	real const gamma_1 = solver->heatCapacityRatio - 1.;\
	(result)->ptr[0] = (\
			(X)->ptr[0] * (.5 * gamma_1 * (eig)->vSq + (eig)->Cs * v_n.x * inv_nLen)\
			+ (X)->ptr[1] * (-gamma_1 * (eig)->vL.x - (eig)->Cs * normal_l1x_over_len(n))\
			+ (X)->ptr[2] * (-gamma_1 * (eig)->vL.y - (eig)->Cs * normal_l1y_over_len(n))\
			+ (X)->ptr[3] * (-gamma_1 * (eig)->vL.z - (eig)->Cs * normal_l1z_over_len(n))\
			+ (X)->ptr[4] * gamma_1\
		) * invDenom;\
	(result)->ptr[1] =\
		(\
			(X)->ptr[0] * (denom - gamma_1 * (eig)->vSq)\
			+ (X)->ptr[1] * 2. * gamma_1 * (eig)->vL.x\
			+ (X)->ptr[2] * 2. * gamma_1 * (eig)->vL.y\
			+ (X)->ptr[3] * 2. * gamma_1 * (eig)->vL.z\
			+ (X)->ptr[4] * -2. * gamma_1\
		) * invDenom;\
	(result)->ptr[2] =\
		(X)->ptr[0] * -v_n.y\
		+ (X)->ptr[1] * normal_l2x(n)\
		+ (X)->ptr[2] * normal_l2y(n)\
		+ (X)->ptr[3] * normal_l2z(n);\
	(result)->ptr[3] =\
		(X)->ptr[0] * -v_n.z\
		+ (X)->ptr[1] * normal_l3x(n)\
		+ (X)->ptr[2] * normal_l3y(n)\
		+ (X)->ptr[3] * normal_l3z(n);\
	(result)->ptr[4] =\
		(\
			(X)->ptr[0] * (.5 * gamma_1 * (eig)->vSq - (eig)->Cs * v_n.x * inv_nLen)\
			+ (X)->ptr[1] * (-gamma_1 * (eig)->vL.x + (eig)->Cs * normal_l1x_over_len(n))\
			+ (X)->ptr[2] * (-gamma_1 * (eig)->vL.y + (eig)->Cs * normal_l1y_over_len(n))\
			+ (X)->ptr[3] * (-gamma_1 * (eig)->vL.z + (eig)->Cs * normal_l1z_over_len(n))\
			+ (X)->ptr[4] * gamma_1\
		) * invDenom;\
}

#define eigen_rightTransform(\
	/*cons_t * const */result,\
	/*constant solver_t const * const */solver,\
	/*eigen_t const * const */eig,\
	/*waves_t const * const */X,\
	/*real3 const */pt,\
	/*normal_t const */n\
) {\
	real3 const v_n = normal_vecDotNs(n, (eig)->v);\
	real const nLen = normal_len(n);\
	real const inv_nLen = 1. / nLen;\
	(result)->ptr[0] =\
		(X)->ptr[0]\
		+ (X)->ptr[1]\
		+ (X)->ptr[4];\
	(result)->ptr[1] =\
		(X)->ptr[0] * ((eig)->v.x - (eig)->Cs * normal_u1x_over_len(n))\
		+ (X)->ptr[1] * (eig)->v.x\
		+ (X)->ptr[2] * normal_u2x(n)\
		+ (X)->ptr[3] * normal_u3x(n)\
		+ (X)->ptr[4] * ((eig)->v.x + (eig)->Cs * normal_u1x_over_len(n));\
	(result)->ptr[2] =\
		(X)->ptr[0] * ((eig)->v.y - (eig)->Cs * normal_u1y_over_len(n))\
		+ (X)->ptr[1] * (eig)->v.y\
		+ (X)->ptr[2] * normal_u2y(n)\
		+ (X)->ptr[3] * normal_u3y(n)\
		+ (X)->ptr[4] * ((eig)->v.y + (eig)->Cs * normal_u1y_over_len(n));\
	(result)->ptr[3] =\
		(X)->ptr[0] * ((eig)->v.z - (eig)->Cs * normal_u1z_over_len(n))\
		+ (X)->ptr[1] * (eig)->v.z\
		+ (X)->ptr[2] * normal_u2z(n)\
		+ (X)->ptr[3] * normal_u3z(n)\
		+ (X)->ptr[4] * ((eig)->v.z + (eig)->Cs * normal_u1z_over_len(n));\
	(result)->ptr[4] =\
		(X)->ptr[0] * ((eig)->hTotal - (eig)->Cs * v_n.x * inv_nLen)\
		+ (X)->ptr[1] * .5 * (eig)->vSq\
		+ (X)->ptr[2] * v_n.y\
		+ (X)->ptr[3] * v_n.z\
		+ (X)->ptr[4] * ((eig)->hTotal + (eig)->Cs * v_n.x * inv_nLen);\
	(result)->ptr[0] =\
		0;\
}

//// MODULE_NAME: eigen_fluxTransform
//// MODULE_DEPENDS: eigen_t normal_t
// not used anymore.  was used by Roe, but I switched that to a fluxFromCons.
// is this needed anymore?
// fluxFromCons only matches eigen_fluxTransform when the eig properties are derived from X_

#define eigen_fluxTransform(\
	/*cons_t * const */resultFlux,\
	/*constant solver_t const * const */solver,\
	/*eigen_t const * const */eig,\
	/*cons_t const * const */X_,\
	/*real3 const */x,\
	/*normal_t const */n\
) {\
	real3 const v_n = normal_vecDotNs(n, (eig).v);\
	real const nLen = normal_len(n);\
	const real gamma = solver->heatCapacityRatio;\
	const real gamma_1 = gamma - 1.;\
	const real gamma_2 = gamma - 2.;\
\
	(resultFlux)->ptr[0] =\
		(X_)->ptr[1] * normal_l1x(n)\
		+ (X_)->ptr[2] * normal_l1y(n)\
		+ (X_)->ptr[3] * normal_l1z(n);\
\
	(resultFlux)->ptr[1] =\
		(X_)->ptr[0] * (-v_n.x * (eig).v.x + gamma_1 * .5 * (eig).vSq * normal_u1x(n))\
		+ (X_)->ptr[1] * ((eig).v.x * normal_l1x(n) - gamma_2 * normal_u1x(n) * (eig).vL.x + v_n.x)\
		+ (X_)->ptr[2] * ((eig).v.x * normal_l1y(n) - gamma_2 * normal_u1x(n) * (eig).vL.y)\
		+ (X_)->ptr[3] * ((eig).v.x * normal_l1z(n) - gamma_2 * normal_u1x(n) * (eig).vL.z)\
		+ (X_)->ptr[4] * gamma_1 * normal_u1x(n);\
\
	(resultFlux)->ptr[2] =\
		(X_)->ptr[0] * (-v_n.x * (eig).v.y + gamma_1 * .5 * (eig).vSq * normal_u1y(n))\
		+ (X_)->ptr[1] * ((eig).v.y * normal_l1x(n) - gamma_2 * normal_u1y(n) * (eig).vL.x)\
		+ (X_)->ptr[2] * ((eig).v.y * normal_l1y(n) - gamma_2 * normal_u1y(n) * (eig).vL.y + v_n.x)\
		+ (X_)->ptr[3] * ((eig).v.y * normal_l1z(n) - gamma_2 * normal_u1y(n) * (eig).vL.z)\
		+ (X_)->ptr[4] * gamma_1 * normal_u1y(n);\
\
	(resultFlux)->ptr[3] =\
		(X_)->ptr[0] * (-v_n.x * (eig).v.z + gamma_1 * .5 * (eig).vSq * normal_u1z(n))\
		+ (X_)->ptr[1] * ((eig).v.z * normal_l1x(n) - gamma_2 * normal_u1z(n) * (eig).vL.x)\
		+ (X_)->ptr[2] * ((eig).v.z * normal_l1y(n) - gamma_2 * normal_u1z(n) * (eig).vL.y)\
		+ (X_)->ptr[3] * ((eig).v.z * normal_l1z(n) - gamma_2 * normal_u1z(n) * (eig).vL.z + v_n.x)\
		+ (X_)->ptr[4] * gamma_1 * normal_u1z(n);\
\
	(resultFlux)->ptr[4] =\
		(X_)->ptr[0] * v_n.x * (.5 * gamma_1 * (eig).vSq - (eig).hTotal)\
		+ (X_)->ptr[1] * (normal_l1x(n) * (eig).hTotal - gamma_1 * v_n.x * (eig).vL.x)\
		+ (X_)->ptr[2] * (normal_l1y(n) * (eig).hTotal - gamma_1 * v_n.x * (eig).vL.y)\
		+ (X_)->ptr[3] * (normal_l1z(n) * (eig).hTotal - gamma_1 * v_n.x * (eig).vL.z)\
		+ (X_)->ptr[4] * gamma * v_n.x;\
\
	(resultFlux)->ptr[5] =\
		0;\
}

<? if false then -- TODO sort addSource out. ?>
//// MODULE_NAME: addSource
//// MODULE_DEPENDS: solver_t cons_t cell_t SETBOUNDS_NOGHOST

kernel void addSource(
	constant solver_t const * const solver,
	global cons_t * const derivBuf,
	global cons_t const * const UBuf,
	global cell_t const * const cellBuf
) {
	SETBOUNDS_NOGHOST();
	real3 const x = cellBuf[index].pos;

	global cons_t const * const deriv = derivBuf + index;
	global cons_t const * const U = UBuf + index;

<? if false
and solver.coord.vectorComponent == 'anholonomic'
and require 'hydro.coord.cylinder'.is(solver.coord)
then ?>
<? 	if true then -- 2009 Trangenstein, p.474, 1999 Toro, p.29, eqn.1.104, 1.105 ?>
	<? for side=0,1 do ?>{
		real3 xL = x; xL.s<?=side?> -= solver->grid_dx.s<?=side?>;
		real3 xR = x; xR.s<?=side?> += solver->grid_dx.s<?=side?>;

		global cons_t const * const UL = U - solver->stepsize.s<?=side?>;
		global cons_t const * const UR = U + solver->stepsize.s<?=side?>;
		real const PL = calc_P(solver, UL, xL);
		real const PR = calc_P(solver, UR, xR);

		deriv->m.s<?=side?> -= (PR - PL) / (2. * solver->grid_dx.s<?=side?>);
	}<? end ?>
<?	end ?>
<?	if false then -- 1999 Toro p.28 eqn.1.102, 1.103 ?>
	cons_t F;
	fluxFromConsForSide0(&F, solver, U, x);
	deriv->rho -= F.rho / x.x;
	deriv->m.x -= F.m.x / x.x;
	deriv->m.y -= F.m.y / x.x;
	deriv->m.z -= F.m.z / x.x;
	deriv->ETotal -= F.ETotal / x.x;
<?	end ?>
<? end ?>

<? do -- if not solver.coord.vectorComponent == 'anholonomic' then ?>
<? if not (require 'hydro.coord.cartesian'.is(solver.coord)
		or solver.coord.vectorComponent == 'cartesian')
then ?>
//// MODULE_DEPENDS: primFromCons coord_conn_apply23 coord_conn_trace23 coord_conn_apply13
/*
This is working for init conds with zero velocity.
Introducing constant velocity of v=[x=1,y=1]=[r=sqrt(2),theta=pi/4] in the init cond causes some numerical errors.
However the problem isn't the terms below -- because disabling this for zero-vel init conds causes things to become unsteady.
That means that the volume gradient in calcDerivFV is causing nonzero velocities to emerge, and this is cancelling them.
Maybe for an initial constant vel as large as sqrt(2) this fails, but it works only for small perturbations?
*/
	//connection coefficient source terms of covariant derivative w/contravariant velocity vectors in a holonomic coordinate system
	prim_t W;
	primFromCons(&W, solver, U, x);

	//- Conn^i_jk rho v^j v^k
	deriv->m = real3_sub(deriv->m, coord_conn_apply23(W.v, U->m, x));

	//- Conn^i_jk g^jk P
	deriv->m = real3_sub(deriv->m, real3_real_mul(coord_conn_trace23(x), W.P));

	//+ (gamma-1) rho v^k v^l Gamma_kjl g^ij
	deriv->m = real3_add(deriv->m, real3_real_mul(coord_conn_apply13(W.v, U->m, x), (solver->heatCapacityRatio - 1.) ));

	//- (gamma-1) rho v^j v^k v^l Gamma_jkl
//	deriv->ETotal -= (solver->heatCapacityRatio - 1.) * coord_conn_apply123(W.v, W.v, U->m, x);

	//+ c_jk^k * Flux^Ij
<? 	if false and solver.coord.vectorComponent == 'anholonomic' then ?>
	real3 const commTrace = coord_tr23_c(x);
	<? for i=0,solver.dim-1 do ?>{
		cons_t flux;
		calcFluxFromCons(&F, *U, x);
		for (int j = 0; j < numIntStates; ++j) {
			deriv->ptr[j] += commTrace.s<?=i?> * flux.ptr[j];
		}
	}<? end ?>
<? 	end ?>
<? end ?>
<? end -- vectorComponent == 'anholonomic' ?>
}
<? end ?>

//// MODULE_NAME: constrainU
//// MODULE_DEPENDS: solver_t cons_t cell_t primFromCons consFromPrim

kernel void constrainU(
	constant solver_t const * const solver,
	global cons_t * const UBuf,
	global cell_t const * const cellBuf
) {
	SETBOUNDS(0,0);
	real3 const x = cellBuf[index].pos;

	global cons_t * const U = UBuf + index;
	prim_t W;
	primFromCons(&W, solver, U, x);

	if (W.rho < solver->rhoMin) W.rho = solver->rhoMin;
	if (W.P < solver->PMin) W.P = solver->PMin;

	consFromPrim(U, solver, &W, x);
}


removed unused file

removed unused file

__kernel void matvec_mult(__global float4* matrix,
                          __global float4* vector,
                          __global float* result)
{
    int i = get_global_id(0);
    result[i] = dot(matrix[i], vector[0]);
}


Fixed a complex data-type bug in the transpose kernel

// =================================================================================================
// This file is part of the CLBlast project. The project is licensed under Apache Version 2.0. This
// project loosely follows the Google C++ styleguide and uses a tab-size of two spaces and a max-
// width of 100 characters per line.
//
// Author(s):
//   Cedric Nugteren <www.cedricnugteren.nl>
//
// This file contains the common kernels shared among different BLAS functions. This file contains
// kernels to transpose matrices.
//
// =================================================================================================

// Enables loading of this file using the C++ pre-processor's #include (C++11 standard raw string
// literal). Comment-out this line for syntax-highlighting when developing.
R"(

// =================================================================================================
// Parameters set by the tuner or by the database. Here they are given a basic default value in case
// this kernel file is used outside of the CLBlast library.
#ifndef TRA_DIM
  #define TRA_DIM 8       // Number of local threads in the two dimensions (x,y)
#endif
#ifndef TRA_WPT
  #define TRA_WPT 1       // Work per thread in one dimension and vector-width in the other
#endif
#ifndef TRA_PAD
  #define TRA_PAD 0       // Padding of the local memory to avoid bank-conflicts
#endif
#ifndef TRA_SHUFFLE
  #define TRA_SHUFFLE 0   // Shuffling of the global indices to avoid global memory bank-conflicts
#endif

// =================================================================================================

// Data-widths
#if TRA_WPT == 1
  typedef real realT;
#elif TRA_WPT == 2
  typedef real2 realT;
#elif TRA_WPT == 4
  typedef real4 realT;
#elif TRA_WPT == 8
  typedef real8 realT;
#elif TRA_WPT == 16
  typedef real16 realT;
#endif

// =================================================================================================

// Transposes and copies a matrix. Requires both matrices to be of the same dimensions and without
// offset. A more general version is available in 'padtranspose.opencl'.
__attribute__((reqd_work_group_size(TRA_DIM, TRA_DIM, 1)))
__kernel void TransposeMatrix(const int ld,
                              __global const realT* restrict src,
                              __global realT* dest) {

  // Sets the group identifiers. They might be 'shuffled' around to distribute work in a different
  // way over workgroups, breaking memory-bank dependencies.
  const int gid0 = get_group_id(0);
  #if TRA_SHUFFLE == 1
    const int gid1 = (get_group_id(0) + get_group_id(1)) % get_num_groups(0);
  #else
    const int gid1 = get_group_id(1);
  #endif

  // Local memory to store a tile of the matrix (for coalescing)
  __local realT tile[TRA_WPT*TRA_DIM][TRA_DIM + TRA_PAD];

  // Loops over the work per thread
  #pragma unroll
  for (int w_one=0; w_one<TRA_WPT; ++w_one) {

    // Computes the identifiers for the source matrix. Note that the local and global dimensions
    // do not correspond to each other!
    const int id_one = gid1 * TRA_DIM + get_local_id(0);
    const int id_two = (gid0 * TRA_DIM + get_local_id(1))*TRA_WPT + w_one;

    // Loads data into the local memory
    realT value = src[id_two*(ld/TRA_WPT) + id_one];
    tile[get_local_id(0)*TRA_WPT + w_one][get_local_id(1)] = value;
  }

  // Synchronizes all threads in a workgroup
  barrier(CLK_LOCAL_MEM_FENCE);

  // Loads transposed data from the local memory
  realT v[TRA_WPT];
  #pragma unroll
  for (int w_one=0; w_one<TRA_WPT; ++w_one) {
    v[w_one] = tile[get_local_id(1)*TRA_WPT + w_one][get_local_id(0)];
  }

  // Performs the register-level transpose of the vectorized data
  realT results[TRA_WPT];
  #if TRA_WPT == 1
    results[0] = v[0];
  #elif TRA_WPT == 2
    results[0] = (realT) {v[0].x, v[1].x};
    results[1] = (realT) {v[0].y, v[1].y};
  #elif TRA_WPT == 4
    results[0] = (realT) {v[0].x, v[1].x, v[2].x, v[3].x};
    results[1] = (realT) {v[0].y, v[1].y, v[2].y, v[3].y};
    results[2] = (realT) {v[0].z, v[1].z, v[2].z, v[3].z};
    results[3] = (realT) {v[0].w, v[1].w, v[2].w, v[3].w};
  #elif TRA_WPT == 8
    results[0] = (realT) {v[0].s0, v[1].s0, v[2].s0, v[3].s0, v[4].s0, v[5].s0, v[6].s0, v[7].s0};
    results[1] = (realT) {v[0].s1, v[1].s1, v[2].s1, v[3].s1, v[4].s1, v[5].s1, v[6].s1, v[7].s1};
    results[2] = (realT) {v[0].s2, v[1].s2, v[2].s2, v[3].s2, v[4].s2, v[5].s2, v[6].s2, v[7].s2};
    results[3] = (realT) {v[0].s3, v[1].s3, v[2].s3, v[3].s3, v[4].s3, v[5].s3, v[6].s3, v[7].s3};
    results[4] = (realT) {v[0].s4, v[1].s4, v[2].s4, v[3].s4, v[4].s4, v[5].s4, v[6].s4, v[7].s4};
    results[5] = (realT) {v[0].s5, v[1].s5, v[2].s5, v[3].s5, v[4].s5, v[5].s5, v[6].s5, v[7].s5};
    results[6] = (realT) {v[0].s6, v[1].s6, v[2].s6, v[3].s6, v[4].s6, v[5].s6, v[6].s6, v[7].s6};
    results[7] = (realT) {v[0].s7, v[1].s7, v[2].s7, v[3].s7, v[4].s7, v[5].s7, v[6].s7, v[7].s7};
  #elif TRA_WPT == 16
    results[ 0] = (realT) {v[0].s0, v[1].s0, v[2].s0, v[3].s0, v[4].s0, v[5].s0, v[6].s0, v[7].s0, v[8].s0, v[9].s0, v[10].s0, v[11].s0, v[12].s0, v[13].s0, v[14].s0, v[15].s0};
    results[ 1] = (realT) {v[0].s1, v[1].s1, v[2].s1, v[3].s1, v[4].s1, v[5].s1, v[6].s1, v[7].s1, v[8].s1, v[9].s1, v[10].s1, v[11].s1, v[12].s1, v[13].s1, v[14].s1, v[15].s1};
    results[ 2] = (realT) {v[0].s2, v[1].s2, v[2].s2, v[3].s2, v[4].s2, v[5].s2, v[6].s2, v[7].s2, v[8].s2, v[9].s2, v[10].s2, v[11].s2, v[12].s2, v[13].s2, v[14].s2, v[15].s2};
    results[ 3] = (realT) {v[0].s3, v[1].s3, v[2].s3, v[3].s3, v[4].s3, v[5].s3, v[6].s3, v[7].s3, v[8].s3, v[9].s3, v[10].s3, v[11].s3, v[12].s3, v[13].s3, v[14].s3, v[15].s3};
    results[ 4] = (realT) {v[0].s4, v[1].s4, v[2].s4, v[3].s4, v[4].s4, v[5].s4, v[6].s4, v[7].s4, v[8].s4, v[9].s4, v[10].s4, v[11].s4, v[12].s4, v[13].s4, v[14].s4, v[15].s4};
    results[ 5] = (realT) {v[0].s5, v[1].s5, v[2].s5, v[3].s5, v[4].s5, v[5].s5, v[6].s5, v[7].s5, v[8].s5, v[9].s5, v[10].s5, v[11].s5, v[12].s5, v[13].s5, v[14].s5, v[15].s5};
    results[ 6] = (realT) {v[0].s6, v[1].s6, v[2].s6, v[3].s6, v[4].s6, v[5].s6, v[6].s6, v[7].s6, v[8].s6, v[9].s6, v[10].s6, v[11].s6, v[12].s6, v[13].s6, v[14].s6, v[15].s6};
    results[ 7] = (realT) {v[0].s7, v[1].s7, v[2].s7, v[3].s7, v[4].s7, v[5].s7, v[6].s7, v[7].s7, v[8].s7, v[9].s7, v[10].s7, v[11].s7, v[12].s7, v[13].s7, v[14].s7, v[15].s7};
    results[ 8] = (realT) {v[0].s8, v[1].s8, v[2].s8, v[3].s8, v[4].s8, v[5].s8, v[6].s8, v[7].s8, v[8].s8, v[9].s8, v[10].s8, v[11].s8, v[12].s8, v[13].s8, v[14].s8, v[15].s8};
    results[ 9] = (realT) {v[0].s9, v[1].s9, v[2].s9, v[3].s9, v[4].s9, v[5].s9, v[6].s9, v[7].s9, v[8].s9, v[9].s9, v[10].s9, v[11].s9, v[12].s9, v[13].s9, v[14].s9, v[15].s9};
    results[10] = (realT) {v[0].sA, v[1].sA, v[2].sA, v[3].sA, v[4].sA, v[5].sA, v[6].sA, v[7].sA, v[8].sA, v[9].sA, v[10].sA, v[11].sA, v[12].sA, v[13].sA, v[14].sA, v[15].sA};
    results[11] = (realT) {v[0].sB, v[1].sB, v[2].sB, v[3].sB, v[4].sB, v[5].sB, v[6].sB, v[7].sB, v[8].sB, v[9].sB, v[10].sB, v[11].sB, v[12].sB, v[13].sB, v[14].sB, v[15].sB};
    results[12] = (realT) {v[0].sC, v[1].sC, v[2].sC, v[3].sC, v[4].sC, v[5].sC, v[6].sC, v[7].sC, v[8].sC, v[9].sC, v[10].sC, v[11].sC, v[12].sC, v[13].sC, v[14].sC, v[15].sC};
    results[13] = (realT) {v[0].sD, v[1].sD, v[2].sD, v[3].sD, v[4].sD, v[5].sD, v[6].sD, v[7].sD, v[8].sD, v[9].sD, v[10].sD, v[11].sD, v[12].sD, v[13].sD, v[14].sD, v[15].sD};
    results[14] = (realT) {v[0].sE, v[1].sE, v[2].sE, v[3].sE, v[4].sE, v[5].sE, v[6].sE, v[7].sE, v[8].sE, v[9].sE, v[10].sE, v[11].sE, v[12].sE, v[13].sE, v[14].sE, v[15].sE};
    results[15] = (realT) {v[0].sF, v[1].sF, v[2].sF, v[3].sF, v[4].sF, v[5].sF, v[6].sF, v[7].sF, v[8].sF, v[9].sF, v[10].sF, v[11].sF, v[12].sF, v[13].sF, v[14].sF, v[15].sF};
  #endif

  // Stores the results into the destination matrix
  #pragma unroll
  for (int w_two=0; w_two<TRA_WPT; ++w_two) {
    const int id_one = gid0*TRA_DIM + get_local_id(0);
    const int id_two = (gid1*TRA_DIM + get_local_id(1))*TRA_WPT + w_two;
    dest[id_two*(ld/TRA_WPT) + id_one] = results[w_two];
  }
}

// =================================================================================================

// End of the C++11 raw string literal
)"

// =================================================================================================

// =================================================================================================
// This file is part of the CLBlast project. The project is licensed under Apache Version 2.0. This
// project loosely follows the Google C++ styleguide and uses a tab-size of two spaces and a max-
// width of 100 characters per line.
//
// Author(s):
//   Cedric Nugteren <www.cedricnugteren.nl>
//
// This file contains the common kernels shared among different BLAS functions. This file contains
// kernels to transpose matrices.
//
// =================================================================================================

// Enables loading of this file using the C++ pre-processor's #include (C++11 standard raw string
// literal). Comment-out this line for syntax-highlighting when developing.
R"(

// =================================================================================================
// Parameters set by the tuner or by the database. Here they are given a basic default value in case
// this kernel file is used outside of the CLBlast library.
#ifndef TRA_DIM
  #define TRA_DIM 8       // Number of local threads in the two dimensions (x,y)
#endif
#ifndef TRA_WPT
  #define TRA_WPT 1       // Work per thread in one dimension and vector-width in the other
#endif
#ifndef TRA_PAD
  #define TRA_PAD 0       // Padding of the local memory to avoid bank-conflicts
#endif
#ifndef TRA_SHUFFLE
  #define TRA_SHUFFLE 0   // Shuffling of the global indices to avoid global memory bank-conflicts
#endif

// =================================================================================================

// Data-widths
#if TRA_WPT == 1
  typedef real realT;
#elif TRA_WPT == 2
  typedef real2 realT;
#elif TRA_WPT == 4
  typedef real4 realT;
#elif TRA_WPT == 8
  typedef real8 realT;
#elif TRA_WPT == 16
  typedef real16 realT;
#endif

// =================================================================================================

// Transposes and copies a matrix. Requires both matrices to be of the same dimensions and without
// offset. A more general version is available in 'padtranspose.opencl'.
__attribute__((reqd_work_group_size(TRA_DIM, TRA_DIM, 1)))
__kernel void TransposeMatrix(const int ld,
                              __global const realT* restrict src,
                              __global realT* dest) {

  // Sets the group identifiers. They might be 'shuffled' around to distribute work in a different
  // way over workgroups, breaking memory-bank dependencies.
  const int gid0 = get_group_id(0);
  #if TRA_SHUFFLE == 1
    const int gid1 = (get_group_id(0) + get_group_id(1)) % get_num_groups(0);
  #else
    const int gid1 = get_group_id(1);
  #endif

  // Local memory to store a tile of the matrix (for coalescing)
  __local realT tile[TRA_WPT*TRA_DIM][TRA_DIM + TRA_PAD];

  // Loops over the work per thread
  #pragma unroll
  for (int w_one=0; w_one<TRA_WPT; ++w_one) {

    // Computes the identifiers for the source matrix. Note that the local and global dimensions
    // do not correspond to each other!
    const int id_one = gid1 * TRA_DIM + get_local_id(0);
    const int id_two = (gid0 * TRA_DIM + get_local_id(1))*TRA_WPT + w_one;

    // Loads data into the local memory
    realT value = src[id_two*(ld/TRA_WPT) + id_one];
    tile[get_local_id(0)*TRA_WPT + w_one][get_local_id(1)] = value;
  }

  // Synchronizes all threads in a workgroup
  barrier(CLK_LOCAL_MEM_FENCE);

  // Loads transposed data from the local memory
  realT v[TRA_WPT];
  #pragma unroll
  for (int w_one=0; w_one<TRA_WPT; ++w_one) {
    v[w_one] = tile[get_local_id(1)*TRA_WPT + w_one][get_local_id(0)];
  }

  // Performs the register-level transpose of the vectorized data
  realT results[TRA_WPT];
  #if TRA_WPT == 1
    results[0] = v[0];
  #elif TRA_WPT == 2
    results[0] = (realT) (v[0].x, v[1].x);
    results[1] = (realT) (v[0].y, v[1].y);
  #elif TRA_WPT == 4
    results[0] = (realT) (v[0].x, v[1].x, v[2].x, v[3].x);
    results[1] = (realT) (v[0].y, v[1].y, v[2].y, v[3].y);
    results[2] = (realT) (v[0].z, v[1].z, v[2].z, v[3].z);
    results[3] = (realT) (v[0].w, v[1].w, v[2].w, v[3].w);
  #elif TRA_WPT == 8
    results[0] = (realT) (v[0].s0, v[1].s0, v[2].s0, v[3].s0, v[4].s0, v[5].s0, v[6].s0, v[7].s0);
    results[1] = (realT) (v[0].s1, v[1].s1, v[2].s1, v[3].s1, v[4].s1, v[5].s1, v[6].s1, v[7].s1);
    results[2] = (realT) (v[0].s2, v[1].s2, v[2].s2, v[3].s2, v[4].s2, v[5].s2, v[6].s2, v[7].s2);
    results[3] = (realT) (v[0].s3, v[1].s3, v[2].s3, v[3].s3, v[4].s3, v[5].s3, v[6].s3, v[7].s3);
    results[4] = (realT) (v[0].s4, v[1].s4, v[2].s4, v[3].s4, v[4].s4, v[5].s4, v[6].s4, v[7].s4);
    results[5] = (realT) (v[0].s5, v[1].s5, v[2].s5, v[3].s5, v[4].s5, v[5].s5, v[6].s5, v[7].s5);
    results[6] = (realT) (v[0].s6, v[1].s6, v[2].s6, v[3].s6, v[4].s6, v[5].s6, v[6].s6, v[7].s6);
    results[7] = (realT) (v[0].s7, v[1].s7, v[2].s7, v[3].s7, v[4].s7, v[5].s7, v[6].s7, v[7].s7);
  #elif TRA_WPT == 16
    results[ 0] = (realT) (v[0].s0, v[1].s0, v[2].s0, v[3].s0, v[4].s0, v[5].s0, v[6].s0, v[7].s0, v[8].s0, v[9].s0, v[10].s0, v[11].s0, v[12].s0, v[13].s0, v[14].s0, v[15].s0);
    results[ 1] = (realT) (v[0].s1, v[1].s1, v[2].s1, v[3].s1, v[4].s1, v[5].s1, v[6].s1, v[7].s1, v[8].s1, v[9].s1, v[10].s1, v[11].s1, v[12].s1, v[13].s1, v[14].s1, v[15].s1);
    results[ 2] = (realT) (v[0].s2, v[1].s2, v[2].s2, v[3].s2, v[4].s2, v[5].s2, v[6].s2, v[7].s2, v[8].s2, v[9].s2, v[10].s2, v[11].s2, v[12].s2, v[13].s2, v[14].s2, v[15].s2);
    results[ 3] = (realT) (v[0].s3, v[1].s3, v[2].s3, v[3].s3, v[4].s3, v[5].s3, v[6].s3, v[7].s3, v[8].s3, v[9].s3, v[10].s3, v[11].s3, v[12].s3, v[13].s3, v[14].s3, v[15].s3);
    results[ 4] = (realT) (v[0].s4, v[1].s4, v[2].s4, v[3].s4, v[4].s4, v[5].s4, v[6].s4, v[7].s4, v[8].s4, v[9].s4, v[10].s4, v[11].s4, v[12].s4, v[13].s4, v[14].s4, v[15].s4);
    results[ 5] = (realT) (v[0].s5, v[1].s5, v[2].s5, v[3].s5, v[4].s5, v[5].s5, v[6].s5, v[7].s5, v[8].s5, v[9].s5, v[10].s5, v[11].s5, v[12].s5, v[13].s5, v[14].s5, v[15].s5);
    results[ 6] = (realT) (v[0].s6, v[1].s6, v[2].s6, v[3].s6, v[4].s6, v[5].s6, v[6].s6, v[7].s6, v[8].s6, v[9].s6, v[10].s6, v[11].s6, v[12].s6, v[13].s6, v[14].s6, v[15].s6);
    results[ 7] = (realT) (v[0].s7, v[1].s7, v[2].s7, v[3].s7, v[4].s7, v[5].s7, v[6].s7, v[7].s7, v[8].s7, v[9].s7, v[10].s7, v[11].s7, v[12].s7, v[13].s7, v[14].s7, v[15].s7);
    results[ 8] = (realT) (v[0].s8, v[1].s8, v[2].s8, v[3].s8, v[4].s8, v[5].s8, v[6].s8, v[7].s8, v[8].s8, v[9].s8, v[10].s8, v[11].s8, v[12].s8, v[13].s8, v[14].s8, v[15].s8);
    results[ 9] = (realT) (v[0].s9, v[1].s9, v[2].s9, v[3].s9, v[4].s9, v[5].s9, v[6].s9, v[7].s9, v[8].s9, v[9].s9, v[10].s9, v[11].s9, v[12].s9, v[13].s9, v[14].s9, v[15].s9);
    results[10] = (realT) (v[0].sA, v[1].sA, v[2].sA, v[3].sA, v[4].sA, v[5].sA, v[6].sA, v[7].sA, v[8].sA, v[9].sA, v[10].sA, v[11].sA, v[12].sA, v[13].sA, v[14].sA, v[15].sA);
    results[11] = (realT) (v[0].sB, v[1].sB, v[2].sB, v[3].sB, v[4].sB, v[5].sB, v[6].sB, v[7].sB, v[8].sB, v[9].sB, v[10].sB, v[11].sB, v[12].sB, v[13].sB, v[14].sB, v[15].sB);
    results[12] = (realT) (v[0].sC, v[1].sC, v[2].sC, v[3].sC, v[4].sC, v[5].sC, v[6].sC, v[7].sC, v[8].sC, v[9].sC, v[10].sC, v[11].sC, v[12].sC, v[13].sC, v[14].sC, v[15].sC);
    results[13] = (realT) (v[0].sD, v[1].sD, v[2].sD, v[3].sD, v[4].sD, v[5].sD, v[6].sD, v[7].sD, v[8].sD, v[9].sD, v[10].sD, v[11].sD, v[12].sD, v[13].sD, v[14].sD, v[15].sD);
    results[14] = (realT) (v[0].sE, v[1].sE, v[2].sE, v[3].sE, v[4].sE, v[5].sE, v[6].sE, v[7].sE, v[8].sE, v[9].sE, v[10].sE, v[11].sE, v[12].sE, v[13].sE, v[14].sE, v[15].sE);
    results[15] = (realT) (v[0].sF, v[1].sF, v[2].sF, v[3].sF, v[4].sF, v[5].sF, v[6].sF, v[7].sF, v[8].sF, v[9].sF, v[10].sF, v[11].sF, v[12].sF, v[13].sF, v[14].sF, v[15].sF);
  #endif

  // Stores the results into the destination matrix
  #pragma unroll
  for (int w_two=0; w_two<TRA_WPT; ++w_two) {
    const int id_one = gid0*TRA_DIM + get_local_id(0);
    const int id_two = (gid1*TRA_DIM + get_local_id(1))*TRA_WPT + w_two;
    dest[id_two*(ld/TRA_WPT) + id_one] = results[w_two];
  }
}

// =================================================================================================

// End of the C++11 raw string literal
)"

// =================================================================================================


Added comments

Added comments

;;; Counter program for counting the number of elements in a list and
;;; printing the inst back in a particular format.
;;;
;;; Written by Andrew Lyne February 24, 2015

;;; Checks to make sure passed parameter is a list. Returns nil otherwise.
(defun counter (ele)
	(cond
	((listp ele)
		(counter_helper ele)
	)
	((atom ele)
		NIL
	)
	( T nil)
	)
)

;;; Used to call helper_function_2 on each element of the parameter list.
;;; Automatically removes -1 flag to indicate space character needed.
(defun counter_helper (ele)
	(remove '-1
		(append (list (length ele)) (mapcar (function counter_helper_2) ele))
	))

;;; Recursivly calls helper_function if element passes is a list or returns -1
;;; to indicate non-list element.
(defun counter_helper_2 (ele)
	(cond
	((listp ele)
		(counter_helper ele)
	)
	((atom ele)
		-1
	)
	(T nil)
	)
)

;;;
;;; put comments here
(defun counter (ele)
	(cond
	((listp ele)
		(counter_helper ele)
	)
	((atom ele)
		NIL
	)
	( T nil)
	)
)

(defun counter_helper (ele)
	(remove '-1
		(append (list (length ele)) (mapcar (function counter_helper_2) ele))
	))

(defun counter_helper_2 (ele)
	(cond
	((listp ele)
		(counter_helper ele)
	)
	((atom ele)
		-1
	)
	(T nil)
	)
)


Something kernel header

Something kernel header

/******************************************************************************
 * function: wah_bitmap                                                       *
 * input:    32-bit integer                                                   *
 * output:   wah compressed bitmap index, offsets for its values              *
 ******************************************************************************/

#define FENCE_TYPE CLK_LOCAL_MEM_FENCE
#define WORK_GROUP_SIZE 1024

// Processing steps
void sort_rids_by_value(global uint* input, global  uint* rids,
                        size_t idx, size_t total);
void produce_chunck_id_literals(global uint* rids, global uint* chids,
                                global uint* lits, size_t idx, size_t total);
size_t merged_lit_by_val_chids(global uint* input, global uint* chids,
                               global uint* lits, size_t idx, size_t total);
void produce_fills(global uint* input, global uint* chids,
                   size_t idx, size_t total);
size_t fuse_fill_literals(global uint* chids, global uint* lits,
                          global uint* index, size_t idx, size_t total);
size_t compute_colum_length(global uint* input, global uint* chids,
                            global uint* offsets, size_t idx, size_t total);

// helper functions
void parallel_selection_sort(global uint* key, global uint* data,
                             size_t idx, size_t total);
size_t reduce_by_key_OR(global uint* keys_high, global uint* keys_low,
                        global uint* data, size_t idx, size_t total);
size_t reduce_by_key_SUM(global uint* keys, local uint* data,
                         size_t idx, size_t total);

// main kernel
kernel void kernel_wah_index(global uint* config,
                             global uint* input,
                             global uint* index,
                             global uint* offsets,
                             global uint* rids,
                             global uint* chids,
                             global uint* lits) {
  // just 1 dimension here: 0
  size_t total = get_global_size(0);
  size_t idx = get_global_id(0);
  if (total > WORK_GROUP_SIZE) {
    offsets[idx] = total;
    index[idx] = WORK_GROUP_SIZE;
    return;
  }
  // CONSIDERATIONS:
  // * some scaling state of the are functions required
  //  - sorting
  //  - scan
  //  - stream compaction
  //  - reduce by key
  // * when do we need barriers?
  //  - at the end of functions, before return?
  //  - can be just assign return values to global stuff
  //  - ...

  // read config data
  // uint input_size       = config[0];
  // uint index_size       = config[1]; should be 2* input size
  // uint processed_ values = config[2];

  // Currently 1 work item per value
  total = config[0];

  //__gloabl uint[index_size] rids;
  sort_rids_by_value(input, rids, idx, total);
  barrier(FENCE_TYPE);
  //index[idx] = rids[idx];
  //offsets[idx] = 0;
  //config[4] = total;
  produce_chunck_id_literals(rids, chids, lits, idx, total);
  barrier(FENCE_TYPE);
  //input[idx] = rids[idx];
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  //config[4] = total;
  size_t k = merged_lit_by_val_chids(input, chids, lits, idx, total);
  barrier(FENCE_TYPE);
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  //config[3] = (uint) k;
  //config[4] = (uint) k;
  //config[5] = (uint) k;
  produce_fills(input, chids, idx, k);
  barrier(FENCE_TYPE);
  //config[4] = k;
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  uint idxlen = fuse_fill_literals(chids, lits, index, idx, k);
  barrier(FENCE_TYPE);
  config[1] = idxlen;
  uint keycnt = compute_colum_length(input, chids, offsets, idx, k);
  barrier(FENCE_TYPE);
  config[0] = keycnt;
}

void sort_rids_by_value(global uint* input,global uint* rids,
                        size_t idx, size_t total) {
  rids[idx] = idx;
  barrier(FENCE_TYPE); // is this needed here?
  // sort by input value
  parallel_selection_sort(input, rids, idx, total);
}

void produce_chunck_id_literals(global uint* rids, global uint* chids,
                                global uint* lits, size_t idx, size_t total) {
  (void) total;
  lits[idx] = 1u << (rids[idx] % 31u);
  lits[idx] |= 1u << 31;
  chids[idx] = (uint) rids[idx] / 31;
}

size_t merged_lit_by_val_chids(global uint* input, global uint* chids,
                               global uint* lits, size_t idx, size_t total) {
  // avoid merge of chids and input into keys,
  // simply pass them both
  size_t k = reduce_by_key_OR(chids, input, lits, idx, total);
  return k;
}

void produce_fills(global uint* input, global uint* chids,
                   size_t idx, size_t total) {
  if (idx != 0 && (input[idx] == input[idx - 1] &&
                   chids[idx] == chids[idx - 1])) {
    chids[idx] = chids[idx] - chids[idx - 1] - 1;
  } else {
    if (chids[idx] != 0) {
      chids[idx] = chids[idx] - 1;
    }
  }
}

size_t fuse_fill_literals(global uint* chids, global uint* lits,
                          global uint* index, size_t idx, size_t total) {
  local uint markers [WORK_GROUP_SIZE * 2];
  local uint position[WORK_GROUP_SIZE * 2];
  volatile local int len;
  //uint a =  2 * idx;
  //uint b = (2 * idx) + 1;
  len = 0;
  if (idx < total) {
    index[ 2 * idx     ] = chids[idx];
    index[(2 * idx) + 1] = lits[idx];
    barrier(FENCE_TYPE);
    // stream compaction
    markers[ 2 * idx     ] = index[ 2 * idx     ] != 0; // ? 1 : 0;
    markers[(2 * idx) + 1] = index[(2 * idx) + 1] != 0; // ? 1 : 0;
    barrier(FENCE_TYPE);
    // should be a parallel scan
    if (idx == 0) {
      position[0] = 0;
      for (uint i = 1; i < 2 * total; ++i) {
        position[i] = position[i - 1] + markers[i - 1];
      }
    }
    uint tmp_a = index[2 * idx];
    uint tmp_b = index[(2 * idx) + 1];
    barrier(FENCE_TYPE);
    if (markers[2 * idx] == 1) {
      index[position[2 * idx]] = tmp_a;
      atomic_add(&len, 1);
    }
    if (markers[(2 * idx) + 1] == 1) {
      index[position[(2 * idx) + 1]] = tmp_b;
      atomic_add(&len, 1);
    }
  }
  barrier(FENCE_TYPE); // <-- should there be a barrier here?
  return len;
}

size_t compute_colum_length(global uint* input, global uint* chids,
                            global uint* offsets, size_t idx, size_t k) {
  local uint tmp[WORK_GROUP_SIZE];
  tmp[idx] = (1 + (chids[idx] == 0 ? 0 : 1));
  uint keycnt = reduce_by_key_SUM(input, tmp, idx, k);
  // inclusive scan to create offsets, should be parallel
  barrier(FENCE_TYPE);
  if (idx == 0) {
    offsets[0] = 0;
    for (uint i = 1; i < keycnt; ++i) {
      offsets[i] = offsets[i - 1] + tmp[i - 1];
    }
  }
  return keycnt;
}

// Helper functions

// we have 64 bit keys consisting of (high << 32 | low), but want to
// avoid the extra copy, so ...
size_t reduce_by_key_OR(global uint* keys_high, global uint* keys_low,
                        global uint* data, size_t idx, size_t total) {
  local uint heads[WORK_GROUP_SIZE];
  volatile local int k;
  heads[idx] = (idx == 0) ||
               (keys_high[idx] != keys_high[idx - 1]) ||
               (keys_low [idx] != keys_low [idx - 1]);
  barrier(FENCE_TYPE);
  if (heads[idx] != 0) {
    uint val = data[idx];
    uint curr = idx + 1;
    while (heads[curr] == 0 && curr < total) {
      val |= data[curr]; // OR operation
      curr += 1;
    }
    atomic_add(&k, 1);
    data[idx] = val;
  }
  barrier(FENCE_TYPE);
  if (idx == 0) {
    uint pos = 0;
    for (uint i = 0; i < total; ++i) {
      if (heads[i] != 0) {
        keys_high[pos] = keys_high[i];
        keys_low[pos] = keys_low[i];
        data[pos] = data[i];
        pos += 1;
      }
    }
  }
  return (uint) k;
}

size_t reduce_by_key_SUM(global uint* keys, local uint* data,
                         size_t idx, size_t total) {
  local uint heads[WORK_GROUP_SIZE];
  heads[idx] = idx == 0 || (keys[idx] != keys[idx - 1]);
  volatile local int k;
  if (heads[idx] != 0 && idx < total) {
    uint val = data[idx];
    uint curr = idx + 1;
    while (heads[curr] == 0 && curr < total) {
      val += data[curr]; // SUM operation
      curr += 1;
    }
    atomic_add(&k, 1);
    data[idx] = val;
  }
  barrier(FENCE_TYPE);
  if (idx == 0) {
    uint pos = 0;
    for (uint i = 0; i < total; ++i) {
      if (heads[i] != 0) {
        keys[pos] = keys[i];
        data[pos] = data[i];
        pos += 1;
      }
    }
  }
  return (uint) k;
}

/**
 * Sort found on: http://www.bealto.com/gpu-sorting_intro.html
 * License:set syntax=:
 *  This code is released under the following license (BSD-style).
 *  --
 *
 *  Copyright (c) 2011, Eric Bainville
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *      * Neither the name of Eric Bainville nor the
 *        names of its contributors may be used to endorse or promote products
 *        derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY ERIC BAINVILLE ''AS IS'' AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL ERIC BAINVILLE BE LIABLE FOR ANY
 *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 **/

void parallel_selection_sort(global uint* key, global uint* data,
                             size_t idx, size_t total) {
  uint key_value = key[idx];
  uint data_value = data[idx];
  // Compute position of in[i] in output
  int pos = 0;
  for (size_t j = 0; j < total; ++j) {
    uint curr = key[j]; // broadcasted
    bool smaller = (curr < key_value) || (curr == key_value && j < idx);
    pos += (smaller) ? 1 : 0;
  }
  barrier(CLK_LOCAL_MEM_FENCE);
  key[pos] = key_value;
  data[pos] = data_value;
}

/*
 * function: wah_bitmap
 * input:    32-bit integer
 * output:   wah compressed bitmap index, offsets for its values
 * table:
 */

#define FENCE_TYPE CLK_LOCAL_MEM_FENCE
#define WORK_GROUP_SIZE 1024

// Processing steps
void sort_rids_by_value(global uint* input, global  uint* rids,
                        size_t idx, size_t total);
void produce_chunck_id_literals(global uint* rids, global uint* chids,
                                global uint* lits, size_t idx, size_t total);
size_t merged_lit_by_val_chids(global uint* input, global uint* chids,
                               global uint* lits, size_t idx, size_t total);
void produce_fills(global uint* input, global uint* chids,
                   size_t idx, size_t total);
size_t fuse_fill_literals(global uint* chids, global uint* lits,
                          global uint* index, size_t idx, size_t total);
size_t compute_colum_length(global uint* input, global uint* chids,
                            global uint* offsets, size_t idx, size_t total);

// helper functions
void parallel_selection_sort(global uint* key, global uint* data,
                             size_t idx, size_t total);
size_t reduce_by_key_OR(global uint* keys_high, global uint* keys_low,
                        global uint* data, size_t idx, size_t total);
size_t reduce_by_key_SUM(global uint* keys, local uint* data,
                         size_t idx, size_t total);

// main kernel
kernel void kernel_wah_index(global uint* config,
                             global uint* input,
                             global uint* index,
                             global uint* offsets,
                             global uint* rids,
                             global uint* chids,
                             global uint* lits) {
  // just 1 dimension here: 0
  size_t total = get_global_size(0);
  size_t idx = get_global_id(0);
  if (total > WORK_GROUP_SIZE) {
    offsets[idx] = total;
    index[idx] = WORK_GROUP_SIZE;
    return;
  }
  // CONSIDERATIONS:
  // * some scaling state of the are functions required
  //  - sorting
  //  - scan
  //  - stream compaction
  //  - reduce by key
  // * when do we need barriers?
  //  - at the end of functions, before return?
  //  - can be just assign return values to global stuff
  //  - ...

  // read config data
  // uint input_size       = config[0];
  // uint index_size       = config[1]; should be 2* input size
  // uint processed_ values = config[2];

  // Currently 1 work item per value
  total = config[0];

  //__gloabl uint[index_size] rids;
  sort_rids_by_value(input, rids, idx, total);
  barrier(FENCE_TYPE);
  //index[idx] = rids[idx];
  //offsets[idx] = 0;
  //config[4] = total;
  produce_chunck_id_literals(rids, chids, lits, idx, total);
  barrier(FENCE_TYPE);
  //input[idx] = rids[idx];
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  //config[4] = total;
  size_t k = merged_lit_by_val_chids(input, chids, lits, idx, total);
  barrier(FENCE_TYPE);
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  //config[3] = (uint) k;
  //config[4] = (uint) k;
  //config[5] = (uint) k;
  produce_fills(input, chids, idx, k);
  barrier(FENCE_TYPE);
  //config[4] = k;
  //index[idx] = chids[idx];
  //offsets[idx] = lits[idx];
  uint idxlen = fuse_fill_literals(chids, lits, index, idx, k);
  barrier(FENCE_TYPE);
  config[1] = idxlen;
  uint keycnt = compute_colum_length(input, chids, offsets, idx, k);
  barrier(FENCE_TYPE);
  config[0] = keycnt;
}

void sort_rids_by_value(global uint* input,global uint* rids,
                        size_t idx, size_t total) {
  rids[idx] = idx;
  barrier(FENCE_TYPE); // is this needed here?
  // sort by input value
  parallel_selection_sort(input, rids, idx, total);
}

void produce_chunck_id_literals(global uint* rids, global uint* chids,
                                global uint* lits, size_t idx, size_t total) {
  (void) total;
  lits[idx] = 1u << (rids[idx] % 31u);
  lits[idx] |= 1u << 31;
  chids[idx] = (uint) rids[idx] / 31;
}

size_t merged_lit_by_val_chids(global uint* input, global uint* chids,
                               global uint* lits, size_t idx, size_t total) {
  // avoid merge of chids and input into keys,
  // simply pass them both
  size_t k = reduce_by_key_OR(chids, input, lits, idx, total);
  return k;
}

void produce_fills(global uint* input, global uint* chids,
                   size_t idx, size_t total) {
  if (idx != 0 && (input[idx] == input[idx - 1] &&
                   chids[idx] == chids[idx - 1])) {
    chids[idx] = chids[idx] - chids[idx - 1] - 1;
  } else {
    if (chids[idx] != 0) {
      chids[idx] = chids[idx] - 1;
    }
  }
}

size_t fuse_fill_literals(global uint* chids, global uint* lits,
                          global uint* index, size_t idx, size_t total) {
  local uint markers [WORK_GROUP_SIZE * 2];
  local uint position[WORK_GROUP_SIZE * 2];
  volatile local int len;
  //uint a =  2 * idx;
  //uint b = (2 * idx) + 1;
  len = 0;
  if (idx < total) {
    index[ 2 * idx     ] = chids[idx];
    index[(2 * idx) + 1] = lits[idx];
    barrier(FENCE_TYPE);
    // stream compaction
    markers[ 2 * idx     ] = index[ 2 * idx     ] != 0; // ? 1 : 0;
    markers[(2 * idx) + 1] = index[(2 * idx) + 1] != 0; // ? 1 : 0;
    barrier(FENCE_TYPE);
    // should be a parallel scan
    if (idx == 0) {
      position[0] = 0;
      for (uint i = 1; i < 2 * total; ++i) {
        position[i] = position[i - 1] + markers[i - 1];
      }
    }
    uint tmp_a = index[2 * idx];
    uint tmp_b = index[(2 * idx) + 1];
    barrier(FENCE_TYPE);
    if (markers[2 * idx] == 1) {
      index[position[2 * idx]] = tmp_a;
      atomic_add(&len, 1);
    }
    if (markers[(2 * idx) + 1] == 1) {
      index[position[(2 * idx) + 1]] = tmp_b;
      atomic_add(&len, 1);
    }
  }
  barrier(FENCE_TYPE); // <-- should there be a barrier here?
  return len;
}

size_t compute_colum_length(global uint* input, global uint* chids,
                            global uint* offsets, size_t idx, size_t k) {
  local uint tmp[WORK_GROUP_SIZE];
  tmp[idx] = (1 + (chids[idx] == 0 ? 0 : 1));
  uint keycnt = reduce_by_key_SUM(input, tmp, idx, k);
  // inclusive scan to create offsets, should be parallel
  barrier(FENCE_TYPE);
  if (idx == 0) {
    offsets[0] = 0;
    for (uint i = 1; i < keycnt; ++i) {
      offsets[i] = offsets[i - 1] + tmp[i - 1];
    }
  }
  return keycnt;
}

// Helper functions

// we have 64 bit keys consisting of (high << 32 | low), but want to
// avoid the extra copy, so ...
size_t reduce_by_key_OR(global uint* keys_high, global uint* keys_low,
                        global uint* data, size_t idx, size_t total) {
  local uint heads[WORK_GROUP_SIZE];
  volatile local int k;
  heads[idx] = (idx == 0) ||
               (keys_high[idx] != keys_high[idx - 1]) ||
               (keys_low [idx] != keys_low [idx - 1]);
  barrier(FENCE_TYPE);
  if (heads[idx] != 0) {
    uint val = data[idx];
    uint curr = idx + 1;
    while (heads[curr] == 0 && curr < total) {
      val |= data[curr]; // OR operation
      curr += 1;
    }
    atomic_add(&k, 1);
    data[idx] = val;
  }
  barrier(FENCE_TYPE);
  if (idx == 0) {
    uint pos = 0;
    for (uint i = 0; i < total; ++i) {
      if (heads[i] != 0) {
        keys_high[pos] = keys_high[i];
        keys_low[pos] = keys_low[i];
        data[pos] = data[i];
        pos += 1;
      }
    }
  }
  return (uint) k;
}

size_t reduce_by_key_SUM(global uint* keys, local uint* data,
                         size_t idx, size_t total) {
  local uint heads[WORK_GROUP_SIZE];
  heads[idx] = idx == 0 || (keys[idx] != keys[idx - 1]);
  volatile local int k;
  if (heads[idx] != 0 && idx < total) {
    uint val = data[idx];
    uint curr = idx + 1;
    while (heads[curr] == 0 && curr < total) {
      val += data[curr]; // SUM operation
      curr += 1;
    }
    atomic_add(&k, 1);
    data[idx] = val;
  }
  barrier(FENCE_TYPE);
  if (idx == 0) {
    uint pos = 0;
    for (uint i = 0; i < total; ++i) {
      if (heads[i] != 0) {
        keys[pos] = keys[i];
        data[pos] = data[i];
        pos += 1;
      }
    }
  }
  return (uint) k;
}

/**
 * Sort found on: http://www.bealto.com/gpu-sorting_intro.html
 * License:set syntax=:
 *  This code is released under the following license (BSD-style).
 *  --
 *
 *  Copyright (c) 2011, Eric Bainville
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *      * Neither the name of Eric Bainville nor the
 *        names of its contributors may be used to endorse or promote products
 *        derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY ERIC BAINVILLE ''AS IS'' AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL ERIC BAINVILLE BE LIABLE FOR ANY
 *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 **/

void parallel_selection_sort(global uint* key, global uint* data,
                             size_t idx, size_t total) {
  uint key_value = key[idx];
  uint data_value = data[idx];
  // Compute position of in[i] in output
  int pos = 0;
  for (size_t j = 0; j < total; ++j) {
    uint curr = key[j]; // broadcasted
    bool smaller = (curr < key_value) || (curr == key_value && j < idx);
    pos += (smaller) ? 1 : 0;
  }
  barrier(CLK_LOCAL_MEM_FENCE);
  key[pos] = key_value;
  data[pos] = data_value;
}


Apply max trace depth limit to overcome intel opencl compiler OOM bug

// Global parameters that will be set via a template during compilation time.
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
#define FRAME_W 512
#define FRAME_H 512
#define TEXEL_STEP_X 1.0f / 512.0f
#define TEXEL_STEP_Y 1.0f / 512.0f
#define NO_SURFACE_HIT -1

// Limits
#define MAX_TRACE_STEPS 32
#define MAX_BOUNCES 4 // iris pro compilation runs out of memory if set to > 5
#define MIN_BOUNCES_TO_USE_RR 3

// Epsilon values
#define NUDGE_EPSILON 1.0e-2f
#define DIST_SNAP_EPSILON 1.0e-3f

// Vector helpers
#define VEC3_MAX_COMPONENT(v) max(v.x, max(v.y, v.z))
#define VEC3(x,y,z) (float4)(x,y,z,0.0f)
#define VEC3_ORIGIN VEC3(0,0,0)

// Supported surface types
#define SURFACE_DIFFUSE 0
#define SURFACE_SPECULAR 1
#define SURFACE_REFRACTIVE 2
#define SURFACE_LIGHT 65535

// DEBUGGING
#define DEBUG (get_global_id(0)==256 && get_global_id(1)==256)

// Distance estimator function generators
// --------------------------------------
#define PLANE_SD_FN(objIndex, origin, planeDims) float distToObj##objIndex( float4 point ); \
float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float4 dims = normalize(planeDims); \
	return dot(point, dims); \
}
#define SPHERE_SD_FN(objIndex, origin, radius) float distToObj##objIndex( float4 point ); \
float distToObj##objIndex( float4 point ){ \
	return length(point - origin) - radius; \
}
#define BOX_SD_FN(objIndex, origin, boxDims) float distToObj##objIndex( float4 point ); \
float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float4 dims = boxDims; \
	float4 d = fabs(point) - (float4)(dims.xyz, point.w); \
	return fmin(VEC3_MAX_COMPONENT(d), 0.0f) + length(fmax(d, 0.0f)); \
}
#define TORUS_SD_FN(objIndex, origin, torusDims) float distToObj##objIndex( float4 point ) \
float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float2 dims = torusDims; \
	return length((float2)(length(point.xz) - dims.x, point.y)) - dims.y; \
}

// Normal estimator generators
#define OBJECT_NORMAL_AT(objIndex, point) \
	normalize( \
		(float4)( \
			distToObj##objIndex(point + VEC3(NUDGE_EPSILON, 0,0)) - distToObj##objIndex(point - VEC3(NUDGE_EPSILON, 0,0)), \
			distToObj##objIndex(point + VEC3(0, NUDGE_EPSILON,0)) - distToObj##objIndex(point - VEC3(0, NUDGE_EPSILON,0)), \
			distToObj##objIndex(point + VEC3(0, 0, NUDGE_EPSILON)) - distToObj##objIndex(point - VEC3(0, 0, NUDGE_EPSILON)), \
			0.0f \
		) \
	)

// Random number and direction generators
// -----------------------
float2 clRand(uint2 *state);
float2 clRand(uint2 *state)
{
    const float2 invMaxInt = (float2) (1.0f/4294967296.0f, 1.0f/4294967296.0f);
    uint x = (*state).x * 17 + (*state).y * 13123;
    (*state).x = (x<<13) ^ x;
    (*state).y ^= (x<<7);

    uint2 tmp = (uint2)
    (
		(x * (x * x * 15731 + 74323) + 871483),
		(x * (x * x * 13734 + 37828) + 234234)
	);

    return convert_float2(tmp) * invMaxInt;
}

// Return random direction on hemiphere around normal.
// This function has: PDF = cos(theta) / pi
float4 rndCosWeightedHemisphereDir(float4 normal, uint2 *seed);
float4 rndCosWeightedHemisphereDir(float4 normal, uint2 *seed) {
	float2 rnd  = clRand(seed);

	// Generate point on disk
	float rd = sqrt(rnd.x);
	float phi = 2.0f*M_PI*rnd.y;

	// Generate tangent, bi-tangent vectors
	float4 u = normalize(cross((fabs(normal.x) > .1f ? (float4)(0.0f, 1.0f, 0.0f, 0.0f) : (float4)(1.0f, 0.0f, 0.0f, 0.0f)), normal));
	float4 v = cross(normal,u);

	// Project disk point to unit hemisphere and rotate so that the normal points up
	return normalize(u * rd * cos(phi) + v * rd * sin(phi) + normal * sqrt(1 - rnd.x));
}

// Tracer implementation
// ---------------------

typedef struct {
	float4 position;
	float4 normal;
	float distFromOrigin;
	int objIndex;
	int matIndex;
} Hit;

typedef struct {
	unsigned int type;
	float4 diffuse;
	float4 emissive;
} Material;

// Material definitions
// The following code will be autogenerated by the scene processor
// ------------------
__constant float4 sceneBgColor = (float4)(0.0f);

__constant Material SceneMaterial[] = {
	{SURFACE_LIGHT, VEC3(0.0f,0.0f,0.0f), VEC3(60.0f,60.0f,60.0f)},
	{SURFACE_DIFFUSE, VEC3(0.75f, 0.25f, 0.25f)},
	{SURFACE_DIFFUSE, VEC3(0.25f, 0.25f, 0.75f)},
	{SURFACE_DIFFUSE, VEC3(0.75f, 0.75f, 0.75f)},
	{SURFACE_SPECULAR, VEC3(0.999f, 0.999f, 0.999f)},
	{SURFACE_REFRACTIVE, VEC3(0.999f, 0.999f, 0.999f)}
};

// Object definitions
// The following code will be autogenerated by the scene processor
// ------------------

// Light
SPHERE_SD_FN(0, VEC3(0.0f, 1.5f, 0.0f), 0.1f)
// Left wall
BOX_SD_FN(1, VEC3(-2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f))
// Right wall
BOX_SD_FN(2, VEC3(2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f))
// Top wall
BOX_SD_FN(3, VEC3(0.0f,2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f))
// Bottom wall
BOX_SD_FN(4, VEC3(0.0f,-2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f))
// Back wall
BOX_SD_FN(5, VEC3(0,0,-2), VEC3(2.0f, 2.0f, 0.1f))
// Mirror ball
SPHERE_SD_FN(6, VEC3(-1.0f, -1.4f, -1.0f), 0.5f)
// Glass ball
SPHERE_SD_FN(7, VEC3(1.4f, -1.4f, -0.5f), 0.5f)

// Estimate normal at intersection point
void getNormal(Hit *hit);
void getNormal(Hit *hit){
	// The following code will be autogenerated by the scene processor
	if( hit->objIndex == 0 ){
		hit->normal = OBJECT_NORMAL_AT(0, hit->position);
	} else if( hit->objIndex == 1 ){
		hit->normal = OBJECT_NORMAL_AT(1, hit->position);
	} else if( hit->objIndex == 2 ){
		hit->normal = OBJECT_NORMAL_AT(2, hit->position);
	} else if( hit->objIndex == 3 ){
		hit->normal = OBJECT_NORMAL_AT(3, hit->position);
	} else if( hit->objIndex == 4 ){
		hit->normal = OBJECT_NORMAL_AT(4, hit->position);
	} else if( hit->objIndex == 5 ){
		hit->normal = OBJECT_NORMAL_AT(5, hit->position);
	} else if( hit->objIndex == 6 ){
		hit->normal = OBJECT_NORMAL_AT(6, hit->position);
	} else if( hit->objIndex == 7 ){
		hit->normal = OBJECT_NORMAL_AT(7, hit->position);
	}
}

// Estimate distance to nearest object
void intersectWorld(const float4 rayOrigin, const float4 rayDir, const float minDist, const float maxDist, Hit *hit);
void intersectWorld(const float4 rayOrigin, const float4 rayDir, const float minDist, const float maxDist, Hit *hit){
	hit->objIndex = NO_SURFACE_HIT;
	hit->matIndex = NO_SURFACE_HIT;

	float4 point;
	float curDist = minDist;
	float objDist, nearestDist;
	int nearestObj = -1;
	int nearestMat = -1;
	for(unsigned int step=0;step<MAX_TRACE_STEPS;step++){
		point = rayOrigin + rayDir * curDist;

		// Find nearest object/dist to point
		// The following code will be autogenerated by the scene processor
		nearestDist = FLT_MAX;
		objDist = distToObj0(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 0;
			nearestMat = 0;
		}
		objDist = distToObj1(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 1;
			nearestMat = 1;
		}
		objDist = distToObj2(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 2;
			nearestMat = 2;
		}
		objDist = distToObj3(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 3;
			nearestMat = 3;
		}
		objDist = distToObj4(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 4;
			nearestMat = 3;
		}
		objDist = distToObj5(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 5;
			nearestMat = 3;
		}
		objDist = distToObj6(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 6;
			nearestMat = 4;
		}
		objDist = distToObj7(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 7;
			nearestMat = 5;
		}

		// If we are inside an object nearestDist will be < 0
		nearestDist = fabs(nearestDist);

		curDist += nearestDist;
		if(curDist > maxDist){
			return;
		}

		// We are close enough to the object to register a hit
		if(nearestDist < DIST_SNAP_EPSILON){
			hit->position = point;
			hit->distFromOrigin = curDist;
			hit->objIndex = nearestObj;
			hit->matIndex = nearestMat;
			return;
		}
	}
}

// Trace a ray and return the gathered color.
float4 traceRay(float4 rayOrigin, float4 rayDir, uint2 *rndSeed);
float4 traceRay(float4 rayOrigin, float4 rayDir, uint2 *rndSeed){
	Hit hit;
	__constant Material *material;
	float4 rCol = (float4)(0.0f);
	float4 mask = (float4)(1.0f);
	unsigned int bounce = 0;
	for(bounce=0;bounce<MAX_BOUNCES;bounce++){
		intersectWorld(rayOrigin, rayDir, NUDGE_EPSILON, FLT_MAX, &hit);

		// No hit
		if(hit.objIndex == NO_SURFACE_HIT){
			if(bounce == 0){
				rCol = sceneBgColor;
			}
			break;
		}

		// Get material at hit point
		material = &SceneMaterial[hit.matIndex];

		// If we hit a light just add its emissive property and stop
		if( material->type == SURFACE_LIGHT ){
			rCol += mask * material->emissive;
			break;
		}

		// Get normal at hit point
		getNormal(&hit);

		// After some bounces apply a russian roulette to terminate long paths
		if( bounce > MIN_BOUNCES_TO_USE_RR ) {
			float maxComponent = VEC3_MAX_COMPONENT(material->diffuse);
			if( maxComponent == 0 || clRand(rndSeed).x > maxComponent ){
				break;
			}

			// Boost surviving paths by 1/rr_prob
			mask /= maxComponent;
		}

		// Mask outgoing reflectance by material diffuse property
		mask *= material->diffuse;

		// Next bounce starts at hit point
		rayOrigin = hit.position;

		if( material->type == SURFACE_DIFFUSE ) {
			// We do importance sampling for diffuse rays
			rayDir = rndCosWeightedHemisphereDir(hit.normal, rndSeed);
		} else{
			// Reflect ray around normal
			rayDir = normalize(-2.0f * dot(hit.normal, rayDir) * hit.normal + rayDir);
		}

		++bounce;
	}

	return rCol;
}

// Emit the color of a pixel by tracing a ray through the scene.
__kernel void tracePixel(
		__global float4 *frameBuffer,
		__global float4 *frustrumCorners,
		const float4 eyePos,
		const unsigned int blockY,
		const unsigned int samplesPerPixel,
		const float exposure,
		const int seed
		){
	// Get pixel coordinates
	unsigned int x = get_global_id(0);
	unsigned int y = get_global_id(1);
	if ( x > FRAME_W || y > FRAME_H ) {
		return;
	}

	// Setup seed for random numbers
	uint2 rndSeed = (uint2)(x * seed, y * seed);

	// Calculate texel coordinates [0,1] range
	float accumScaler = 1.0f / (float)samplesPerPixel;
	float4 accum = (float4)(0.0f,0.0f,0.0f,0.0f);

	for( uint sample=0;sample<samplesPerPixel;sample++){
		// Apply stratified sampling using a tent filter. This will wrap our
		// random numbers in the [-1, 1] range. X and Y point to the top corner
		// of the current texel so we need to add a bit of offset to get the coords
		// into the [-0.5, 1.5] range.
		float2 random = clRand(&rndSeed);
		float offX = random.x < 0.5f ? sqrt(2.0f * random.x) - 0.5f : 1.5f - sqrt(2.0f - 2.0f * random.x);
		float offY = random.y < 0.5f ? sqrt(2.0f * random.y) - 0.5f : 1.5f - sqrt(2.0f - 2.0f * random.y);
		float tx = ((float)x + offX) * TEXEL_STEP_X;
		float ty = ((float)y + offY) * TEXEL_STEP_Y;

		// Get ray direction using trilinear interpolation; trace ray and
		// add result to accumulation buffer
		float4 lVec = frustrumCorners[0] * (1 - ty) + frustrumCorners[2] * ty;
		float4 rVec = frustrumCorners[1] * (1 - ty) + frustrumCorners[3] * ty;
		accum += traceRay(eyePos, normalize(lVec * (1 - tx) + rVec * tx), &rndSeed);
	}

	// Average samples
	accum *= accumScaler;

	// Apply tone-mapping and gamma correction using:
	// 1 - exp(-hdrColor * exposure)) [tone mapping HDR -> LDR]
	// pow(ldr, 0.45)) [gamma correction]
	frameBuffer[y*FRAME_W + x] = pow(-expm1(-accum*exposure), 0.45f);
}

// Global parameters that will be set via a template during compilation time.
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
#define FRAME_W 512
#define FRAME_H 512
#define TEXEL_STEP_X 1.0f / 512.0f
#define TEXEL_STEP_Y 1.0f / 512.0f
#define MAX_TRACE_STEPS 128
#define NO_SURFACE_HIT -1

// Epsilon values
#define NUDGE_EPSILON 1.0e-2f
#define DIST_SNAP_EPSILON 1.0e-3f

// Vector helpers
#define VEC3_MAX_COMPONENT(v) max(v.x, max(v.y, v.z))
#define VEC3(x,y,z) (float4)(x,y,z,0.0f)
#define VEC3_ORIGIN VEC3(0,0,0)

// Supported surface types
#define SURFACE_DIFFUSE 0
#define SURFACE_SPECULAR 1
#define SURFACE_REFRACTIVE 2
#define SURFACE_LIGHT 65535

// DEBUGGING
#define DEBUG (get_global_id(0)==256 && get_global_id(1)==256)

// Distance estimator function generators
// --------------------------------------
#define PLANE_SD_FN(objIndex, origin, planeDims) float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float4 dims = normalize(planeDims); \
	return dot(point, dims); \
}
#define SPHERE_SD_FN(objIndex, origin, radius) float distToObj##objIndex( float4 point ){ \
	return length(point - origin) - radius; \
}
#define BOX_SD_FN(objIndex, origin, boxDims) float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float4 dims = boxDims; \
	float4 d = fabs(point) - (float4)(dims.xyz, point.w); \
	return fmin(VEC3_MAX_COMPONENT(d), 0.0f) + length(fmax(d, 0.0f)); \
}
#define TORUS_SD_FN(objIndex, origin, torusDims) float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float2 dims = torusDims; \
	return length((float2)(length(point.xz) - dims.x, point.y)) - dims.y; \
}

// Normal estimator generators
#define OBJECT_NORMAL_AT(objIndex, point) \
	normalize( \
		(float4)( \
			distToObj##objIndex(point + VEC3(NUDGE_EPSILON, 0,0)) - distToObj##objIndex(point - VEC3(NUDGE_EPSILON, 0,0)), \
			distToObj##objIndex(point + VEC3(0, NUDGE_EPSILON,0)) - distToObj##objIndex(point - VEC3(0, NUDGE_EPSILON,0)), \
			distToObj##objIndex(point + VEC3(0, 0, NUDGE_EPSILON)) - distToObj##objIndex(point - VEC3(0, 0, NUDGE_EPSILON)), \
			0.0f \
		) \
	)

// Random number and direction generators
// -----------------------
float2 clRand(uint2 *state)
{
    const float2 invMaxInt = (float2) (1.0f/4294967296.0f, 1.0f/4294967296.0f);
    uint x = (*state).x * 17 + (*state).y * 13123;
    (*state).x = (x<<13) ^ x;
    (*state).y ^= (x<<7);

    uint2 tmp = (uint2)
    (
		(x * (x * x * 15731 + 74323) + 871483),
		(x * (x * x * 13734 + 37828) + 234234)
	);

    return convert_float2(tmp) * invMaxInt;
}

// Return random direction on hemiphere around normal.
// This function has: PDF = cos(theta) / pi
float4 rndCosWeightedHemisphereDir(float4 normal, uint2 *seed) {
	float2 rnd  = clRand(seed);

	// Generate point on disk
	float rd = sqrt(rnd.x);
	float phi = 2*M_PI*rnd.y;

	// Generate tangent, bi-tangent vectors
	float4 u = normalize(cross((fabs(normal.x) > .1 ? (float4)(0, 1, 0, 0) : (float4)(1, 0, 0 ,0)), normal));
	float4 v = cross(normal,u);

	// Project disk point to unit hemisphere and rotate so that the normal points up
	return normalize(u * rd * cos(phi) + v * rd * sin(phi) + normal * sqrt(1 - rnd.x));
}

// Tracer implementation
// ---------------------

typedef struct {
	float4 position;
	float4 normal;
	float distFromOrigin;
	int objIndex;
	int matIndex;
} Hit;

typedef struct {
	unsigned int type;
	float4 diffuse;
	float4 emissive;
} Material;

// Material definitions
// The following code will be autogenerated by the scene processor
// ------------------
__constant float4 sceneBgColor = (float4)(0.0f);

__constant Material SceneMaterial[] = {
	{SURFACE_LIGHT, VEC3(0.0f,0.0f,0.0f), VEC3(60.0f,60.0f,60.0f)},
	{SURFACE_DIFFUSE, VEC3(0.75f, 0.25f, 0.25f)},
	{SURFACE_DIFFUSE, VEC3(0.25f, 0.25f, 0.75f)},
	{SURFACE_DIFFUSE, VEC3(0.75f, 0.75f, 0.75f)},
	{SURFACE_SPECULAR, VEC3(0.999f, 0.999f, 0.999f)},
	{SURFACE_REFRACTIVE, VEC3(0.999f, 0.999f, 0.999f)}
};

// Object definitions
// The following code will be autogenerated by the scene processor
// ------------------

// Light
SPHERE_SD_FN(0, VEC3(0.0f, 1.5f, 0.0f), 0.1f)
// Left wall
BOX_SD_FN(1, VEC3(-2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f))
// Right wall
BOX_SD_FN(2, VEC3(2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f))
// Top wall
BOX_SD_FN(3, VEC3(0.0f,2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f))
// Bottom wall
BOX_SD_FN(4, VEC3(0.0f,-2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f))
// Back wall
BOX_SD_FN(5, VEC3(0,0,-2), VEC3(2.0f, 2.0f, 0.1f))
// Mirror ball
SPHERE_SD_FN(6, VEC3(-1.0f, -1.4f, -1.0f), 0.5f)
// Glass ball
SPHERE_SD_FN(7, VEC3(1.4f, -1.4f, -0.5f), 0.5f)

// Estimate normal at intersection point
void getNormal(Hit *hit){
	// The following code will be autogenerated by the scene processor
	if( hit->objIndex == 0 ){
		hit->normal = OBJECT_NORMAL_AT(0, hit->position);
	} else if( hit->objIndex == 1 ){
		hit->normal = OBJECT_NORMAL_AT(1, hit->position);
	} else if( hit->objIndex == 2 ){
		hit->normal = OBJECT_NORMAL_AT(2, hit->position);
	} else if( hit->objIndex == 3 ){
		hit->normal = OBJECT_NORMAL_AT(3, hit->position);
	} else if( hit->objIndex == 4 ){
		hit->normal = OBJECT_NORMAL_AT(4, hit->position);
	} else if( hit->objIndex == 5 ){
		hit->normal = OBJECT_NORMAL_AT(5, hit->position);
	} else if( hit->objIndex == 6 ){
		hit->normal = OBJECT_NORMAL_AT(6, hit->position);
	} else if( hit->objIndex == 7 ){
		hit->normal = OBJECT_NORMAL_AT(7, hit->position);
	}
}

// Estimate distance to nearest object
void intersectWorld(const float4 rayOrigin, const float4 rayDir, const float minDist, const float maxDist, Hit *hit){
	hit->objIndex = NO_SURFACE_HIT;
	hit->matIndex = NO_SURFACE_HIT;

	float4 point;
	float curDist = minDist;
	float objDist, nearestDist;
	int nearestObj = -1;
	int nearestMat = -1;
	for(unsigned int step=0;step<MAX_TRACE_STEPS;step++){
		point = rayOrigin + rayDir * curDist;

		// Find nearest object/dist to point
		// The following code will be autogenerated by the scene processor
		nearestDist = FLT_MAX;
		objDist = distToObj0(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 0;
			nearestMat = 0;
		}
		objDist = distToObj1(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 1;
			nearestMat = 1;
		}
		objDist = distToObj2(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 2;
			nearestMat = 2;
		}
		objDist = distToObj3(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 3;
			nearestMat = 3;
		}
		objDist = distToObj4(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 4;
			nearestMat = 3;
		}
		objDist = distToObj5(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 5;
			nearestMat = 3;
		}
		objDist = distToObj6(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 6;
			nearestMat = 4;
		}
		objDist = distToObj7(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 7;
			nearestMat = 5;
		}

		// If we are inside an object nearestDist will be < 0
		nearestDist = fabs(nearestDist);

		curDist += nearestDist;
		if(curDist > maxDist){
			return;
		}

		// We are close enough to the object to register a hit
		if(nearestDist < DIST_SNAP_EPSILON){
			hit->position = point;
			hit->distFromOrigin = curDist;
			hit->objIndex = nearestObj;
			hit->matIndex = nearestMat;
			return;
		}
	}
}

// Trace a ray and return the gathered color.
float4 traceRay(float4 rayOrigin, float4 rayDir, uint2 *rndSeed){
	Hit hit;
	__constant Material *material;
	float4 rCol = (float4)(0.0f);
	float4 mask = (float4)(1.0f);
	unsigned int bounce = 0;
	while( true ){
		intersectWorld(rayOrigin, rayDir, NUDGE_EPSILON, FLT_MAX, &hit);

		// No hit
		if(hit.objIndex == NO_SURFACE_HIT){
			if(bounce == 0){
				rCol = sceneBgColor;
			}
			break;
		}

		// Get material at hit point
		material = &SceneMaterial[hit.matIndex];

		// If we hit a light just add its emissive property and stop
		if( material->type == SURFACE_LIGHT ){
			rCol += mask * material->emissive;
			break;
		}

		// Get normal at hit point
		getNormal(&hit);

		// After some bounces apply a russian roulette to terminate long paths
		if( bounce > 5 ) {
			float maxComponent = VEC3_MAX_COMPONENT(material->diffuse);
			if( maxComponent == 0 || clRand(rndSeed).x > maxComponent ){
				break;
			}

			// Boost surviving paths by 1/rr_prob
			mask /= maxComponent;
		}

		// Mask outgoing reflectance by material diffuse property
		mask *= material->diffuse;

		// Next bounce starts at hit point
		rayOrigin = hit.position;

		if( material->type == SURFACE_DIFFUSE ) {
			// We do importance sampling for diffuse rays
			rayDir = rndCosWeightedHemisphereDir(hit.normal, rndSeed);
		} else{
			// Reflect ray around normal
			rayDir = normalize(-2.0f * dot(hit.normal, rayDir) * hit.normal + rayDir);
		}

		++bounce;
	}

	return rCol;
}

// Emit the color of a pixel by tracing a ray through the scene.
__kernel void tracePixel(
		__global float4 *frameBuffer,
		__global float4 *frustrumCorners,
		const float4 eyePos,
		const unsigned int blockY,
		const unsigned int samplesPerPixel,
		const float exposure
		){
	// Get pixel coordinates
	unsigned int x = get_global_id(0);
	unsigned int y = get_global_id(1);
	if ( x > FRAME_W || y > FRAME_H ) {
		return;
	}

	// Setup seed for random numbers
	uint2 rndSeed = (uint2)(x, y);

	// Calculate texel coordinates [0,1] range
	float accumScaler = 1.0f / (float)samplesPerPixel;
	float4 accum = (float4)(0,0,0,0);

	for( uint sample=0;sample<samplesPerPixel;sample++){
		// Apply stratified sampling using a tent filter. This will wrap our
		// random numbers in the [-1, 1] range. X and Y point to the top corner
		// of the current texel so we need to add a bit of offset to get the coords
		// into the [-0.5, 1.5] range.
		float2 random = clRand(&rndSeed);
		float offX = random.x < 0.5f ? sqrt(2.0f * random.x) - 0.5f : 1.5f - sqrt(2.0f - 2.0f * random.x);
		float offY = random.y < 0.5f ? sqrt(2.0f * random.y) - 0.5f : 1.5f - sqrt(2.0f - 2.0f * random.y);
		float tx = ((float)x + offX) * TEXEL_STEP_X;
		float ty = ((float)y + offY) * TEXEL_STEP_Y;

		// Get ray direction using trilinear interpolation; trace ray and
		// add result to accumulation buffer
		float4 lVec = frustrumCorners[0] * (1 - ty) + frustrumCorners[2] * ty;
		float4 rVec = frustrumCorners[1] * (1 - ty) + frustrumCorners[3] * ty;
		accum += traceRay(eyePos, normalize(lVec * (1 - tx) + rVec * tx), &rndSeed);
	}

	// Average samples
	accum *= accumScaler;

	// Apply tone-mapping and gamma correction using:
	// 1 - exp(-hdrColor * exposure)) [tone mapping HDR -> LDR]
	// pow(ldr, 0.45)) [gamma correction]
	frameBuffer[y*FRAME_W + x] = pow(-expm1(-accum*exposure), 0.45f);
}