#pragma once

#include "Variation.h"

namespace EmberNs
{
/// <summary>
/// bubble2.
/// </summary>
template <typename T>
class Bubble2Variation : public ParametricVariation<T>
{
public:
	Bubble2Variation(T weight = 1.0) : ParametricVariation<T>("bubble2", eVariationId::VAR_BUBBLE2, weight, true)
	{
		Init();
	}

	PARVARCOPY(Bubble2Variation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T t = Zeps(T(0.25) * (helper.m_PrecalcSumSquares + SQR(helper.In.z)) + 1);
		T r = m_Weight / t;
		helper.Out.x = helper.In.x * r * m_X;
		helper.Out.y = helper.In.y * r * m_Y;

		if (helper.In.z >= 0)
			helper.Out.z = m_Weight * (helper.In.z + m_Z);
		else
			helper.Out.z = m_Weight * (helper.In.z - m_Z);

		helper.Out.z += helper.In.z * r * m_Z;//The += is intentional.
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string x = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string y = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string z = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t t = Zeps(fma((real_t)(0.25), fma(vIn.z, vIn.z, precalcSumSquares), (real_t)(1.0)));\n"
		   << "\t\treal_t r = " << weight << " / t;\n"
		   << "\n"
		   << "\t\tvOut.x = vIn.x * r * " << x << ";\n"
		   << "\t\tvOut.y = vIn.y * r * " << y << ";\n"
		   << "\n"
		   << "\t\tif (vIn.z >= 0)\n"
		   << "\t\t	vOut.z = " << weight << " * (vIn.z + " << z << ");\n"
		   << "\t\telse\n"
		   << "\t\t	vOut.z = " << weight << " * (vIn.z - " << z << ");\n"
		   << "\n"
		   << "\t\tvOut.z += vIn.z * r * " << z << ";\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Zeps" };
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_X, prefix + "bubble2_x", 1));//Original used a prefix of bubble_, which is incompatible with Ember's design.
		m_Params.push_back(ParamWithName<T>(&m_Y, prefix + "bubble2_y", 1));
		m_Params.push_back(ParamWithName<T>(&m_Z, prefix + "bubble2_z"));
	}

private:
	T m_X;
	T m_Y;
	T m_Z;
};

/// <summary>
/// CircleLinear.
/// </summary>
template <typename T>
class CircleLinearVariation : public ParametricVariation<T>
{
public:
	CircleLinearVariation(T weight = 1.0) : ParametricVariation<T>("CircleLinear", eVariationId::VAR_CIRCLELINEAR, weight)
	{
		Init();
	}

	PARVARCOPY(CircleLinearVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		int m = int(Floor<T>(T(0.5) * helper.In.x / m_Sc));
		int n = int(Floor<T>(T(0.5) * helper.In.y / m_Sc));
		int m21 = m * 2 + 1;
		int n21 = n * 2 + 1;
		T x = helper.In.x - m21 * m_Sc;
		T y = helper.In.y - n21 * m_Sc;
		T u = Zeps(VarFuncs<T>::Hypot(x, y));
		T v = (T(0.3) + T(0.7) * DiscreteNoise2(m + 10, n + 3)) * m_Sc;
		T z1 = DiscreteNoise2(int(m + m_Seed), n);

		if ((z1 < m_Dens1) && (u < v))
		{
			if (m_Reverse > 0)
			{
				if (z1 < m_Dens1 * m_Dens2)
				{
					x *= m_K;
					y *= m_K;
				}
				else
				{
					T z = v / u * (1 - m_K) + m_K;
					x *= z;
					y *= z;
				}
			}
			else
			{
				if (z1 > m_Dens1 * m_Dens2)
				{
					x *= m_K;
					y *= m_K;
				}
				else
				{
					T z = v / u * (1 - m_K) + m_K;
					x *= z;
					y *= z;
				}
			}
		}

		helper.Out.x = m_Weight * (x + m21 * m_Sc);
		helper.Out.y = m_Weight * (y + n21 * m_Sc);
		helper.Out.z = DefaultZ(helper);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string sc      = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string k       = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string dens1   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string dens2   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string reverse = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string x       = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string y       = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string seed    = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tint m = (int)floor((real_t)(0.5) * vIn.x / " << sc << ");\n"
		   << "\t\tint n = (int)floor((real_t)(0.5) * vIn.y / " << sc << ");\n"
		   << "\t\tint m21 = m * 2 + 1;\n"
		   << "\t\tint n21 = n * 2 + 1;\n"
		   << "\t\treal_t x = vIn.x - m21 * " << sc << ";\n"
		   << "\t\treal_t y = vIn.y - n21 * " << sc << ";\n"
		   << "\t\treal_t u = Zeps(Hypot(x, y));\n"
		   << "\t\treal_t v = fma(CircleLinearDiscreteNoise2(m + 10, n + 3), (real_t)(0.7), (real_t)(0.3)) * " << sc << ";\n"
		   << "\t\treal_t z1 = CircleLinearDiscreteNoise2((int)(m + " << seed << "), n);\n"
		   << "\n"
		   << "\t\tif ((z1 < " << dens1 << ") && (u < v))\n"
		   << "\t\t{\n"
		   << "\t\t	if (" << reverse << " > 0)\n"
		   << "\t\t	{\n"
		   << "\t\t		if (z1 < " << dens1 << " * " << dens2 << ")\n"
		   << "\t\t		{\n"
		   << "\t\t			x *= " << k << ";\n"
		   << "\t\t			y *= " << k << ";\n"
		   << "\t\t		}\n"
		   << "\t\t		else\n"
		   << "\t\t		{\n"
		   << "\t\t			real_t z = fma(v / u, (1 - " << k << "), " << k << ");\n"
		   << "\n"
		   << "\t\t			x *= z;\n"
		   << "\t\t			y *= z;\n"
		   << "\t\t		}\n"
		   << "\t\t	}\n"
		   << "\t\t	else\n"
		   << "\t\t	{\n"
		   << "\t\t		if (z1 > " << dens1 << " * " << dens2 << ")\n"
		   << "\t\t		{\n"
		   << "\t\t			x *= " << k << ";\n"
		   << "\t\t			y *= " << k << ";\n"
		   << "\t\t		}\n"
		   << "\t\t		else\n"
		   << "\t\t		{\n"
		   << "\t\t			real_t z = fma(v / u, (1 - " << k << "), " << k << ");\n"
		   << "\n"
		   << "\t\t			x *= z;\n"
		   << "\t\t			y *= z;\n"
		   << "\t\t		}\n"
		   << "\t\t	}\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\tvOut.x = " << weight << " * fma((real_t)m21, " << sc << ", x);\n"
		   << "\t\tvOut.y = " << weight << " * fma((real_t)n21, " << sc << ", y);\n"
		   << "\t\tvOut.z = " << DefaultZCl()
		   << "\t}\n";
		return ss.str();
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Hypot", "Zeps" };
	}

	virtual string OpenCLFuncsString() const override
	{
		return
			"real_t CircleLinearDiscreteNoise2(int x, int y)\n"
			"{\n"
			"	const real_t im = 2147483647;\n"
			"	const real_t am = 1 / im;\n"
			"\n"
			"	int n = x + y * 57;\n"
			"	n = (n << 13) ^ n;\n"
			"	return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) * am;\n"
			"}\n"
			"\n";
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Sc, prefix + "CircleLinear_Sc", 1, eParamType::REAL_NONZERO));
		m_Params.push_back(ParamWithName<T>(&m_K, prefix + "CircleLinear_K", T(0.5)));
		m_Params.push_back(ParamWithName<T>(&m_Dens1, prefix + "CircleLinear_Dens1", T(0.5)));
		m_Params.push_back(ParamWithName<T>(&m_Dens2, prefix + "CircleLinear_Dens2", T(0.5)));
		m_Params.push_back(ParamWithName<T>(&m_Reverse, prefix + "CircleLinear_Reverse", 1));
		m_Params.push_back(ParamWithName<T>(&m_X, prefix + "CircleLinear_X", 10));
		m_Params.push_back(ParamWithName<T>(&m_Y, prefix + "CircleLinear_Y", 10));
		m_Params.push_back(ParamWithName<T>(&m_Seed, prefix + "CircleLinear_Seed", 0, eParamType::INTEGER));
	}

private:
	T DiscreteNoise2(int x, int y)
	{
		const T im = T(2147483647);
		const T am = (1 / im);
		int n = x + y * 57;
		n = (n << 13) ^ n;
		return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) * am;
	}

	T m_Sc;
	T m_K;
	T m_Dens1;
	T m_Dens2;
	T m_Reverse;
	T m_X;
	T m_Y;
	T m_Seed;
};

/// <summary>
/// CircleRand.
/// The original would loop infinitely as x and y approached zero, so put a check for a max of 10 iters.
/// </summary>
template <typename T>
class CircleRandVariation : public ParametricVariation<T>
{
public:
	CircleRandVariation(T weight = 1.0) : ParametricVariation<T>("CircleRand", eVariationId::VAR_CIRCLERAND, weight)
	{
		Init();
	}

	PARVARCOPY(CircleRandVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		intmax_t m, n, iters = 0;
		T x, y, u;

		do
		{
			x = m_X * (1 - 2 * rand.Frand01<T>());
			y = m_Y * (1 - 2 * rand.Frand01<T>());
			m = Floor<T>(T(0.5) * x / m_Sc);
			n = Floor<T>(T(0.5) * y / m_Sc);
			x -= (m * 2 + 1) * m_Sc;
			y -= (n * 2 + 1) * m_Sc;
			u = VarFuncs<T>::Hypot(x, y);

			if (++iters > 10)
				break;
		}
		while ((DiscreteNoise2(int(m + m_Seed), int(n)) > m_Dens) || (u > (T(0.3) + T(0.7) * DiscreteNoise2(int(m + 10), int(n + 3))) * m_Sc));

		helper.Out.x = m_Weight * (x + (m * 2 + 1) * m_Sc);
		helper.Out.y = m_Weight * (y + (n * 2 + 1) * m_Sc);
		helper.Out.z = DefaultZ(helper);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index  = ss2.str();
		string weight = WeightDefineString();
		string sc     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string dens   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string x      = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string y      = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string seed   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tint m, n, iters = 0;\n"
		   << "\t\treal_t x, y, u;\n"
		   << "\n"
		   << "\t\tdo\n"
		   << "\t\t{\n"
		   << "\t\t	x = " << x << " * (1 - 2 * MwcNext01(mwc));\n"
		   << "\t\t	y = " << y << " * (1 - 2 * MwcNext01(mwc));\n"
		   << "\t\t	m = (int)floor((real_t)(0.5) * x / " << sc << ");\n"
		   << "\t\t	n = (int)floor((real_t)(0.5) * y / " << sc << ");\n"
		   << "\t\t	x = x - (m * 2 + 1) * " << sc << ";\n"
		   << "\t\t	y = y - (n * 2 + 1) * " << sc << ";\n"
		   << "\t\t	u = Hypot(x, y);\n"
		   << "\n"
		   << "\t\t	if (++iters > 10)\n"
		   << "\t\t		break;\n"
		   << "\t\t}\n"
		   << "\t\twhile ((CircleRandDiscreteNoise2((int)(m + " << seed << "), n) > " << dens << ") || (u > fma(CircleRandDiscreteNoise2(m + 10, n + 3), (real_t)(0.7), (real_t)(0.3)) * " << sc << "));\n"
		   << "\n"
		   << "\t\tvOut.x = " << weight << " * fma((real_t)(m * 2 + 1), " << sc << ", x);\n"
		   << "\t\tvOut.y = " << weight << " * fma((real_t)(n * 2 + 1), " << sc << ", y);\n"
		   << "\t\tvOut.z = " << DefaultZCl()
		   << "\t}\n";
		return ss.str();
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Hypot" };
	}

	virtual string OpenCLFuncsString() const override
	{
		return
			"real_t CircleRandDiscreteNoise2(int x, int y)\n"
			"{\n"
			"	const real_t im = 2147483647;\n"
			"	const real_t am = 1 / im;\n"
			"\n"
			"	int n = x + y * 57;\n"
			"	n = (n << 13) ^ n;\n"
			"	return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) * am;\n"
			"}\n"
			"\n";
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Sc, prefix   + "CircleRand_Sc", 1, eParamType::REAL_NONZERO));
		m_Params.push_back(ParamWithName<T>(&m_Dens, prefix + "CircleRand_Dens", T(0.5)));
		m_Params.push_back(ParamWithName<T>(&m_X, prefix    + "CircleRand_X", 10));
		m_Params.push_back(ParamWithName<T>(&m_Y, prefix    + "CircleRand_Y", 10));
		m_Params.push_back(ParamWithName<T>(&m_Seed, prefix + "CircleRand_Seed", 0, eParamType::INTEGER));
	}

private:
	T DiscreteNoise2(int x, int y)
	{
		const T im = T(2147483647);
		const T am = (1 / im);
		int n = x + y * 57;
		n = (n << 13) ^ n;
		return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) * am;
	}

	T m_Sc;
	T m_Dens;
	T m_X;
	T m_Y;
	T m_Seed;
};

/// <summary>
/// CircleTrans1.
/// The original would loop infinitely as x and y approached zero, so put a check for a max of 10 iters.
/// </summary>
template <typename T>
class CircleTrans1Variation : public ParametricVariation<T>
{
public:
	CircleTrans1Variation(T weight = 1.0) : ParametricVariation<T>("CircleTrans1", eVariationId::VAR_CIRCLETRANS1, weight)
	{
		Init();
	}

	PARVARCOPY(CircleTrans1Variation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T ux, uy, u, x, y;
		Trans(m_X, m_Y, helper.In.x, helper.In.y, &ux, &uy);
		intmax_t m = Floor<T>(T(0.5) * ux / m_Sc);
		intmax_t n = Floor<T>(T(0.5) * uy / m_Sc);
		x = ux - (m * 2 + 1) * m_Sc;
		y = uy - (n * 2 + 1) * m_Sc;
		u = VarFuncs<T>::Hypot(x, y);

		if ((DiscreteNoise2(int(m + m_Seed), int(n)) > m_Dens) || (u > (T(0.3) + T(0.7) * DiscreteNoise2(int(m + 10), int(n + 3))) * m_Sc))
		{
			ux = ux;
			uy = uy;
		}
		else
		{
			CircleR(&ux, &uy, rand);
		}

		helper.Out.x = m_Weight * ux;
		helper.Out.y = m_Weight * uy;
		helper.Out.z = DefaultZ(helper);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string sc = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string dens = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string x = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string y = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string seed = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t ux, uy, u, x, y;\n"
		   << "\n"
		   << "\t\tCircleTrans1Trans(" << x << ", " << y << ", vIn.x, vIn.y, &ux, &uy);\n"
		   << "\n"
		   << "\t\tint m = (int)floor((real_t)(0.5) * ux / " << sc << ");\n"
		   << "\t\tint n = (int)floor((real_t)(0.5) * uy / " << sc << ");\n"
		   << "\n"
		   << "\t\tx = ux - (m * 2 + 1) * " << sc << ";\n"
		   << "\t\ty = uy - (n * 2 + 1) * " << sc << ";\n"
		   << "\t\tu = Hypot(x, y);\n"
		   << "\n"
		   << "\t\tif ((CircleTrans1DiscreteNoise2((int)(m + " << seed << "), n) > " << dens << ") || (u > fma(CircleTrans1DiscreteNoise2(m + 10, n + 3), (real_t)(0.7), (real_t)(0.3)) * " << sc << "))\n"
		   << "\t\t{\n"
		   << "\t\t	ux = ux;\n"
		   << "\t\t	uy = uy;\n"
		   << "\t\t}\n"
		   << "\t\telse\n"
		   << "\t\t{\n"
		   << "\t\t	CircleTrans1CircleR(" << x << ", " << y << ", " << sc << ", " << seed << ", " << dens << ", &ux, &uy, mwc);\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\tvOut.x = " << weight << " * ux;\n"
		   << "\t\tvOut.y = " << weight << " * uy;\n"
		   << "\t\tvOut.z = " << DefaultZCl()
		   << "\t}\n";
		return ss.str();
	}

	virtual string OpenCLFuncsString() const override
	{
		return
			"real_t CircleTrans1DiscreteNoise2(int x, int y)\n"
			"{\n"
			"	const real_t im = 2147483647;\n"
			"	const real_t am = 1 / im;\n"
			"\n"
			"	int n = x + y * 57;\n"
			"	n = (n << 13) ^ n;\n"
			"	return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) * am;\n"
			"}\n"
			"\n"
			"void CircleTrans1Trans(real_t a, real_t b, real_t x, real_t y, real_t* x1, real_t* y1)\n"
			"{\n"
			"	*x1 = fma((x - a), (real_t)(0.5), a);\n"
			"	*y1 = fma((y - b), (real_t)(0.5), b);\n"
			"}\n"
			"\n"
			"void CircleTrans1CircleR(real_t mx, real_t my, real_t sc, real_t seed, real_t dens, real_t* ux, real_t* vy, uint2* mwc)\n"
			"{\n"
			"	int m, n, iters = 0;\n"
			"	real_t x, y, alpha, u;\n"
			"\n"
			"	do\n"
			"	{\n"
			"		x = fabs(mx) * (1 - 2 * MwcNext01(mwc));\n"
			"		y = fabs(my) * (1 - 2 * MwcNext01(mwc));\n"
			"		m = (int)floor((real_t)(0.5) * x / sc);\n"
			"		n = (int)floor((real_t)(0.5) * y / sc);\n"
			"		alpha = M_2PI * MwcNext01(mwc);\n"
			"		u = fma(CircleTrans1DiscreteNoise2(m + 10, n + 3), (real_t)(0.7), (real_t)(0.3));\n"
			"		x = u * cos(alpha);\n"
			"		y = u * sin(alpha);\n"
			"\n"
			"		if (++iters > 10)\n"
			"			break;\n"
			"	}\n"
			"	while (CircleTrans1DiscreteNoise2((int)(m + seed), n) > dens);\n"
			"\n"
			"	*ux = fma((real_t)(m * 2 + 1), sc, x);\n"
			"	*vy = fma((real_t)(n * 2 + 1), sc, y);\n"
			"}\n"
			"\n";
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Hypot" };
	}

	virtual void Precalc() override
	{
		m_Sc = Zeps(m_Sc);
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Sc, prefix + "CircleTrans1_Sc", 1, eParamType::REAL_NONZERO));
		m_Params.push_back(ParamWithName<T>(&m_Dens, prefix + "CircleTrans1_Dens", T(0.5)));
		m_Params.push_back(ParamWithName<T>(&m_X, prefix + "CircleTrans1_X", 10));
		m_Params.push_back(ParamWithName<T>(&m_Y, prefix + "CircleTrans1_Y", 10));
		m_Params.push_back(ParamWithName<T>(&m_Seed, prefix + "CircleTrans1_Seed", 0, eParamType::INTEGER));
	}

private:
	T DiscreteNoise2(int x, int y)
	{
		const T im = T(2147483647);
		const T am = (1 / im);
		int n = x + y * 57;
		n = (n << 13) ^ n;
		return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) * am;
	}

	void Trans(T a, T b, T x, T y, T* x1, T* y1)
	{
		*x1 = (x - a) * T(0.5) + a;
		*y1 = (y - b) * T(0.5) + b;
	}

	void CircleR(T* ux, T* vy, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
	{
		intmax_t m, n, iters = 0;
		T x, y, alpha, u;

		do
		{
			x = std::abs(m_X) * (1 - 2 * rand.Frand01<T>());
			y = std::abs(m_Y) * (1 - 2 * rand.Frand01<T>());
			m = Floor<T>(T(0.5) * x / m_Sc);
			n = Floor<T>(T(0.5) * y / m_Sc);
			alpha = M_2PI * rand.Frand01<T>();
			u = T(0.3) + T(0.7) * DiscreteNoise2(int(m + 10), int(n + 3));
			x = u * std::cos(alpha);
			y = u * std::sin(alpha);

			if (++iters > 10)
				break;
		}
		while (DiscreteNoise2(int(m + m_Seed), int(n)) > m_Dens);

		*ux = x + (m * 2 + 1) * m_Sc;
		*vy = y + (n * 2 + 1) * m_Sc;
	}

	T m_Sc;
	T m_Dens;
	T m_X;
	T m_Y;
	T m_Seed;
};

/// <summary>
/// cubic3D.
/// </summary>
template <typename T>
class Cubic3DVariation : public ParametricVariation<T>
{
public:
	Cubic3DVariation(T weight = 1.0) : ParametricVariation<T>("cubic3D", eVariationId::VAR_CUBIC3D, weight)
	{
		Init();
	}

	PARVARCOPY(Cubic3DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		int useNode = rand.Rand() & 7;//Faster than % 8.
		T exnze, wynze, znxy;
		T px, py, pz;
		exnze = 1 - (m_SmoothStyle * (1 - (std::cos(std::atan2(helper.In.x, helper.In.z)))));
		wynze = 1 - (m_SmoothStyle * (1 - (std::sin(std::atan2(helper.In.y, helper.In.z)))));

		if (m_SmoothStyle > 1)
			znxy = 1 - (m_SmoothStyle * (1 - ((exnze + wynze) / 2 * m_SmoothStyle)));
		else
			znxy = 1 - (m_SmoothStyle * (1 - ((exnze + wynze) * T(0.5))));

		if (m_VarType == eVariationType::VARTYPE_PRE)
		{
			px = helper.In.x;
			py = helper.In.y;
			pz = helper.In.z;
		}
		else
		{
			px = outPoint.m_X;
			py = outPoint.m_Y;
			pz = outPoint.m_Z;
		}

		T val1 = (px - (m_Smooth1mFill * px * exnze)) + (helper.In.x * m_SmoothFill * exnze);
		T val2 = (py - (m_Smooth1mFill * py * wynze)) + (helper.In.y * m_SmoothFill * wynze);
		T val3 = (pz - (m_Smooth1mFill * pz * znxy)) + (helper.In.z * m_SmoothFill * znxy);

		switch (useNode)
		{
			case 0:
				helper.Out.x = val1 + m_HalfWeight;
				helper.Out.y = val2 + m_HalfWeight;
				helper.Out.z = val3 + m_HalfWeight;
				break;

			case 1:
				helper.Out.x = val1 + m_HalfWeight;
				helper.Out.y = val2 - m_HalfWeight;
				helper.Out.z = val3 + m_HalfWeight;
				break;

			case 2:
				helper.Out.x = val1 + m_HalfWeight;
				helper.Out.y = val2 + m_HalfWeight;
				helper.Out.z = val3 - m_HalfWeight;
				break;

			case 3:
				helper.Out.x = val1 + m_HalfWeight;
				helper.Out.y = val2 - m_HalfWeight;
				helper.Out.z = val3 - m_HalfWeight;
				break;

			case 4:
				helper.Out.x = val1 - m_HalfWeight;
				helper.Out.y = val2 + m_HalfWeight;
				helper.Out.z = val3 + m_HalfWeight;
				break;

			case 5:
				helper.Out.x = val1 - m_HalfWeight;
				helper.Out.y = val2 - m_HalfWeight;
				helper.Out.z = val3 + m_HalfWeight;
				break;

			case 6:
				helper.Out.x = val1 - m_HalfWeight;
				helper.Out.y = val2 + m_HalfWeight;
				helper.Out.z = val3 - m_HalfWeight;
				break;

			case 7:
			default:
				helper.Out.x = val1 - m_HalfWeight;
				helper.Out.y = val2 - m_HalfWeight;
				helper.Out.z = val3 - m_HalfWeight;
				break;
		}
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index        = ss2.str();
		string weight       = WeightDefineString();
		string xpand        = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string style        = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string fill         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string smooth       = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string smoothStyle  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string smoothfill   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string smooth1mfill = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string halfweight   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tint useNode = MwcNext(mwc) & 7;\n"
		   << "\t\treal_t exnze, wynze, znxy;\n"
		   << "\t\treal_t px, py, pz;\n"
		   << "\n"
		   << "\t\texnze = 1 - (" << smoothStyle << " * (1 - (cos(atan2(vIn.x, vIn.z)))));\n"
		   << "\t\twynze = 1 - (" << smoothStyle << " * (1 - (sin(atan2(vIn.y, vIn.z)))));\n"
		   << "\n"
		   << "\t\tif (" << smoothStyle << " > 1)\n"
		   << "\t\t	znxy = 1 - (" << smoothStyle << " * (1 - ((exnze + wynze) / 2 * " << smoothStyle << ")));\n"
		   << "\t\telse\n"
		   << "\t\t	znxy = 1 - (" << smoothStyle << " * (1 - ((exnze + wynze) * (real_t)(0.5))));\n\n";

		if (m_VarType == eVariationType::VARTYPE_PRE)
		{
			ss <<
			   "\t\tpx = vIn.x;\n"
			   "\t\tpy = vIn.y;\n"
			   "\t\tpz = vIn.z;\n\n";
		}
		else
		{
			ss <<
			   "\t\tpx = outPoint->m_X;\n"
			   "\t\tpy = outPoint->m_Y;\n"
			   "\t\tpz = outPoint->m_Z;\n\n";
		}

		ss <<
		   "\t\treal_t val1 = (px - (" << smooth1mfill << " * px * exnze)) + (vIn.x * " << smoothfill << " * exnze);\n"
		   "\t\treal_t val2 = (py - (" << smooth1mfill << " * py * wynze)) + (vIn.y * " << smoothfill << " * wynze);\n"
		   "\t\treal_t val3 = (pz - (" << smooth1mfill << " * pz * znxy))  + (vIn.z * " << smoothfill << " * znxy);\n"
		   "\n"
		   "\t\tswitch (useNode)\n"
		   "\t\t{\n"
		   "\t\t	case 0 :\n"
		   "\t\t		vOut.x = val1 + " << halfweight << ";\n"
		   "\t\t		vOut.y = val2 + " << halfweight << ";\n"
		   "\t\t		vOut.z = val3 + " << halfweight << ";\n"
		   "\t\t		break;\n"
		   "\t\t	case 1 :\n"
		   "\t\t		vOut.x = val1 + " << halfweight << ";\n"
		   "\t\t		vOut.y = val2 - " << halfweight << ";\n"
		   "\t\t		vOut.z = val3 + " << halfweight << ";\n"
		   "\t\t		break;\n"
		   "\t\t	case 2 :\n"
		   "\t\t		vOut.x = val1 + " << halfweight << ";\n"
		   "\t\t		vOut.y = val2 + " << halfweight << ";\n"
		   "\t\t		vOut.z = val3 - " << halfweight << ";\n"
		   "\t\t		break;\n"
		   "\t\t	case 3 :\n"
		   "\t\t		vOut.x = val1 + " << halfweight << ";\n"
		   "\t\t		vOut.y = val2 - " << halfweight << ";\n"
		   "\t\t		vOut.z = val3 - " << halfweight << ";\n"
		   "\t\t		break;\n"
		   "\t\t	case 4 :\n"
		   "\t\t		vOut.x = val1 - " << halfweight << ";\n"
		   "\t\t		vOut.y = val2 + " << halfweight << ";\n"
		   "\t\t		vOut.z = val3 + " << halfweight << ";\n"
		   "\t\t		break;\n"
		   "\t\t	case 5 :\n"
		   "\t\t		vOut.x = val1 - " << halfweight << ";\n"
		   "\t\t		vOut.y = val2 - " << halfweight << ";\n"
		   "\t\t		vOut.z = val3 + " << halfweight << ";\n"
		   "\t\t		break;\n"
		   "\t\t	case 6 :\n"
		   "\t\t		vOut.x = val1 - " << halfweight << ";\n"
		   "\t\t		vOut.y = val2 + " << halfweight << ";\n"
		   "\t\t		vOut.z = val3 - " << halfweight << ";\n"
		   "\t\t		break;\n"
		   "\t\t	case 7 :\n"
		   "\t\t		vOut.x = val1 - " << halfweight << ";\n"
		   "\t\t		vOut.y = val2 - " << halfweight << ";\n"
		   "\t\t		vOut.z = val3 - " << halfweight << ";\n"
		   "\t\t		break;\n"
		   "\t\t}\n"
		   "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		if (std::abs(m_Xpand) <= 1)
			m_Fill = m_Xpand * T(0.5);
		else
			m_Fill = std::sqrt(m_Xpand) * T(0.5);

		if (std::abs(m_Weight) <= T(0.5))
			m_Smooth = m_Weight * 2;//Causes full effect above m_Weight = 0.5.
		else
			m_Smooth = 1;

		if (std::abs(m_Style) <= 1)
		{
			m_SmoothStyle = m_Style;
		}
		else
		{
			if (m_Style > 1)
				m_SmoothStyle = 1 + (m_Style - 1) * T(0.25);
			else
				m_SmoothStyle = (m_Style + 1) * T(0.25) - 1;
		}

		m_SmoothFill = m_Smooth * m_Fill;
		m_Smooth1mFill = m_Smooth * (1 - m_Fill);
		m_HalfWeight = m_Weight * T(0.5);
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Xpand, prefix              + "cubic3D_xpand", T(0.25)));
		m_Params.push_back(ParamWithName<T>(&m_Style, prefix              + "cubic3D_style"));
		m_Params.push_back(ParamWithName<T>(true, &m_Fill, prefix         + "cubic3D_fill"));    //Precalc.
		m_Params.push_back(ParamWithName<T>(true, &m_Smooth, prefix       + "cubic3D_smooth"));
		m_Params.push_back(ParamWithName<T>(true, &m_SmoothStyle, prefix  + "cubic3D_smooth_style"));
		m_Params.push_back(ParamWithName<T>(true, &m_SmoothFill, prefix   + "cubic3D_smooth_fill"));
		m_Params.push_back(ParamWithName<T>(true, &m_Smooth1mFill, prefix + "cubic3D_smooth_1m_fill"));
		m_Params.push_back(ParamWithName<T>(true, &m_HalfWeight, prefix   + "cubic3D_half_weight"));
	}

private:
	T m_Xpand;
	T m_Style;
	T m_Fill;//Precalc.
	T m_Smooth;
	T m_SmoothStyle;
	T m_SmoothFill;
	T m_Smooth1mFill;
	T m_HalfWeight;
};

/// <summary>
/// cubicLattice_3D.
/// </summary>
template <typename T>
class CubicLattice3DVariation : public ParametricVariation<T>
{
public:
	CubicLattice3DVariation(T weight = 1.0) : ParametricVariation<T>("cubicLattice_3D", eVariationId::VAR_CUBIC_LATTICE3D, weight)
	{
		Init();
	}

	PARVARCOPY(CubicLattice3DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		int useNode = rand.Rand() & 7;//Faster than % 8.
		T exnze, wynze, znxy, px, py, pz;

		if (m_Style == 2)
		{
			exnze = std::cos(std::atan2(helper.In.x, helper.In.z));
			wynze = std::sin(std::atan2(helper.In.y, helper.In.z));
			znxy = (exnze + wynze) * T(0.5);
		}
		else
		{
			exnze = 1;
			wynze = 1;
			znxy = 1;
		}

		if (m_VarType == eVariationType::VARTYPE_PRE)
		{
			px = helper.In.x;
			py = helper.In.y;
			pz = helper.In.z;
		}
		else
		{
			px = outPoint.m_X;
			py = outPoint.m_Y;
			pz = outPoint.m_Z;
		}

		T pxtx = (px + helper.In.x) * m_Fill * exnze;
		T pyty = (py + helper.In.y) * m_Fill * wynze;
		T pztz = (pz + helper.In.z) * m_Fill * znxy;

		switch (useNode)
		{
			case 0:
				helper.Out.x = pxtx + m_Weight;
				helper.Out.y = pyty + m_Weight;
				helper.Out.z = pztz + m_Weight;
				break;

			case 1:
				helper.Out.x = pxtx + m_Weight;
				helper.Out.y = pyty - m_Weight;
				helper.Out.z = pztz + m_Weight;
				break;

			case 2:
				helper.Out.x = pxtx + m_Weight;
				helper.Out.y = pyty + m_Weight;
				helper.Out.z = pztz - m_Weight;
				break;

			case 3:
				helper.Out.x = pxtx + m_Weight;
				helper.Out.y = pyty - m_Weight;
				helper.Out.z = pztz - m_Weight;
				break;

			case 4:
				helper.Out.x = pxtx - m_Weight;
				helper.Out.y = pyty + m_Weight;
				helper.Out.z = pztz + m_Weight;
				break;

			case 5:
				helper.Out.x = pxtx - m_Weight;
				helper.Out.y = pyty - m_Weight;
				helper.Out.z = pztz + m_Weight;
				break;

			case 6:
				helper.Out.x = pxtx - m_Weight;
				helper.Out.y = pyty + m_Weight;
				helper.Out.z = pztz - m_Weight;
				break;

			case 7:
			default:
				helper.Out.x = pxtx - m_Weight;
				helper.Out.y = pyty - m_Weight;
				helper.Out.z = pztz - m_Weight;
				break;
		}
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index  = ss2.str();
		string weight = WeightDefineString();
		string xpand  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string style  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string fill   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tint useNode = MwcNext(mwc) & 7;\n"
		   << "\t\treal_t exnze, wynze, znxy, px, py, pz;\n"
		   << "\n"
		   << "\t\tif (" << style << " == 2)\n"
		   << "\t\t{\n"
		   << "\t\t	exnze = cos(atan2(vIn.x, vIn.z));\n"
		   << "\t\t	wynze = sin(atan2(vIn.y, vIn.z));\n"
		   << "\t\t	znxy = (exnze + wynze) * (real_t)(0.5);\n"
		   << "\t\t}\n"
		   << "\t\telse\n"
		   << "\t\t{\n"
		   << "\t\t	exnze = 1;\n"
		   << "\t\t	wynze = 1;\n"
		   << "\t\t	znxy = 1;\n"
		   << "\t\t}\n";

		if (m_VarType == eVariationType::VARTYPE_PRE)
		{
			ss <<
			   "\t\tpx = vIn.x;\n"
			   "\t\tpy = vIn.y;\n"
			   "\t\tpz = vIn.z;\n";
		}
		else
		{
			ss <<
			   "\t\tpx = outPoint->m_X;\n"
			   "\t\tpy = outPoint->m_Y;\n"
			   "\t\tpz = outPoint->m_Z;\n";
		}

		ss << "\t\treal_t pxtx = (px + vIn.x) * " << fill << " * exnze;\n"
		   << "\t\treal_t pyty = (py + vIn.y) * " << fill << " * wynze;\n"
		   << "\t\treal_t pztz = (pz + vIn.z) * " << fill << " * znxy ;\n"
		   << "\n"
		   << "\t\tswitch (useNode)\n"
		   << "\t\t{\n"
		   << "\t\t	case 0 :\n"
		   << "\t\t		vOut.x = pxtx + " << weight << ";\n"
		   << "\t\t		vOut.y = pyty + " << weight << ";\n"
		   << "\t\t		vOut.z = pztz + " << weight << ";\n"
		   << "\t\t		break;\n"
		   << "\t\t	case 1 :\n"
		   << "\t\t		vOut.x = pxtx + " << weight << ";\n"
		   << "\t\t		vOut.y = pyty - " << weight << ";\n"
		   << "\t\t		vOut.z = pztz + " << weight << ";\n"
		   << "\t\t		break;\n"
		   << "\t\t	case 2 :\n"
		   << "\t\t		vOut.x = pxtx + " << weight << ";\n"
		   << "\t\t		vOut.y = pyty + " << weight << ";\n"
		   << "\t\t		vOut.z = pztz - " << weight << ";\n"
		   << "\t\t		break;\n"
		   << "\t\t	case 3 :\n"
		   << "\t\t		vOut.x = pxtx + " << weight << ";\n"
		   << "\t\t		vOut.y = pyty - " << weight << ";\n"
		   << "\t\t		vOut.z = pztz - " << weight << ";\n"
		   << "\t\t		break;\n"
		   << "\t\t	case 4 :\n"
		   << "\t\t		vOut.x = pxtx - " << weight << ";\n"
		   << "\t\t		vOut.y = pyty + " << weight << ";\n"
		   << "\t\t		vOut.z = pztz + " << weight << ";\n"
		   << "\t\t		break;\n"
		   << "\t\t	case 5 :\n"
		   << "\t\t		vOut.x = pxtx - " << weight << ";\n"
		   << "\t\t		vOut.y = pyty - " << weight << ";\n"
		   << "\t\t		vOut.z = pztz + " << weight << ";\n"
		   << "\t\t		break;\n"
		   << "\t\t	case 6 :\n"
		   << "\t\t		vOut.x = pxtx - " << weight << ";\n"
		   << "\t\t		vOut.y = pyty + " << weight << ";\n"
		   << "\t\t		vOut.z = pztz - " << weight << ";\n"
		   << "\t\t		break;\n"
		   << "\t\t	case 7 :\n"
		   << "\t\t		vOut.x = pxtx - " << weight << ";\n"
		   << "\t\t		vOut.y = pyty - " << weight << ";\n"
		   << "\t\t		vOut.z = pztz - " << weight << ";\n"
		   << "\t\t		break;\n"
		   << "\t\t}\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		if (std::abs(m_Xpand) <= 1)
			m_Fill = m_Xpand * T(0.5);
		else
			m_Fill = std::sqrt(m_Xpand) * T(0.5);
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Xpand, prefix + "cubicLattice_3D_xpand", T(0.2)));//Original used a prefix of cubic3D_, which is incompatible with Ember's design.
		m_Params.push_back(ParamWithName<T>(&m_Style, prefix + "cubicLattice_3D_style", 1, eParamType::INTEGER, 1, 2));
		m_Params.push_back(ParamWithName<T>(true, &m_Fill, prefix + "cubicLattice_3D_fill"));//Precalc.
	}

private:
	T m_Xpand;
	T m_Style;
	T m_Fill;//Precalc.
};

/// <summary>
/// foci_3D.
/// </summary>
template <typename T>
class Foci3DVariation : public Variation<T>
{
public:
	Foci3DVariation(T weight = 1.0) : Variation<T>("foci_3D", eVariationId::VAR_FOCI3D, weight, false, false, false, false, true) { }

	VARCOPY(Foci3DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T expx = std::exp(helper.In.x) * T(0.5);
		T expnx = T(0.25) / Zeps(expx);
		T boot = helper.In.z == 0 ? helper.m_PrecalcAtanyx : helper.In.z;
		T tmp = m_Weight / Zeps(expx + expnx - (std::cos(helper.In.y) * std::cos(boot)));
		helper.Out.x = (expx - expnx) * tmp;
		helper.Out.y = std::sin(helper.In.y) * tmp;
		helper.Out.z = std::sin(boot) * tmp;
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss;
		intmax_t varIndex = IndexInXform();
		string weight = WeightDefineString();
		ss << "\t{\n"
		   << "\t\treal_t expx = exp(vIn.x) * (real_t)(0.5);\n"
		   << "\t\treal_t expnx = (real_t)(0.25) / Zeps(expx);\n"
		   << "\t\treal_t boot = vIn.z == 0 ? precalcAtanyx : vIn.z;\n"
		   << "\t\treal_t tmp = " << weight << " / Zeps(expx + expnx - (cos(vIn.y) * cos(boot)));\n"
		   << "\n"
		   << "\t\tvOut.x = (expx - expnx) * tmp;\n"
		   << "\t\tvOut.y = sin(vIn.y) * tmp;\n"
		   << "\t\tvOut.z = sin(boot) * tmp;\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Zeps" };
	}
};

/// <summary>
/// foci_p.
/// Idea by Chaosfissure.
/// </summary>
template <typename T>
class FociPVariation : public ParametricVariation<T>
{
public:
	FociPVariation(T weight = 1.0) : ParametricVariation<T>("foci_p", eVariationId::VAR_FOCI_P, weight)
	{
		Init();
	}

	PARVARCOPY(FociPVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T exp_x = Zeps(std::exp(helper.In.x));
		T exp_x_2 = exp_x * m_C1;
		T exp_nz = m_C2 / exp_x;
		T cos_y = std::cos(helper.In.y);
		T sin_y = std::sin(helper.In.y);
		T r = m_Weight / Zeps(exp_x_2 + exp_nz - cos_y);
		helper.Out.x = (exp_x_2 - exp_nz) * r;
		helper.Out.y = sin_y * r;
		helper.Out.z = DefaultZ(helper);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string c1 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string c2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t exp_x = Zeps(exp(vIn.x));\n"
		   << "\t\treal_t exp_x_2 = exp_x * " << c1 << ";\n"
		   << "\t\treal_t exp_nz = " << c2 << " / exp_x;\n"
		   << "\t\treal_t cos_y = cos(vIn.y);\n"
		   << "\t\treal_t sin_y = sin(vIn.y);\n"
		   << "\t\treal_t r = " << weight << " / Zeps(exp_x_2 + exp_nz - cos_y);\n"
		   << "\n"
		   << "\t\tvOut.x = (exp_x_2 - exp_nz) * r;\n"
		   << "\t\tvOut.y = sin_y * r;\n"
		   << "\t\tvOut.z = " << DefaultZCl()
		   << "\t}\n";
		return ss.str();
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Zeps" };
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_C1, prefix + "foci_p_c1", T(0.5)));
		m_Params.push_back(ParamWithName<T>(&m_C2, prefix + "foci_p_c2", T(0.5)));
	}

private:
	T m_C1;
	T m_C2;
};

/// <summary>
/// ho.
/// </summary>
template <typename T>
class HoVariation : public ParametricVariation<T>
{
public:
	HoVariation(T weight = 1.0) : ParametricVariation<T>("ho", eVariationId::VAR_HO, weight)
	{
		Init();
	}

	PARVARCOPY(HoVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T uu = SQR(helper.In.x);
		T vv = SQR(helper.In.y);
		T ww = SQR(helper.In.z);
		T atOmegaX = std::atan2(vv, ww);
		T atOmegaY = std::atan2(uu, ww);
		T su = std::sin(helper.In.x);
		T cu = std::cos(helper.In.x);
		T sv = std::sin(helper.In.y);
		T cv = std::cos(helper.In.y);
		T cucv = cu * cv;
		T sucv = su * cv;
		T x = std::pow(std::abs(cucv), m_XPow) + (cucv * m_XPow) + (T(0.25) * atOmegaX);//Must fabs first argument to pow, because negative values will return NaN.
		T y = std::pow(std::abs(sucv), m_YPow) + (sucv * m_YPow) + (T(0.25) * atOmegaY);//Original did not do this and would frequently return bad values.
		T z = std::pow(std::abs(sv), m_ZPow) + sv * m_ZPow;
		helper.Out.x = m_Weight * x;
		helper.Out.y = m_Weight * y;
		helper.Out.z = m_Weight * z;
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string xpow = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string ypow = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string zpow = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t uu = SQR(vIn.x);\n"
		   << "\t\treal_t vv = SQR(vIn.y);\n"
		   << "\t\treal_t ww = SQR(vIn.z);\n"
		   << "\t\treal_t atOmegaX = atan2(vv, ww);\n"
		   << "\t\treal_t atOmegaY = atan2(uu, ww);\n"
		   << "\t\treal_t su = sin(vIn.x);\n"
		   << "\t\treal_t cu = cos(vIn.x);\n"
		   << "\t\treal_t sv = sin(vIn.y);\n"
		   << "\t\treal_t cv = cos(vIn.y);\n"
		   << "\t\treal_t cucv = cu * cv;\n"
		   << "\t\treal_t sucv = su * cv;\n"
		   << "\t\treal_t x = pow(fabs(cucv), " << xpow << ") + fma(cucv, " << xpow << ", (real_t)(0.25) * atOmegaX);\n"
		   << "\t\treal_t y = pow(fabs(sucv), " << ypow << ") + fma(sucv, " << ypow << ", (real_t)(0.25) * atOmegaY);\n"
		   << "\t\treal_t z = fma(sv, " << zpow << ", pow(fabs(sv), " << zpow << "));\n"
		   << "\n"
		   << "\t\tvOut.x = " << weight << " * x;\n"
		   << "\t\tvOut.y = " << weight << " * y;\n"
		   << "\t\tvOut.z = " << weight << " * z;\n"
		   << "\t}\n";
		return ss.str();
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_XPow, prefix + "ho_xpow", 3));
		m_Params.push_back(ParamWithName<T>(&m_YPow, prefix + "ho_ypow", 3));
		m_Params.push_back(ParamWithName<T>(&m_ZPow, prefix + "ho_zpow", 3));
	}

private:
	T m_XPow;
	T m_YPow;
	T m_ZPow;
};

/// <summary>
/// Julia3Dq.
/// </summary>
template <typename T>
class Julia3DqVariation : public ParametricVariation<T>
{
public:
	Julia3DqVariation(T weight = 1.0) : ParametricVariation<T>("julia3Dq", eVariationId::VAR_JULIA3DQ, weight, true, true, false, false, true)
	{
		Init();
	}

	PARVARCOPY(Julia3DqVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T temp = helper.m_PrecalcAtanyx * m_InvPower + rand.Rand() * m_InvPower2pi;
		T sina = std::sin(temp);
		T cosa = std::cos(temp);
		T z = helper.In.z * m_AbsInvPower;
		T r2d = helper.m_PrecalcSumSquares;
		T r = m_Weight * std::pow(r2d + SQR(z), m_HalfInvPower);
		T rsss = r * helper.m_PrecalcSqrtSumSquares;
		helper.Out.x = rsss * cosa;
		helper.Out.y = rsss * sina;
		helper.Out.z = r * z;
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string power        = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string divisor      = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string invPower     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string absInvPower  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string halfInvPower = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string invPower2pi  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t temp = fma(precalcAtanyx, " << invPower << ", MwcNext(mwc) * " << invPower2pi << ");\n"
		   << "\t\treal_t sina = sin(temp);\n"
		   << "\t\treal_t cosa = cos(temp);\n"
		   << "\t\treal_t z = vIn.z * " << absInvPower << ";\n"
		   << "\t\treal_t r2d = precalcSumSquares;\n"
		   << "\t\treal_t r = " << weight << " * pow(fma(z, z, r2d), " << halfInvPower << ");\n"
		   << "\t\treal_t rsss = r * precalcSqrtSumSquares;\n"
		   << "\n"
		   << "\t\tvOut.x = rsss * cosa;\n"
		   << "\t\tvOut.y = rsss * sina;\n"
		   << "\t\tvOut.z = r * z;\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		m_InvPower = m_Divisor / m_Power;
		m_AbsInvPower = std::abs(m_InvPower);
		m_HalfInvPower = T(0.5) * m_InvPower - T(0.5);
		m_InvPower2pi = M_2PI / m_Power;
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Power, prefix + "julia3Dq_power", 3, eParamType::INTEGER_NONZERO));
		m_Params.push_back(ParamWithName<T>(&m_Divisor, prefix + "julia3Dq_divisor", 2, eParamType::INTEGER_NONZERO));
		m_Params.push_back(ParamWithName<T>(true, &m_InvPower, prefix + "julia3Dq_inv_power"));//Precalc.
		m_Params.push_back(ParamWithName<T>(true, &m_AbsInvPower, prefix + "julia3Dq_abs_inv_power"));
		m_Params.push_back(ParamWithName<T>(true, &m_HalfInvPower, prefix + "julia3Dq_half_inv_power"));
		m_Params.push_back(ParamWithName<T>(true, &m_InvPower2pi, prefix + "julia3Dq_inv_power_2pi"));
	}

private:
	T m_Power;
	T m_Divisor;
	T m_InvPower;//Precalc.
	T m_AbsInvPower;
	T m_HalfInvPower;
	T m_InvPower2pi;
};

/// <summary>
/// line.
/// </summary>
template <typename T>
class LineVariation : public ParametricVariation<T>
{
public:
	LineVariation(T weight = 1.0) : ParametricVariation<T>("line", eVariationId::VAR_LINE, weight)
	{
		Init();
	}

	PARVARCOPY(LineVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T r = rand.Frand01<T>() * m_Weight;
		helper.Out.x = m_Ux * r;
		helper.Out.y = m_Uy * r;
		helper.Out.z = m_Uz * r;
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string delta = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string phi = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string ux = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string uy = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string uz = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t r = MwcNext01(mwc) * " << weight << ";\n"
		   << "\n"
		   << "\t\tvOut.x = " << ux << " * r;\n"
		   << "\t\tvOut.y = " << uy << " * r;\n"
		   << "\t\tvOut.z = " << uz << " * r;\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		//Unit vector of the line.
		m_Ux = std::cos(m_Delta * T(M_PI)) * std::cos(m_Phi * T(M_PI));
		m_Uy = std::sin(m_Delta * T(M_PI)) * std::cos(m_Phi * T(M_PI));
		m_Uz = std::sin(m_Phi * T(M_PI));
		T r = std::sqrt(SQR(m_Ux) + SQR(m_Uy) + SQR(m_Uz));
		//Normalize.
		m_Ux /= r;
		m_Uy /= r;
		m_Uz /= r;
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Delta, prefix + "line_delta"));
		m_Params.push_back(ParamWithName<T>(&m_Phi, prefix + "line_phi"));
		m_Params.push_back(ParamWithName<T>(true, &m_Ux, prefix + "line_ux"));//Precalc.
		m_Params.push_back(ParamWithName<T>(true, &m_Uy, prefix + "line_uy"));
		m_Params.push_back(ParamWithName<T>(true, &m_Uz, prefix + "line_uz"));
	}

private:
	T m_Delta;
	T m_Phi;
	T m_Ux;//Precalc.
	T m_Uy;
	T m_Uz;
};

/// <summary>
/// Loonie2.
/// </summary>
template <typename T>
class Loonie2Variation : public ParametricVariation<T>
{
public:
	Loonie2Variation(T weight = 1.0) : ParametricVariation<T>("loonie2", eVariationId::VAR_LOONIE2, weight, true, true)
	{
		Init();
	}

	PARVARCOPY(Loonie2Variation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		int i;
		T xrt = helper.In.x, yrt = helper.In.y, swp;
		T r2 = xrt * m_Coss + std::abs(yrt) * m_Sins;
		T circle = helper.m_PrecalcSqrtSumSquares;

		for (i = 0; i < m_Sides - 1; i++)
		{
			swp = xrt * m_Cosa - yrt * m_Sina;
			yrt = xrt * m_Sina + yrt * m_Cosa;
			xrt = swp;
			r2 = std::max(r2, xrt * m_Coss + std::abs(yrt) * m_Sins);
		}

		r2 = r2 * m_Cosc + circle * m_Sinc;

		if (i > 1)
			r2 = SQR(r2);
		else
			r2 = std::abs(r2) * r2;

		if (r2 > 0 && (r2 < m_W2))
		{
			T r = m_Weight * std::sqrt(std::abs(m_W2 / r2 - 1));
			helper.Out.x = r * helper.In.x;
			helper.Out.y = r * helper.In.y;
		}
		else if (r2 < 0)
		{
			T r = m_Weight / std::sqrt(std::abs(m_W2 / r2) - 1);
			helper.Out.x = r * helper.In.x;
			helper.Out.y = r * helper.In.y;
		}
		else
		{
			helper.Out.x = m_Weight * helper.In.x;
			helper.Out.y = m_Weight * helper.In.y;
		}

		helper.Out.z = DefaultZ(helper);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string weight = WeightDefineString();
		string index = ss2.str();
		string sides  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string star   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string circle = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string w2     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string sina   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string cosa   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string sins   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string coss   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string sinc   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string cosc   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tint i;\n"
		   << "\t\treal_t xrt = vIn.x, yrt = vIn.y, swp;\n"
		   << "\t\treal_t r2 = fma(xrt, " << coss << ", fabs(yrt) * " << sins << ");\n"
		   << "\t\treal_t circle = precalcSqrtSumSquares;\n"
		   << "\n"
		   << "\t\tfor (i = 0; i < " << sides << " - 1; i++)\n"
		   << "\t\t{\n"
		   << "\t\t	swp = fma(xrt, " << cosa << ", -(yrt * " << sina << "));\n"
		   << "\t\t	yrt = fma(xrt, " << sina << ", yrt * " << cosa << ");\n"
		   << "\t\t	xrt = swp;\n"
		   << "\n"
		   << "\t\t	r2 = max(r2, fma(xrt, " << coss << ", fabs(yrt) * " << sins << "));\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\tr2 = fma(r2, " << cosc << ", circle * " << sinc << ");\n"
		   << "\n"
		   << "\t\tif (i > 1)\n"
		   << "\t\t	r2 = SQR(r2);\n"
		   << "\t\telse\n"
		   << "\t\t	r2 = fabs(r2) * r2;\n"
		   << "\n"
		   << "\t\tif (r2 > 0 && (r2 < " << w2 << "))\n"
		   << "\t\t{\n"
		   << "\t\t	real_t r = " << weight << " * sqrt(fabs(" << w2 << " / r2 - 1));\n"
		   << "\n"
		   << "\t\t	vOut.x = r * vIn.x;\n"
		   << "\t\t	vOut.y = r * vIn.y;\n"
		   << "\t\t}\n"
		   << "\t\telse if (r2 < 0)\n"
		   << "\t\t{\n"
		   << "\t\t	real_t r = " << weight << " / sqrt(fabs(" << w2 << " / r2) - 1);\n"
		   << "\n"
		   << "\t\t	vOut.x = r * vIn.x;\n"
		   << "\t\t	vOut.y = r * vIn.y;\n"
		   << "\t\t}\n"
		   << "\t\telse\n"
		   << "\t\t{\n"
		   << "\t\t	vOut.x = " << weight << " * vIn.x;\n"
		   << "\t\t	vOut.y = " << weight << " * vIn.y;\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\tvOut.z = " << DefaultZCl()
		   << "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		auto a = M_2PI / m_Sides;
		auto s = T(-M_PI_2) * m_Star;
		auto c = T(M_PI_2) * m_Circle;
		m_W2 = SQR(m_Weight);
		sincos(a, &m_Sina, &m_Cosa);
		sincos(s, &m_Sins, &m_Coss);
		sincos(c, &m_Sinc, &m_Cosc);
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Sides, prefix + "loonie2_sides", 4, eParamType::INTEGER, 1, 50));
		m_Params.push_back(ParamWithName<T>(&m_Star, prefix + "loonie2_star", 0, eParamType::REAL, -1, 1));
		m_Params.push_back(ParamWithName<T>(&m_Circle, prefix + "loonie2_circle", 0, eParamType::REAL, -1, 1));
		m_Params.push_back(ParamWithName<T>(true, &m_W2, prefix + "loonie2_w2"));//Precalc.
		m_Params.push_back(ParamWithName<T>(true, &m_Sina, prefix + "loonie2_sina"));
		m_Params.push_back(ParamWithName<T>(true, &m_Cosa, prefix + "loonie2_cosa"));
		m_Params.push_back(ParamWithName<T>(true, &m_Sins, prefix + "loonie2_sins"));
		m_Params.push_back(ParamWithName<T>(true, &m_Coss, prefix + "loonie2_coss"));
		m_Params.push_back(ParamWithName<T>(true, &m_Sinc, prefix + "loonie2_sinc"));
		m_Params.push_back(ParamWithName<T>(true, &m_Cosc, prefix + "loonie2_cosc"));
	}

private:
	T m_Sides;
	T m_Star;
	T m_Circle;
	T m_W2;//Precalc.
	T m_Sina;
	T m_Cosa;
	T m_Sins;
	T m_Coss;
	T m_Sinc;
	T m_Cosc;
};

/// <summary>
/// Loonie3.
/// </summary>
template <typename T>
class Loonie3Variation : public ParametricVariation<T>
{
public:
	Loonie3Variation(T weight = 1.0) : ParametricVariation<T>("loonie3", eVariationId::VAR_LOONIE3, weight, true)
	{
		Init();
	}

	PARVARCOPY(Loonie3Variation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T r2;

		if (helper.In.x > EPS)
			r2 = SQR(helper.m_PrecalcSumSquares / helper.In.x);
		else
			r2 = 2 * m_W2;

		if (r2 < m_W2)
		{
			T r = m_Weight * std::sqrt(m_W2 / r2 - 1);
			helper.Out.x = r * helper.In.x;
			helper.Out.y = r * helper.In.y;
		}
		else
		{
			helper.Out.x = m_Weight * helper.In.x;
			helper.Out.y = m_Weight * helper.In.y;
		}

		helper.Out.z = DefaultZ(helper);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string w2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t r2;\n"
		   << "\n"
		   << "\t\tif (vIn.x > EPS)\n"
		   << "\t\t	r2 = SQR(precalcSumSquares / vIn.x);\n"
		   << "\t\telse\n"
		   << "\t\t	r2 = 2 * " << w2 << ";\n"
		   << "\n"
		   << "\t\tif (r2 < " << w2 << ")\n"
		   << "\t\t{\n"
		   << "\t\t	real_t r = " << weight << " * sqrt(" << w2 << " / r2 - 1);\n"
		   << "\n"
		   << "\t\t	vOut.x = r * vIn.x;\n"
		   << "\t\t	vOut.y = r * vIn.y;\n"
		   << "\t\t}\n"
		   << "\t\telse\n"
		   << "\t\t{\n"
		   << "\t\t	vOut.x = " << weight << " * vIn.x;\n"
		   << "\t\t	vOut.y = " << weight << " * vIn.y;\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\tvOut.z = " << DefaultZCl()
		   << "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		m_W2 = SQR(m_Weight);
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(true, &m_W2, prefix + "loonie3_w2"));//Precalc.
	}

private:
	T m_W2;//Precalc.
};

/// <summary>
/// loonie_3D.
/// </summary>
template <typename T>
class Loonie3DVariation : public ParametricVariation<T>
{
public:
	Loonie3DVariation(T weight = 1.0) : ParametricVariation<T>("loonie_3D", eVariationId::VAR_LOONIE3D, weight, true, false, false, false, true)
	{
		Init();
	}

	PARVARCOPY(Loonie3DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T kikr = helper.m_PrecalcAtanyx;
		T efTez = helper.In.z == 0 ? kikr : helper.In.z;
		T r2 = helper.m_PrecalcSumSquares + SQR(efTez);

		if (r2 < m_Vv)
		{
			T r = m_Weight * std::sqrt(m_Vv / r2 - 1);
			helper.Out.x = r * helper.In.x;
			helper.Out.y = r * helper.In.y;
			helper.Out.z = r * efTez * T(0.5);
		}
		else
		{
			helper.Out.x = m_Weight * helper.In.x;
			helper.Out.y = m_Weight * helper.In.y;
			helper.Out.z = m_Weight * efTez * T(0.5);
		}
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string vv = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t kikr = precalcAtanyx;\n"
		   << "\t\treal_t efTez = vIn.z == 0 ? kikr : vIn.z;\n"
		   << "\t\treal_t r2 = fma(efTez, efTez, precalcSumSquares);\n"
		   << "\n"
		   << "\t\tif (r2 < " << vv << ")\n"
		   << "\t\t{\n"
		   << "\t\t	real_t r = " << weight << " * sqrt(" << vv << " / r2 - 1);\n"
		   << "\n"
		   << "\t\t	vOut.x = r * vIn.x;\n"
		   << "\t\t	vOut.y = r * vIn.y;\n"
		   << "\t\t	vOut.z = r * efTez * (real_t)(0.5);\n"
		   << "\t\t}\n"
		   << "\t\telse\n"
		   << "\t\t{\n"
		   << "\t\t	vOut.x = " << weight << " * vIn.x;\n"
		   << "\t\t	vOut.y = " << weight << " * vIn.y;\n"
		   << "\t\t	vOut.z = " << weight << " * efTez * (real_t)(0.5);\n"
		   << "\t\t}\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		m_Vv = SQR(m_Weight);
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(true, &m_Vv, prefix + "loonie_3D_vv"));//Precalcs only, no params.
	}

private:
	T m_Vv;//Precalcs only, no params.
};

/// <summary>
/// mcarpet.
/// </summary>
template <typename T>
class McarpetVariation : public ParametricVariation<T>
{
public:
	McarpetVariation(T weight = 1.0) : ParametricVariation<T>("mcarpet", eVariationId::VAR_MCARPET, weight, true)
	{
		Init();
	}

	PARVARCOPY(McarpetVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T t = helper.m_PrecalcSumSquares * T(0.25) + 1;
		T r = m_Weight / t;
		helper.Out.x = helper.In.x * r * m_X;
		helper.Out.y = helper.In.y * r * m_Y;
		helper.Out.x += (1 - (m_Twist * SQR(helper.In.x)) + helper.In.y) * m_Weight;//The += is intentional.
		helper.Out.y += m_Tilt * helper.In.x * m_Weight;
		helper.Out.z = DefaultZ(helper);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string x = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string y = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string twist = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string tilt = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t t = fma(precalcSumSquares, (real_t)(0.25), (real_t)(1.0));\n"
		   << "\t\treal_t r = " << weight << " / t;\n"
		   << "\n"
		   << "\t\tvOut.x = vIn.x * r * " << x << ";\n"
		   << "\t\tvOut.y = vIn.y * r * " << y << ";\n"
		   << "\t\tvOut.x += (1 - (" << twist << " * SQR(vIn.x)) + vIn.y) * " << weight << ";\n"
		   << "\t\tvOut.y += " << tilt << " * vIn.x * " << weight << ";\n"
		   << "\t\tvOut.z = " << DefaultZCl()
		   << "\t}\n";
		return ss.str();
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_X, prefix + "mcarpet_x"));
		m_Params.push_back(ParamWithName<T>(&m_Y, prefix + "mcarpet_y"));
		m_Params.push_back(ParamWithName<T>(&m_Twist, prefix + "mcarpet_twist"));
		m_Params.push_back(ParamWithName<T>(&m_Tilt, prefix + "mcarpet_tilt"));
	}

private:
	T m_X;
	T m_Y;
	T m_Twist;
	T m_Tilt;
};

/// <summary>
/// waves2_3D.
/// Original used a precalc for the input points, but it doesn't
/// work with Ember's design (and is also likely wrong), so it gets calculated on every iter
/// which is slightly slower, but more correct.
/// </summary>
template <typename T>
class Waves23DVariation : public ParametricVariation<T>
{
public:
	Waves23DVariation(T weight = 1.0) : ParametricVariation<T>("waves2_3D", eVariationId::VAR_WAVES23D, weight)
	{
		Init();
	}

	PARVARCOPY(Waves23DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T avgxy = (helper.In.x + helper.In.y) * T(0.5);
		helper.Out.x = m_Weight * (helper.In.x + m_Scale * std::sin(helper.In.y * m_Freq));
		helper.Out.y = m_Weight * (helper.In.y + m_Scale * std::sin(helper.In.x * m_Freq));
		helper.Out.z = m_Weight * (helper.In.z + m_Scale * std::sin(avgxy * m_Freq));//Averages the XY to get Z.
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string freq = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string scale = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t avgxy = (vIn.x + vIn.y) * (real_t)(0.5);\n"
		   << "\n"
		   << "\t\tvOut.x = " << weight << " * fma(" << scale << ", sin(vIn.y * " << freq << "), vIn.x);\n"
		   << "\t\tvOut.y = " << weight << " * fma(" << scale << ", sin(vIn.x * " << freq << "), vIn.y);\n"
		   << "\t\tvOut.z = " << weight << " * fma(" << scale << ", sin(avgxy * " << freq << "), vIn.z);\n"
		   << "\t}\n";
		return ss.str();
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Freq, prefix + "waves2_3D_freq", 2));
		m_Params.push_back(ParamWithName<T>(&m_Scale, prefix + "waves2_3D_scale", 1));
	}

private:
	T m_Freq;
	T m_Scale;
};

/// <summary>
/// Pie3D.
/// </summary>
template <typename T>
class Pie3DVariation : public ParametricVariation<T>
{
public:
	Pie3DVariation(T weight = 1.0) : ParametricVariation<T>("pie3D", eVariationId::VAR_PIE3D, weight)
	{
		Init();
	}

	PARVARCOPY(Pie3DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		int sl = int(rand.Frand01<T>() * m_Slices + T(0.5));
		T a = m_Rotation + M_2PI * (sl + rand.Frand01<T>() * m_Thickness) / m_Slices;
		T r = m_Weight * rand.Frand01<T>();
		helper.Out.x = r * std::cos(a);
		helper.Out.y = r * std::sin(a);
		helper.Out.z = m_Weight * std::sin(r);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string slices    = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string rotation  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string thickness = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tint sl = (int)fma(MwcNext01(mwc), " << slices << ", (real_t)(0.5));\n"
		   << "\t\treal_t a = fma(M_2PI, fma(MwcNext01(mwc), " << thickness << ", (real_t)(sl)) / " << slices << ", " << rotation << ");\n"
		   << "\t\treal_t r = " << weight << " * MwcNext01(mwc);\n"
		   << "\n"
		   << "\t\tvOut.x = r * cos(a);\n"
		   << "\t\tvOut.y = r * sin(a);\n"
		   << "\t\tvOut.z = " << weight << " * sin(r);\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual void Random(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		m_Params[0].Set(10 * rand.Frand01<T>());//Slices.
		m_Params[1].Set(M_2PI * rand.Frand11<T>());//Rotation.
		m_Thickness = rand.Frand01<T>();
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Slices, prefix + "pie3D_slices", 6, eParamType::INTEGER_NONZERO, 1));
		m_Params.push_back(ParamWithName<T>(&m_Rotation, prefix + "pie3D_rotation", T(0.5), eParamType::REAL_CYCLIC, 0, M_2PI));
		m_Params.push_back(ParamWithName<T>(&m_Thickness, prefix + "pie3D_thickness", T(0.5), eParamType::REAL, 0, 1));
	}

private:
	T m_Slices;
	T m_Rotation;
	T m_Thickness;
};

/// <summary>
/// popcorn2_3D.
/// </summary>
template <typename T>
class Popcorn23DVariation : public ParametricVariation<T>
{
public:
	Popcorn23DVariation(T weight = 1.0) : ParametricVariation<T>("popcorn2_3D", eVariationId::VAR_POPCORN23D, weight, false, false, false, false, true)
	{
		Init();
	}

	PARVARCOPY(Popcorn23DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T otherZ, tempPZ = 0;
		T tempTZ = helper.In.z == 0 ? m_Vv * m_SinTanC * helper.m_PrecalcAtanyx : helper.In.z;

		if (m_VarType == eVariationType::VARTYPE_PRE)
			otherZ = helper.In.z;
		else
			otherZ = outPoint.m_Z;

		if (otherZ == 0)
			tempPZ = m_Vv * m_SinTanC * helper.m_PrecalcAtanyx;

		helper.Out.x = m_HalfWeight * (helper.In.x + m_X * std::sin(SafeTan<T>(m_C * helper.In.y)));
		helper.Out.y = m_HalfWeight * (helper.In.y + m_Y * std::sin(SafeTan<T>(m_C * helper.In.x)));
		helper.Out.z = tempPZ + m_Vv * (m_Z * m_SinTanC * tempTZ);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		int i = 0;
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string x   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string y   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string z   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string c   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string stc = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string hw  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string vv  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t otherZ, tempPZ = 0;\n"
		   << "\t\treal_t tempTZ = vIn.z == 0 ? " << vv << " * " << stc << " * precalcAtanyx : vIn.z;\n";

		if (m_VarType == eVariationType::VARTYPE_PRE)
			ss << "\t\totherZ = vIn.z;\n";
		else
			ss << "\t\totherZ = outPoint->m_Z;\n";

		ss << "\t\tif (otherZ == 0)\n"
		   << "\t\t	tempPZ = " << vv << " * " << stc << " * precalcAtanyx;\n"
		   << "\n"
		   << "\t\tvOut.x = " << hw << " * fma(" << x << ", sin(tan(" << c << " * vIn.y)), vIn.x);\n"
		   << "\t\tvOut.y = " << hw << " * fma(" << y << ", sin(tan(" << c << " * vIn.x)), vIn.y);\n"
		   << "\t\tvOut.z = fma(" << vv << ", (" << z << " * " << stc << " * tempTZ), tempPZ);\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		m_SinTanC = std::sin(SafeTan<T>(m_C));
		m_HalfWeight = m_Weight * T(0.5);

		if (std::abs(m_Weight) <= 1)
			m_Vv = std::abs(m_Weight) * m_Weight;//Sqr(m_Weight) value retaining sign.
		else
			m_Vv = m_Weight;
	}

	virtual void Random(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		m_X = T(0.2) + rand.Frand01<T>();
		m_Y = T(0.2) * rand.Frand01<T>();
		m_Z = T(0.2) * rand.Frand01<T>();
		m_C = 5 * rand.Frand01<T>();
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_X, prefix + "popcorn2_3D_x", T(0.1)));
		m_Params.push_back(ParamWithName<T>(&m_Y, prefix + "popcorn2_3D_y", T(0.1)));
		m_Params.push_back(ParamWithName<T>(&m_Z, prefix + "popcorn2_3D_z", T(0.1)));
		m_Params.push_back(ParamWithName<T>(&m_C, prefix + "popcorn2_3D_c", 3));
		m_Params.push_back(ParamWithName<T>(true, &m_SinTanC, prefix + "popcorn2_3D_sintanc"));
		m_Params.push_back(ParamWithName<T>(true, &m_HalfWeight, prefix + "popcorn2_3D_half_weight"));
		m_Params.push_back(ParamWithName<T>(true, &m_Vv, prefix + "popcorn2_3D_vv"));
	}

private:
	T m_X;
	T m_Y;
	T m_Z;
	T m_C;
	T m_SinTanC;//Precalcs.
	T m_HalfWeight;
	T m_Vv;
};

/// <summary>
/// sinusoidal3d.
/// </summary>
template <typename T>
class Sinusoidal3DVariation : public Variation<T>
{
public:
	Sinusoidal3DVariation(T weight = 1.0) : Variation<T>("sinusoidal3D", eVariationId::VAR_SINUSOIDAL3D, weight) { }

	VARCOPY(Sinusoidal3DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		helper.Out.x = m_Weight * std::sin(helper.In.x);
		helper.Out.y = m_Weight * std::sin(helper.In.y);
		helper.Out.z = m_Weight * (std::atan2(SQR(helper.In.x), SQR(helper.In.y)) * std::cos(helper.In.z));
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss;
		intmax_t varIndex = IndexInXform();
		string weight = WeightDefineString();
		ss << "\t{\n"
		   << "\t\tvOut.x = " << weight << " * sin(vIn.x);\n"
		   << "\t\tvOut.y = " << weight << " * sin(vIn.y);\n"
		   << "\t\tvOut.z = " << weight << " * (atan2(SQR(vIn.x), SQR(vIn.y)) * cos(vIn.z));\n"
		   << "\t}\n";
		return ss.str();
	}
};

/// <summary>
/// scry_3D.
/// </summary>
template <typename T>
class Scry3DVariation : public ParametricVariation<T>
{
public:
	Scry3DVariation(T weight = 1.0) : ParametricVariation<T>("scry_3D", eVariationId::VAR_SCRY3D, weight, true, false, false, false, true)
	{
		Init();
	}

	PARVARCOPY(Scry3DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T t = helper.m_PrecalcSumSquares + SQR(helper.In.z);
		T r = 1 / Zeps(std::sqrt(t) * (t + m_InvWeight));
		T z = helper.In.z == 0 ? helper.m_PrecalcAtanyx : helper.In.z;
		helper.Out.x = helper.In.x * r;
		helper.Out.y = helper.In.y * r;
		helper.Out.z = z * r;
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		int i = 0;
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string invWeight = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t t = fma(vIn.z, vIn.z, precalcSumSquares);\n"
		   << "\t\treal_t r = 1 / Zeps(sqrt(t) * (t + " << invWeight << "));\n"
		   << "\t\treal_t z = vIn.z == 0 ? precalcAtanyx : vIn.z;\n"
		   << "\n"
		   << "\t\tvOut.x = vIn.x * r;\n"
		   << "\t\tvOut.y = vIn.y * r;\n"
		   << "\t\tvOut.z = z * r;\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		m_InvWeight = 1 / Zeps(m_Weight);
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Zeps" };
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(true, &m_InvWeight, prefix + "scry_3D_inv_weight"));//Precalcs only, no params.
	}

private:
	T m_InvWeight;//Precalcs only, no params.
};

/// <summary>
/// shredlin.
/// </summary>
template <typename T>
class ShredlinVariation : public ParametricVariation<T>
{
public:
	ShredlinVariation(T weight = 1.0) : ParametricVariation<T>("shredlin", eVariationId::VAR_SHRED_LIN, weight)
	{
		Init();
	}

	PARVARCOPY(ShredlinVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		const int xpos = helper.In.x < 0;
		const int ypos = helper.In.y < 0;
		const T xrng = helper.In.x / m_XDistance;
		const T yrng = helper.In.y / m_YDistance;
		helper.Out.x = m_Xw * ((xrng - int(xrng)) * m_XWidth + int(xrng) + (T(0.5) - xpos) * m_1mX);
		helper.Out.y = m_Yw * ((yrng - int(yrng)) * m_YWidth + int(yrng) + (T(0.5) - ypos) * m_1mY);
		helper.Out.z = m_Weight * helper.In.z;
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string xdist  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string xwidth = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string ydist  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string ywidth = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string xw     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string yw     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string onemx  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string onemy  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tconst int xpos = vIn.x < 0;\n"
		   << "\t\tconst int ypos = vIn.y < 0;\n"
		   << "\t\tconst real_t xrng = vIn.x / " << xdist << ";\n"
		   << "\t\tconst real_t yrng = vIn.y / " << ydist << ";\n"
		   << "\n"
		   << "\t\tvOut.x = " << xw << " * fma((xrng - (int)xrng), " << xwidth << ", (int)xrng + ((real_t)(0.5) - xpos) * " << onemx << ");\n"
		   << "\t\tvOut.y = " << yw << " * fma((yrng - (int)yrng), " << ywidth << ", (int)yrng + ((real_t)(0.5) - ypos) * " << onemy << ");\n"
		   << "\t\tvOut.z = " << weight << " * vIn.z;\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		m_Xw = m_Weight * m_XDistance;
		m_Yw = m_Weight * m_YDistance;
		m_1mX = 1 - m_XWidth;
		m_1mY = 1 - m_YWidth;
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_XDistance, prefix + "shredlin_xdistance", 1, eParamType::REAL_NONZERO));
		m_Params.push_back(ParamWithName<T>(&m_XWidth, prefix + "shredlin_xwidth", T(0.5), eParamType::REAL, -1, 1));
		m_Params.push_back(ParamWithName<T>(&m_YDistance, prefix + "shredlin_ydistance", 1, eParamType::REAL_NONZERO));
		m_Params.push_back(ParamWithName<T>(&m_YWidth, prefix + "shredlin_ywidth", T(0.5), eParamType::REAL, -1, 1));
		m_Params.push_back(ParamWithName<T>(true, &m_Xw, prefix + "shredlin_xw"));
		m_Params.push_back(ParamWithName<T>(true, &m_Yw, prefix + "shredlin_yw"));
		m_Params.push_back(ParamWithName<T>(true, &m_1mX, prefix + "shredlin_1mx"));
		m_Params.push_back(ParamWithName<T>(true, &m_1mY, prefix + "shredlin_1my"));
	}

private:
	T m_XDistance;
	T m_XWidth;
	T m_YDistance;
	T m_YWidth;
	T m_Xw;//Precalc.
	T m_Yw;
	T m_1mX;
	T m_1mY;
};

/// <summary>
/// splitbrdr.
/// </summary>
template <typename T>
class SplitBrdrVariation : public ParametricVariation<T>
{
public:
	SplitBrdrVariation(T weight = 1.0) : ParametricVariation<T>("SplitBrdr", eVariationId::VAR_SPLIT_BRDR, weight, true)
	{
		Init();
	}

	PARVARCOPY(SplitBrdrVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T b = m_Weight / (helper.m_PrecalcSumSquares * T(0.25) + 1);
		T roundX = std::rint(helper.In.x);
		T roundY = std::rint(helper.In.y);
		T offsetX = helper.In.x - roundX;
		T offsetY = helper.In.y - roundY;
		helper.Out.x = helper.In.x * b;
		helper.Out.y = helper.In.y * b;

		if (rand.Frand01<T>() >= T(0.75))
		{
			helper.Out.x += m_Weight * (offsetX * T(0.5) + roundX);
			helper.Out.y += m_Weight * (offsetY * T(0.5) + roundY);
		}
		else
		{
			if (std::abs(offsetX) >= std::abs(offsetY))
			{
				if (offsetX >= 0)
				{
					helper.Out.x += m_Weight * (offsetX * T(0.5) + roundX + m_X);
					helper.Out.y += m_Weight * (offsetY * T(0.5) + roundY + m_Y * offsetY / Zeps(offsetX));
				}
				else
				{
					helper.Out.x += m_Weight * (offsetX * T(0.5) + roundX - m_Y);
					helper.Out.y += m_Weight * (offsetY * T(0.5) + roundY - m_Y * offsetY / Zeps(offsetX));
				}
			}
			else
			{
				if (offsetY >= 0)
				{
					helper.Out.y += m_Weight * (offsetY * T(0.5) + roundY + m_Y);
					helper.Out.x += m_Weight * (offsetX * T(0.5) + roundX + offsetX / Zeps(offsetY) * m_Y);
				}
				else
				{
					helper.Out.y += m_Weight * (offsetY * T(0.5) + roundY - m_Y);
					helper.Out.x += m_Weight * (offsetX * T(0.5) + roundX - offsetX / Zeps(offsetY) * m_X);
				}
			}
		}

		helper.Out.x += helper.In.x * m_Px;
		helper.Out.y += helper.In.y * m_Py;
		helper.Out.z = DefaultZ(helper);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string x  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string y  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string px = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string py = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\treal_t b = " << weight << " / fma(precalcSumSquares, (real_t)(0.25), (real_t)(1.0));\n"
		   << "\t\treal_t roundX = rint(vIn.x);\n"
		   << "\t\treal_t roundY = rint(vIn.y);\n"
		   << "\t\treal_t offsetX = vIn.x - roundX;\n"
		   << "\t\treal_t offsetY = vIn.y - roundY;\n"
		   << "\n"
		   << "\t\tvOut.x = vIn.x * b;\n"
		   << "\t\tvOut.y = vIn.y * b;\n"
		   << "\n"
		   << "\t\tif (MwcNext01(mwc) >= (real_t)(0.75))\n"
		   << "\t\t{\n"
		   << "\t\t	vOut.x += " << weight << " * fma(offsetX, (real_t)(0.5), roundX);\n"
		   << "\t\t	vOut.y += " << weight << " * fma(offsetY, (real_t)(0.5), roundY);\n"
		   << "\t\t}\n"
		   << "\t\telse\n"
		   << "\t\t{\n"
		   << "\t\t	if (fabs(offsetX) >= fabs(offsetY))\n"
		   << "\t\t	{\n"
		   << "\t\t		if (offsetX >= 0)\n"
		   << "\t\t		{\n"
		   << "\t\t			vOut.x += " << weight << " * fma(offsetX, (real_t)(0.5), roundX + " << x << ");\n"
		   << "\t\t			vOut.y += " << weight << " * fma(offsetY, (real_t)(0.5), fma(" << y << ", offsetY / Zeps(offsetX), roundY));\n"
		   << "\t\t		}\n"
		   << "\t\t		else\n"
		   << "\t\t		{\n"
		   << "\t\t			vOut.x += " << weight << " * fma(offsetX, (real_t)(0.5), roundX - " << y << ");\n"
		   << "\t\t			vOut.y += " << weight << " * fma(offsetY, (real_t)(0.5), roundY - " << y << " * offsetY / Zeps(offsetX));\n"
		   << "\t\t		}\n"
		   << "\t\t	}\n"
		   << "\t\t	else\n"
		   << "\t\t	{\n"
		   << "\t\t		if (offsetY >= 0)\n"
		   << "\t\t		{\n"
		   << "\t\t			vOut.y += " << weight << " * fma(offsetY, (real_t)(0.5), roundY + " << y << ");\n"
		   << "\t\t			vOut.x += " << weight << " * fma(offsetX, (real_t)(0.5), roundX + offsetX / Zeps(offsetY) * " << y << ");\n"
		   << "\t\t		}\n"
		   << "\t\t		else\n"
		   << "\t\t		{\n"
		   << "\t\t			vOut.y += " << weight << " * fma(offsetY, (real_t)(0.5), roundY - " << y << ");\n"
		   << "\t\t			vOut.x += " << weight << " * fma(offsetX, (real_t)(0.5), roundX - offsetX / Zeps(offsetY) * " << x << ");\n"
		   << "\t\t		}\n"
		   << "\t\t	}\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\tvOut.x += vIn.x * " << px << ";\n"
		   << "\t\tvOut.y += vIn.y * " << py << ";\n"
		   << "\t\tvOut.z = " << DefaultZCl()
		   << "\t}\n";
		return ss.str();
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Zeps" };
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_X, prefix + "SplitBrdr_x", T(0.25)));//Original used a prefix of splitb_, which is incompatible with Ember's design.
		m_Params.push_back(ParamWithName<T>(&m_Y, prefix + "SplitBrdr_y", T(0.25)));
		m_Params.push_back(ParamWithName<T>(&m_Px, prefix + "SplitBrdr_px"));
		m_Params.push_back(ParamWithName<T>(&m_Py, prefix + "SplitBrdr_py"));
	}

private:
	T m_X;
	T m_Y;
	T m_Px;
	T m_Py;
};

/// <summary>
/// wdisc.
/// </summary>
template <typename T>
class WdiscVariation : public Variation<T>
{
public:
	WdiscVariation(T weight = 1.0) : Variation<T>("wdisc", eVariationId::VAR_WDISC, weight, true, true, false, false, true) { }

	VARCOPY(WdiscVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		T a = T(M_PI) / (helper.m_PrecalcSqrtSumSquares + 1);
		T r = helper.m_PrecalcAtanyx * T(M_1_PI);

		if (r > 0)
			a = T(M_PI) - a;

		helper.Out.x = m_Weight * r * std::cos(a);
		helper.Out.y = m_Weight * r * std::sin(a);
		helper.Out.z = DefaultZ(helper);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss;
		intmax_t varIndex = IndexInXform();
		string weight = WeightDefineString();
		ss << "\t{\n"
		   << "\t\treal_t a = MPI / (precalcSqrtSumSquares + 1);\n"
		   << "\t\treal_t r = precalcAtanyx * M1PI;\n"
		   << "\n"
		   << "\t\tif (r > 0)\n"
		   << "\t\t	a = MPI - a;\n"
		   << "\n"
		   << "\t\tvOut.x = " << weight << " * r * cos(a);\n"
		   << "\t\tvOut.y = " << weight << " * r * sin(a);\n"
		   << "\t\tvOut.z = " << DefaultZCl()
		   << "\t}\n";
		return ss.str();
	}
};

/// <summary>
/// falloff.
/// </summary>
template <typename T>
class FalloffVariation : public ParametricVariation<T>
{
public:
	FalloffVariation(T weight = 1.0) : ParametricVariation<T>("falloff", eVariationId::VAR_FALLOFF, weight, false, false, false, false, true)
	{
		Init();
	}

	PARVARCOPY(FalloffVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		const T ax = rand.Frand<T>(T(-0.5), T(0.5));
		const T ay = rand.Frand<T>(T(-0.5), T(0.5));
		const T az = rand.Frand<T>(T(-0.5), T(0.5));
		const T r = std::sqrt(Sqr(helper.In.x - m_X0) + Sqr(helper.In.y - m_Y0) + Sqr(helper.In.z - m_Z0));
		const T rc = ((m_Invert != 0 ? std::max<T>(1 - r, 0) : std::max<T>(r, 0)) - m_MinDist) * m_InternalScatter;//Original called a macro named min, which internally performed max.
		const T rs = std::max<T>(rc, 0);
		T sigma, phi, rad, sigmas, sigmac, phis, phic;
		T scale, denom;

		switch (int(m_Type))
		{
			case 0://Linear.
				helper.Out.x = m_Weight * (helper.In.x + m_MulX * ax * rs);
				helper.Out.y = m_Weight * (helper.In.y + m_MulY * ay * rs);
				helper.Out.z = m_Weight * (helper.In.z + m_MulZ * az * rs);
				break;

			case 1://Radial.
				rad = r + m_MulX * ax * rs;
				phi = helper.m_PrecalcAtanyx + m_MulY * ay * rs;
				sigma = std::asin(r == 0 ? 0 : helper.In.z / r) + m_MulZ * az * rs;
				sigmas = std::sin(sigma) * m_Weight;
				sigmac = std::cos(sigma) * m_Weight;
				phis = std::sin(phi);
				phic = std::cos(phi);
				helper.Out.x = rad * sigmac * phic;
				helper.Out.y = rad * sigmac * phis;
				helper.Out.z = rad * sigmas;
				break;

			case 2://Box.
			default:
				scale = Clamp<T>(rs, 0, T(0.9)) + T(0.1);
				denom = 1 / scale;
				helper.Out.x = m_Weight * Lerp<T>(helper.In.x, std::floor(helper.In.x * denom) + scale * ax, m_MulX * rs) + m_MulX * std::pow(ax, m_BoxPow) * rs * denom;//m_BoxPow should be an integer value held in T,
				helper.Out.y = m_Weight * Lerp<T>(helper.In.y, std::floor(helper.In.y * denom) + scale * ay, m_MulY * rs) + m_MulY * std::pow(ay, m_BoxPow) * rs * denom;//so std::abs() shouldn't be necessary.
				helper.Out.z = m_Weight * Lerp<T>(helper.In.z, std::floor(helper.In.z * denom) + scale * az, m_MulZ * rs) + m_MulZ * std::pow(az, m_BoxPow) * rs * denom;
				break;
		}
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string scatter         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string minDist         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulX            = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulY            = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulZ            = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string x0              = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string y0              = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string z0              = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string invert          = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string type            = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string boxPow          = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string internalScatter = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tconst real_t ax = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t ay = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t az = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t xmx = vIn.x - " << x0 << ";\n"
		   << "\t\tconst real_t ymy = vIn.y - " << y0 << ";\n"
		   << "\t\tconst real_t zmz = vIn.z - " << z0 << ";\n"
		   << "\t\tconst real_t r = sqrt(fma(xmx, xmx, fma(ymy, ymy, SQR(zmz))));\n"
		   << "\t\tconst real_t rc = ((" << invert << " != 0 ? max(1 - r, (real_t)(0.0)) : max(r, (real_t)(0.0))) - " << minDist << ") * " << internalScatter << ";\n"
		   << "\t\tconst real_t rs = max(rc, (real_t)(0.0));\n"
		   << "\n"
		   << "\t\treal_t sigma, phi, rad, sigmas, sigmac, phis, phic;\n"
		   << "\t\treal_t scale, denom;\n"
		   << "\n"
		   << "\t\tswitch ((int)" << type << ")\n"
		   << "\t\t{\n"
		   << "\t\t	case 0:\n"
		   << "\t\t		vOut.x = " << weight << " * fma(" << mulX << ", ax * rs, vIn.x);\n"
		   << "\t\t		vOut.y = " << weight << " * fma(" << mulY << ", ay * rs, vIn.y);\n"
		   << "\t\t		vOut.z = " << weight << " * fma(" << mulZ << ", az * rs, vIn.z);\n"
		   << "\t\t		break;\n"
		   << "\t\t	case 1:\n"
		   << "\t\t		rad = fma(" << mulX << ", ax * rs, r);\n"
		   << "\t\t		phi = fma(" << mulY << ", ay * rs, precalcAtanyx);\n"
		   << "\t\t		sigma = fma(" << mulZ << ", az * rs, asin(r == 0 ? (real_t)(0.0) : vIn.z / r));\n"
		   << "\n"
		   << "\t\t		sigmas = sin(sigma) * " << weight << ";\n"
		   << "\t\t		sigmac = cos(sigma) * " << weight << ";\n"
		   << "\t\t		phis = sin(phi);\n"
		   << "\t\t		phic = cos(phi);\n"
		   << "\n"
		   << "\t\t		vOut.x = rad * sigmac * phic;\n"
		   << "\t\t		vOut.y = rad * sigmac * phis;\n"
		   << "\t\t		vOut.z = rad * sigmas;\n"
		   << "\t\t		break;\n"
		   << "\t\t	case 2:\n"
		   << "\t\t		scale = clamp(rs, (real_t)(0.0), (real_t)(0.9)) + (real_t)(0.1);\n"
		   << "\t\t		denom = 1 / scale;\n"
		   << "\t\t		vOut.x = fma(" << weight << ", Lerp(vIn.x, fma(scale, ax, floor(vIn.x * denom)), " << mulX << " * rs), " << mulX << " * pow(ax, " << boxPow << ") * rs * denom);\n"
		   << "\t\t		vOut.y = fma(" << weight << ", Lerp(vIn.y, fma(scale, ay, floor(vIn.y * denom)), " << mulY << " * rs), " << mulY << " * pow(ay, " << boxPow << ") * rs * denom);\n"
		   << "\t\t		vOut.z = fma(" << weight << ", Lerp(vIn.z, fma(scale, az, floor(vIn.z * denom)), " << mulZ << " * rs), " << mulZ << " * pow(az, " << boxPow << ") * rs * denom);\n"
		   << "\t\t		break;\n"
		   << "\t\t}\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Lerp" };
	}

	virtual void Precalc() override
	{
		m_InternalScatter = T(0.04) * m_Scatter;
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Scatter,               prefix + "falloff_scatter", 1, eParamType::REAL, EPS, TMAX));
		m_Params.push_back(ParamWithName<T>(&m_MinDist,               prefix + "falloff_mindist", T(0.5), eParamType::REAL, 0, TMAX));
		m_Params.push_back(ParamWithName<T>(&m_MulX,                  prefix + "falloff_mul_x", 1, eParamType::REAL, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_MulY,                  prefix + "falloff_mul_y", 1, eParamType::REAL, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_MulZ,                  prefix + "falloff_mul_z", 0, eParamType::REAL, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_X0,                    prefix + "falloff_x0"));
		m_Params.push_back(ParamWithName<T>(&m_Y0,                    prefix + "falloff_y0"));
		m_Params.push_back(ParamWithName<T>(&m_Z0,                    prefix + "falloff_z0"));
		m_Params.push_back(ParamWithName<T>(&m_Invert,                prefix + "falloff_invert", 0, eParamType::INTEGER, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_Type,                  prefix + "falloff_type", 0, eParamType::INTEGER, 0, 2));
		m_Params.push_back(ParamWithName<T>(&m_BoxPow,                prefix + "falloff_boxpow", 2, eParamType::INTEGER, 2, 32));//Original defaulted this to 0 which directly contradicts the specified range of 2-32.
		m_Params.push_back(ParamWithName<T>(true, &m_InternalScatter, prefix + "falloff_internal_scatter"));
	}

private:
	T m_Scatter;
	T m_MinDist;
	T m_MulX;
	T m_MulY;
	T m_MulZ;
	T m_X0;
	T m_Y0;
	T m_Z0;
	T m_Invert;
	T m_Type;
	T m_BoxPow;
	T m_InternalScatter;
};

/// <summary>
/// falloff2.
/// </summary>
template <typename T>
class Falloff2Variation : public ParametricVariation<T>
{
public:
	Falloff2Variation(T weight = 1.0) : ParametricVariation<T>("falloff2", eVariationId::VAR_FALLOFF2, weight, true, false, false, false, true)
	{
		Init();
	}

	PARVARCOPY(Falloff2Variation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		const v4T random(rand.Frand<T>(T(-0.5), T(0.5)), rand.Frand<T>(T(-0.5), T(0.5)), rand.Frand<T>(T(-0.5), T(0.5)), rand.Frand<T>(T(-0.5), T(0.5)));
		const T distA = std::sqrt(Sqr(helper.In.x - m_X0) + Sqr(helper.In.y - m_Y0) + Sqr(helper.In.z - m_Z0));
		const T distB = m_Invert != 0 ? std::max<T>(1 - distA, 0) : std::max<T>(distA, 0);//Original called a macro named min, which internally performed max.
		const T dist = std::max<T>((distB - m_MinDist) * m_RMax, 0);

		switch (int(m_Type))
		{
			case 0://Linear.
			{
				helper.Out.x = (helper.In.x + m_MulX * random.x * dist) * m_Weight;
				helper.Out.y = (helper.In.y + m_MulY * random.y * dist) * m_Weight;
				helper.Out.z = (helper.In.z + m_MulZ * random.z * dist) * m_Weight;
			}
			break;

			case 1://Radial.
			{
				if (helper.In.x == 0 && helper.In.y == 0 && helper.In.z == 0)
				{
					helper.Out.x = helper.In.x * m_Weight;
					helper.Out.y = helper.In.y * m_Weight;
					helper.Out.z = helper.In.z * m_Weight;
					return;
				}
				else
				{
					const T rIn = Zeps(std::sqrt(helper.m_PrecalcSumSquares + SQR(helper.In.z)));
					const T r = rIn + m_MulX * random.x * dist;
					const T phi = helper.m_PrecalcAtanyx + m_MulY * random.y * dist;
					const T sigma = std::asin(helper.In.z / rIn) + m_MulZ * random.z * dist;
					const T sigmas = std::sin(sigma) * m_Weight;
					const T sigmac = std::cos(sigma) * m_Weight;
					const T phis = std::sin(phi);
					const T phic = std::cos(phi);
					helper.Out.x = r * sigmac * phic;
					helper.Out.y = r * sigmac * phis;
					helper.Out.z = r * sigmas;
				}
			}
			break;

			case 2://Gaussian.
			default:
			{
				const T sigma = dist * random.y * M_2PI;
				const T phi = dist * random.z * T(M_PI);
				const T rad = dist * random.x;
				const T sigmas = std::sin(sigma);
				const T sigmac = std::cos(sigma);
				const T phis = std::sin(phi);
				const T phic = std::cos(phi);
				helper.Out.x = (helper.In.x + m_MulX * rad * sigmac * phic) * m_Weight;
				helper.Out.y = (helper.In.y + m_MulY * rad * sigmac * phis) * m_Weight;
				helper.Out.z = (helper.In.z + m_MulZ * rad * sigmas) * m_Weight;
			}
			break;
		}

		outPoint.m_ColorX = std::abs(fmod(outPoint.m_ColorX + m_MulC * random.w * dist, T(1)));
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		int i = 0;
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string scatter    = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string minDist    = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulX       = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulY       = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulZ       = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulC       = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string x0         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string y0         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string z0         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string invert     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string type       = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string rMax       = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tconst real_t randx = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t randy = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t randz = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t randc = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t xmx = vIn.x - " << x0 << ";\n"
		   << "\t\tconst real_t ymy = vIn.y - " << y0 << ";\n"
		   << "\t\tconst real_t zmz = vIn.z - " << z0 << ";\n"
		   << "\t\tconst real_t distA = sqrt(fma(xmx, xmx, fma(ymy, ymy, SQR(zmz))));\n"
		   << "\t\tconst real_t distB = " << invert << " != 0 ? max(1 - distA, (real_t)(0.0)) : max(distA, (real_t)(0.0));\n"
		   << "\t\tconst real_t dist = max((distB - " << minDist << ") * " << rMax << ", (real_t)(0.0));\n"
		   << "\n"
		   << "\t\tswitch ((int)" << type << ")\n"
		   << "\t\t{\n"
		   << "\t\t   case 0:\n"
		   << "\t\t	   vOut.x = fma(" << mulX << ", randx * dist, vIn.x) * " << weight << ";\n"
		   << "\t\t	   vOut.y = fma(" << mulY << ", randy * dist, vIn.y) * " << weight << ";\n"
		   << "\t\t	   vOut.z = fma(" << mulZ << ", randz * dist, vIn.z) * " << weight << ";\n"
		   << "\t\t	   outPoint->m_ColorX = fabs(fmod(fma(" << mulC << ", randc * dist, outPoint->m_ColorX), (real_t)(1.0)));\n"
		   << "\t\t	   break;\n"
		   << "\t\t   case 1:\n"
		   << "\t\t	   if (vIn.x == 0 && vIn.y == 0 && vIn.z == 0)\n"
		   << "\t\t	   {\n"
		   << "\t\t		   vOut.x = vIn.x * " << weight << ";\n"
		   << "\t\t		   vOut.y = vIn.y * " << weight << ";\n"
		   << "\t\t		   vOut.z = vIn.z * " << weight << ";\n"
		   << "\t\t	   }\n"
		   << "\t\t	   else\n"
		   << "\t\t	   {\n"
		   << "\t\t		   real_t rIn = Zeps(sqrt(fma(vIn.z, vIn.z, precalcSumSquares)));\n"
		   << "\t\t		   real_t r = fma(" << mulX << ", randx * dist, rIn);\n"
		   << "\t\t		   real_t phi = fma(" << mulY << ", randy * dist, precalcAtanyx);\n"
		   << "\t\t		   real_t sigma = fma(" << mulZ << ", randz * dist, asin(vIn.z / rIn));\n"
		   << "\t\t		   real_t sigmas = sin(sigma) * " << weight << ";\n"
		   << "\t\t		   real_t sigmac = cos(sigma) * " << weight << ";\n"
		   << "\t\t		   real_t phis = sin(phi);\n"
		   << "\t\t		   real_t phic = cos(phi);\n"
		   << "\n"
		   << "\t\t		   vOut.x = r * sigmac * phic;\n"
		   << "\t\t		   vOut.y = r * sigmac * phis;\n"
		   << "\t\t		   vOut.z = r * sigmas;\n"
		   << "\t\t		   outPoint->m_ColorX = fabs(fmod(fma(" << mulC << ", randc * dist, outPoint->m_ColorX), (real_t)(1.0)));\n"
		   << "\t\t	   }\n"
		   << "\t\t	   break;\n"
		   << "\t\t    case 2:\n"
		   << "\t\t	   {\n"
		   << "\t\t	    real_t sigma = dist * randy * M_2PI;\n"
		   << "\t\t	    real_t phi = dist * randz * MPI;\n"
		   << "\t\t	    real_t rad = dist * randx;\n"
		   << "\t\t	    real_t sigmas = sin(sigma);\n"
		   << "\t\t	    real_t sigmac = cos(sigma);\n"
		   << "\t\t	    real_t phis = sin(phi);\n"
		   << "\t\t	    real_t phic = cos(phi);\n"
		   << "\n"
		   << "\t\t	    vOut.x = fma(" << mulX << " * rad, sigmac * phic, vIn.x) * " << weight << ";\n"
		   << "\t\t	    vOut.y = fma(" << mulY << " * rad, sigmac * phis, vIn.y) * " << weight << ";\n"
		   << "\t\t	    vOut.z = fma(" << mulZ << " * rad, sigmas, vIn.z) * " << weight << ";\n"
		   << "\t\t	    outPoint->m_ColorX = fabs(fmod(fma(" << mulC << ", randc * dist, outPoint->m_ColorX), (real_t)(1.0)));\n"
		   << "\t\t	    break;\n"
		   << "\t\t	   }\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Zeps" };
	}

	virtual void Precalc() override
	{
		m_RMax = T(0.04) * m_Scatter;
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Scatter,          prefix + "falloff2_scatter", 1));
		m_Params.push_back(ParamWithName<T>(&m_MinDist,          prefix + "falloff2_mindist", T(0.5)));
		m_Params.push_back(ParamWithName<T>(&m_MulX,             prefix + "falloff2_mul_x", 1));
		m_Params.push_back(ParamWithName<T>(&m_MulY,             prefix + "falloff2_mul_y", 1));
		m_Params.push_back(ParamWithName<T>(&m_MulZ,             prefix + "falloff2_mul_z", 0));
		m_Params.push_back(ParamWithName<T>(&m_MulC,             prefix + "falloff2_mul_c", 0));
		m_Params.push_back(ParamWithName<T>(&m_X0,               prefix + "falloff2_x0"));
		m_Params.push_back(ParamWithName<T>(&m_Y0,               prefix + "falloff2_y0"));
		m_Params.push_back(ParamWithName<T>(&m_Z0,               prefix + "falloff2_z0"));
		m_Params.push_back(ParamWithName<T>(&m_Invert,           prefix + "falloff2_invert", 0, eParamType::INTEGER, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_Type,             prefix + "falloff2_type", 0, eParamType::INTEGER, 0, 2));
		m_Params.push_back(ParamWithName<T>(true, &m_RMax,       prefix + "falloff2_rmax"));//Precalc.
	}

private:
	T m_Scatter;
	T m_MinDist;
	T m_MulX;
	T m_MulY;
	T m_MulZ;
	T m_MulC;
	T m_X0;
	T m_Y0;
	T m_Z0;
	T m_Invert;
	T m_Type;
	T m_RMax;//Precalc.
};

/// <summary>
/// falloff3.
/// </summary>
template <typename T>
class Falloff3Variation : public ParametricVariation<T>
{
public:
	Falloff3Variation(T weight = 1.0) : ParametricVariation<T>("falloff3", eVariationId::VAR_FALLOFF3, weight, true, false, false, false, true)
	{
		Init();
	}

	PARVARCOPY(Falloff3Variation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		const v4T random(rand.Frand<T>(T(-0.5), T(0.5)), rand.Frand<T>(T(-0.5), T(0.5)), rand.Frand<T>(T(-0.5), T(0.5)), rand.Frand<T>(T(-0.5), T(0.5)));
		T radius;

		if (m_BlurShape == 0)//Circle.
			radius = std::sqrt(Sqr(helper.In.x - m_CenterX) + Sqr(helper.In.y - m_CenterY) + Sqr(helper.In.z - m_CenterZ));
		else//Square.
			radius = std::max(std::abs(helper.In.x - m_CenterX), std::max(std::abs(helper.In.y - m_CenterY), (std::abs(helper.In.z - m_CenterZ))));//Original called a macro named min, which internally performed max.

		const T dist = std::max<T>(((m_InvertDistance != 0 ? std::max<T>(1 - radius, 0) : std::max<T>(radius, 0)) - m_MinDistance) * m_RMax, 0);

		switch (int(m_BlurType))
		{
			case 0://Gaussian.
			{
				const T sigma = dist * random.y * M_2PI;
				const T phi = dist * random.z * T(M_PI);
				const T rad = dist * random.x;
				const T sigmas = std::sin(sigma);
				const T sigmac = std::cos(sigma);
				const T phis = std::sin(phi);
				const T phic = std::cos(phi);
				helper.Out.x = (helper.In.x + m_MulX * rad * sigmac * phic) * m_Weight;
				helper.Out.y = (helper.In.y + m_MulY * rad * sigmac * phis) * m_Weight;
				helper.Out.z = (helper.In.z + m_MulZ * rad * sigmas) * m_Weight;
				outPoint.m_ColorX = std::abs(fmod(outPoint.m_ColorX + m_MulC * random.w * dist, T(1)));
			}
			break;

			case 1://Radial.
				if (helper.In.x == 0 && helper.In.y == 0 && helper.In.z == 0)
				{
					helper.Out.x = helper.In.x * m_Weight;
					helper.Out.y = helper.In.y * m_Weight;
					helper.Out.z = helper.In.z * m_Weight;
					return;
				}
				else
				{
					const T rIn = std::sqrt(helper.m_PrecalcSumSquares + SQR(helper.In.z));
					const T r = rIn + m_MulX * random.x * dist;
					const T phi = helper.m_PrecalcAtanyx + m_MulY * random.y * dist;
					const T sigma = std::asin(helper.In.z / rIn) + m_MulZ * random.z * dist;
					const T sigmas = std::sin(sigma) * m_Weight;
					const T sigmac = std::cos(sigma) * m_Weight;
					const T phis = std::sin(phi);
					const T phic = std::cos(phi);
					helper.Out.x = r * sigmac * phic;
					helper.Out.y = r * sigmac * phis;
					helper.Out.z = r * sigmas;
					outPoint.m_ColorX = std::abs(fmod(outPoint.m_ColorX + m_MulC * random.w * dist, T(1)));
				}

				break;

			case 2://Log.
			default:
			{
				const T coeff = m_RMax <= EPS ? dist : dist + m_Alpha * (VarFuncs<T>::LogMap(dist) - dist);
				helper.Out.x = (helper.In.x + VarFuncs<T>::LogMap(m_MulX) * VarFuncs<T>::LogScale(random.x) * coeff) * m_Weight;
				helper.Out.y = (helper.In.y + VarFuncs<T>::LogMap(m_MulY) * VarFuncs<T>::LogScale(random.y) * coeff) * m_Weight;
				helper.Out.z = (helper.In.z + VarFuncs<T>::LogMap(m_MulZ) * VarFuncs<T>::LogScale(random.z) * coeff) * m_Weight;
				outPoint.m_ColorX = std::abs(fmod(outPoint.m_ColorX + VarFuncs<T>::LogMap(m_MulC) * VarFuncs<T>::LogScale(random.w) * coeff, T(1)));
			}
			break;
		}
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		int i = 0;
		ss2 << "_" << XformIndexInEmber() << "]";
		string index = ss2.str();
		string weight = WeightDefineString();
		string blurType     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string blurShape    = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string blurStrength = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string minDist      = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string invertDist   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulX         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulY         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulZ         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string mulC         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string centerX      = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string centerY      = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string centerZ      = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string alpha        = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string rMax         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tconst real_t randx = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t randy = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t randz = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t randc = MwcNext0505(mwc);\n"
		   << "\t\tconst real_t xmx = vIn.x - " << centerX << ";\n"
		   << "\t\tconst real_t ymy = vIn.y - " << centerY << ";\n"
		   << "\t\tconst real_t zmz = vIn.z - " << centerZ << ";\n"
		   << "\t\treal_t radius;\n"
		   << "\n"
		   << "\t\tif (" << blurShape << " == 0)\n"
		   << "\t\t		radius = sqrt(fma(xmx, xmx, fma(ymy, ymy, SQR(zmz))));\n"
		   << "\t\telse\n"
		   << "\t\t		radius = max(fabs(xmx), max(fabs(ymy), (fabs(zmz))));\n"
		   << "\n"
		   << "\t\tconst real_t dist = max(((" << invertDist << " != 0 ? max(1 - radius, (real_t)(0.0)) : max(radius, (real_t)(0.0))) - " << minDist << ") * " << rMax << ", (real_t)(0.0));\n"
		   << "\n"
		   << "\t\tswitch ((int)" << blurType << ")\n"
		   << "\t\t{\n"
		   << "\t\tcase 0:\n"
		   << "\t\t	{\n"
		   << "\t\t		real_t sigma = dist * randy * M_2PI;\n"
		   << "\t\t		real_t phi = dist * randz * MPI;\n"
		   << "\t\t		real_t rad = dist * randx;\n"
		   << "\t\t		real_t sigmas = sin(sigma);\n"
		   << "\t\t		real_t sigmac = cos(sigma);\n"
		   << "\t\t		real_t phis = sin(phi);\n"
		   << "\t\t		real_t phic = cos(phi);\n"
		   << "\n"
		   << "\t\t		vOut.x = fma(" << mulX << " * rad, sigmac * phic, vIn.x) * " << weight << ";\n"
		   << "\t\t		vOut.y = fma(" << mulY << " * rad, sigmac * phis, vIn.y) * " << weight << ";\n"
		   << "\t\t		vOut.z = fma(" << mulZ << " * rad, sigmas, vIn.z) * " << weight << ";\n"
		   << "\t\t		outPoint->m_ColorX = fabs(fmod(fma(" << mulC << ", randc * dist, outPoint->m_ColorX), (real_t)(1.0)));\n"
		   << "\t\t	}\n"
		   << "\t\t	break;\n"
		   << "\t\tcase 1:\n"
		   << "\t\t	if (vIn.x == 0 && vIn.y == 0 && vIn.z == 0)\n"
		   << "\t\t	{\n"
		   << "\t\t		vOut.x = vIn.x * " << weight << ";\n"
		   << "\t\t		vOut.y = vIn.y * " << weight << ";\n"
		   << "\t\t		vOut.z = vIn.z * " << weight << ";\n"
		   << "\t\t	}\n"
		   << "\t\t	else\n"
		   << "\t\t	{\n"
		   << "\t\t		real_t rIn = Zeps(sqrt(fma(vIn.z, vIn.z, precalcSumSquares)));\n"
		   << "\t\t		real_t r = fma(" << mulX << ", randx * dist, rIn);\n"
		   << "\t\t		real_t phi = fma(" << mulY << ", randy * dist, precalcAtanyx);\n"
		   << "\t\t		real_t sigma = fma(" << mulZ << ", randz * dist, asin(vIn.z / rIn));\n"
		   << "\t\t		real_t sigmas = sin(sigma) * " << weight << ";\n"
		   << "\t\t		real_t sigmac = cos(sigma) * " << weight << ";\n"
		   << "\t\t		real_t phis = sin(phi);\n"
		   << "\t\t		real_t phic = cos(phi);\n"
		   << "\n"
		   << "\t\t		vOut.x = r * sigmac * phic;\n"
		   << "\t\t		vOut.y = r * sigmac * phis;\n"
		   << "\t\t		vOut.z = r * sigmas;\n"
		   << "\t\t		outPoint->m_ColorX = fabs(fmod(fma(" << mulC << ", randc * dist, outPoint->m_ColorX), (real_t)(1.0)));\n"
		   << "\t\t	}\n"
		   << "\t\t	break;\n"
		   << "\t\tcase 2:\n"
		   << "\t\t	{\n"
		   << "\t\t		real_t coeff = " << rMax << " <= EPS ? dist : fma(" << alpha << ", (LogMap(dist) - dist), dist);\n"
		   << "\n"
		   << "\t\t		vOut.x = fma(LogMap(" << mulX << "), LogScale(randx) * coeff, vIn.x) * " << weight << ";\n"
		   << "\t\t		vOut.y = fma(LogMap(" << mulY << "), LogScale(randy) * coeff, vIn.y) * " << weight << ";\n"
		   << "\t\t		vOut.z = fma(LogMap(" << mulZ << "), LogScale(randz) * coeff, vIn.z) * " << weight << ";\n"
		   << "\t\t		outPoint->m_ColorX = fabs(fmod(fma(LogMap(" << mulC << "), LogScale(randc) * coeff, outPoint->m_ColorX), (real_t)(1.0)));\n"
		   << "\t\t	}\n"
		   << "\t\t	break;\n"
		   << "\t\t}\n"
		   << "\t}\n";
		return ss.str();
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "SignNz", "LogMap", "LogScale", "Zeps" };
	}

	virtual void Precalc() override
	{
		m_RMax = T(0.04) * m_BlurStrength;
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_BlurType,         prefix + "falloff3_blur_type", 0, eParamType::INTEGER, 0, 3));
		m_Params.push_back(ParamWithName<T>(&m_BlurShape,        prefix + "falloff3_blur_shape", 0, eParamType::INTEGER, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_BlurStrength,     prefix + "falloff3_blur_strength", 1, eParamType::REAL, EPS, TMAX));
		m_Params.push_back(ParamWithName<T>(&m_MinDistance,      prefix + "falloff3_min_distance", T(0.5), eParamType::REAL, 0, TMAX));
		m_Params.push_back(ParamWithName<T>(&m_InvertDistance,   prefix + "falloff3_invert_distance", 0, eParamType::INTEGER, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_MulX,             prefix + "falloff3_mul_x", 1, eParamType::REAL, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_MulY,             prefix + "falloff3_mul_y", 1, eParamType::REAL, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_MulZ,             prefix + "falloff3_mul_z", 0, eParamType::REAL, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_MulC,             prefix + "falloff3_mul_c", 0, eParamType::REAL, 0, 1));
		m_Params.push_back(ParamWithName<T>(&m_CenterX,          prefix + "falloff3_center_x"));
		m_Params.push_back(ParamWithName<T>(&m_CenterY,          prefix + "falloff3_center_y"));
		m_Params.push_back(ParamWithName<T>(&m_CenterZ,          prefix + "falloff3_center_z"));
		m_Params.push_back(ParamWithName<T>(&m_Alpha,            prefix + "falloff3_alpha"));
		m_Params.push_back(ParamWithName<T>(true, &m_RMax,       prefix + "falloff3_rmax"));//Precalc.
	}

private:
	T m_BlurType;
	T m_BlurShape;
	T m_BlurStrength;
	T m_MinDistance;
	T m_InvertDistance;
	T m_MulX;
	T m_MulY;
	T m_MulZ;
	T m_MulC;
	T m_CenterX;
	T m_CenterY;
	T m_CenterZ;
	T m_Alpha;
	T m_RMax;//Precalc.
};

/// <summary>
/// xtrb.
/// </summary>
template <typename T>
class XtrbVariation : public ParametricVariation<T>
{
public:
	XtrbVariation(T weight = 1.0) : ParametricVariation<T>("xtrb", eVariationId::VAR_XTRB, weight)
	{
		Init();
	}

	PARVARCOPY(XtrbVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		intmax_t m, n;
		T alpha, beta, offsetAl, offsetBe, offsetGa, x, y;
		//Transfer to trilinear coordinates, normalized to real distances from triangle sides.
		DirectTrilinear(helper.In.x, helper.In.y, alpha, beta);
		m = Floor<T>(alpha / m_S2a);
		offsetAl = alpha - m * m_S2a;
		n = Floor<T>(beta / m_S2b);
		offsetBe = beta - n * m_S2b;
		offsetGa = m_S2c - m_Ac * offsetAl - m_Bc * offsetBe;

		if (offsetGa > 0)
		{
			Hex(offsetAl, offsetBe, offsetGa, alpha, beta, rand);
		}
		else
		{
			offsetAl = m_S2a - offsetAl;
			offsetBe = m_S2b - offsetBe;
			offsetGa = -offsetGa;
			Hex(offsetAl, offsetBe, offsetGa, alpha, beta, rand);
			alpha = m_S2a - alpha;
			beta = m_S2b - beta;
		}

		alpha += m * m_S2a;
		beta += n * m_S2b;
		InverseTrilinear(alpha, beta, x, y, rand);
		helper.Out.x = m_Weight * x;
		helper.Out.y = m_Weight * y;
		helper.Out.z = DefaultZ(helper);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber() << "]";
		string index  = ss2.str();
		string weight = WeightDefineString();
		string power  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string radius = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string width  = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string dist   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string a      = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string b      = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string sinC   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string cosC   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string ha     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string hb     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string hc     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string ab     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string ac     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string ba     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string bc     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string ca     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string cb     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string s2a    = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string s2b    = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string s2c    = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string s2ab   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string s2ac   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string s2bc   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string width1 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string width2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string width3 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string absN   = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string cn     = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		ss << "\t{\n"
		   << "\t\tint m, n;\n"
		   << "\t\treal_t alpha, beta, offsetAl, offsetBe, offsetGa, x, y;\n"
		   << "\n"
		   << "\t\t{\n"//DirectTrilinear function extracted out here.
		   << "\t\t	alpha = vIn.y + " << radius << ";\n"
		   << "\t\t	beta = fma(vIn.x, " << sinC << ", fma(-vIn.y, " << cosC << ", " << radius << "));\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\tm = floor(alpha / " << s2a << ");\n"
		   << "\t\toffsetAl = alpha - m * " << s2a << ";\n"
		   << "\t\tn = floor(beta / " << s2b << ");\n"
		   << "\t\toffsetBe = beta - n * " << s2b << ";\n"
		   << "\t\toffsetGa = " << s2c << " + fma(-" << ac << ", offsetAl, -" << bc << " * offsetBe);\n"
		   << "\n"
		   << "\t\tif (offsetGa > 0)\n"
		   << "\t\t{\n"
		   << "\n"
		   << "\t\t	Hex(offsetAl, offsetBe, offsetGa,\n"
		   << "\t\t		" << width << ",\n"
		   << "\t\t		" << ha << ",\n"
		   << "\t\t		" << hb << ",\n"
		   << "\t\t		" << hc << ",\n"
		   << "\t\t		" << ab << ",\n"
		   << "\t\t		" << ba << ",\n"
		   << "\t\t		" << bc << ",\n"
		   << "\t\t		" << ca << ",\n"
		   << "\t\t		" << cb << ",\n"
		   << "\t\t		" << s2a << ",\n"
		   << "\t\t		" << s2b << ",\n"
		   << "\t\t		" << s2ab << ",\n"
		   << "\t\t		" << s2ac << ",\n"
		   << "\t\t		" << s2bc << ",\n"
		   << "\t\t		" << width1 << ",\n"
		   << "\t\t		" << width2 << ",\n"
		   << "\t\t		" << width3 << ",\n"
		   << "\t\t		&alpha, &beta, mwc);\n"
		   << "\n"
		   << "\t\t}\n"
		   << "\t\telse\n"
		   << "\t\t{\n"
		   << "\t\t	offsetAl = " << s2a << " - offsetAl;\n"
		   << "\t\t	offsetBe = " << s2b << " - offsetBe;\n"
		   << "\t\t	offsetGa = -offsetGa;\n"
		   << "\n"
		   << "\t\t	Hex(offsetAl, offsetBe, offsetGa,\n"
		   << "\t\t		" << width << ",\n"
		   << "\t\t		" << ha << ",\n"
		   << "\t\t		" << hb << ",\n"
		   << "\t\t		" << hc << ",\n"
		   << "\t\t		" << ab << ",\n"
		   << "\t\t		" << ba << ",\n"
		   << "\t\t		" << bc << ",\n"
		   << "\t\t		" << ca << ",\n"
		   << "\t\t		" << cb << ",\n"
		   << "\t\t		" << s2a << ",\n"
		   << "\t\t		" << s2b << ",\n"
		   << "\t\t		" << s2ab << ",\n"
		   << "\t\t		" << s2ac << ",\n"
		   << "\t\t		" << s2bc << ",\n"
		   << "\t\t		" << width1 << ",\n"
		   << "\t\t		" << width2 << ",\n"
		   << "\t\t		" << width3 << ",\n"
		   << "\t\t		&alpha, &beta, mwc);\n"
		   << "\n"
		   << "\t\t	alpha = " << s2a << " - alpha;\n"
		   << "\t\t	beta  = " << s2b << " - beta;\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\talpha += m * " << s2a << ";\n"
		   << "\t\tbeta  += n * " << s2b << ";\n"
		   << "\n"
		   << "\t\t{\n"//InverseTrilinear function extracted out here.
		   << "\t\t	real_t inx = fma(alpha - " << radius << ", " << cosC << ", beta - " << radius << ") / " << sinC << ";\n"
		   << "\t\t	real_t iny = alpha - " << radius << ";\n"
		   << "\t\t	real_t angle = fma(M_2PI, (real_t)MwcNextRange(mwc, (int)" << absN << "), atan2(iny, inx)) / " << power << ";\n"
		   << "\t\t	real_t r = " << weight << " * pow(fma(inx, inx, SQR(iny)), " << cn << ");\n"
		   << "\n"
		   << "\t\t	x = r * cos(angle);\n"
		   << "\t\t	y = r * sin(angle);\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\tvOut.x = " << weight << " * x;\n"
		   << "\t\tvOut.y = " << weight << " * y;\n"
		   << "\t\tvOut.z = " << DefaultZCl()
		   << "\t}\n";
		return ss.str();
	}

	virtual string OpenCLFuncsString() const override
	{
		return
			"\n"
			"void Hex(real_t al, real_t be, real_t ga,\n"
			"	real_t width,\n"
			"	real_t ha,\n"
			"	real_t hb,\n"
			"	real_t hc,\n"
			"	real_t ab,\n"
			"	real_t ba,\n"
			"	real_t bc,\n"
			"	real_t ca,\n"
			"	real_t cb,\n"
			"	real_t s2a,\n"
			"	real_t s2b,\n"
			"	real_t s2ab,\n"
			"	real_t s2ac,\n"
			"	real_t s2bc,\n"
			"	real_t width1,\n"
			"	real_t width2,\n"
			"	real_t width3,\n"
			"	real_t* al1, real_t* be1, uint2* mwc)\n"
			"{\n"
			"	real_t ga1, de1, r = MwcNext01(mwc);\n"
			"\n"
			"	if (be < al)\n"
			"	{\n"
			"		if (ga < be)\n"
			"		{\n"
			"			if (r >= width3)\n"
			"			{\n"
			"				de1 = width * be;\n"
			"				ga1 = width * ga;\n"
			"			}\n"
			"			else\n"
			"			{\n"
			"				ga1 = fma(width1, ga, width2 * hc * ga / be);\n"
			"				de1 = fma(width1, be, width2 * s2ab * ((real_t)(3.0) - ga / be));\n"
			"			}\n"
			"\n"
			"			*al1 = s2a + fma(-ba, de1, - ca * ga1);\n"
			"			*be1 = de1;\n"
			"		}\n"
			"		else\n"
			"		{\n"
			"			if (ga < al)\n"
			"			{\n"
			"				if (r >= width3)\n"
			"				{\n"
			"					ga1 = width * ga;\n"
			"					de1 = width * be;\n"
			"				}\n"
			"				else\n"
			"				{\n"
			"					de1 = fma(width1, be, width2 * hb * be / ga);\n"
			"					ga1 = fma(width1, ga, width2 * s2ac * ((real_t)(3.0) - be / ga));\n"
			"				}\n"
			"\n"
			"				*al1 = s2a + fma(-ba, de1, -ca * ga1);\n"
			"				*be1 = de1;\n"
			"			}\n"
			"			else\n"
			"			{\n"
			"				if (r >= width3)\n"
			"				{\n"
			"					*al1 = width * al;\n"
			"					*be1 = width * be;\n"
			"				}\n"
			"				else\n"
			"				{\n"
			"					*be1 = fma(width1, be, width2 * hb * be / al);\n"
			"					*al1 = fma(width1, al, width2 * s2ac * ((real_t)(3.0) - be / al));\n"
			"				}\n"
			"			}\n"
			"		}\n"
			"	}\n"
			"	else\n"
			"	{\n"
			"		if (ga < al)\n"
			"		{\n"
			"			if (r >= width3)\n"
			"			{\n"
			"				de1 = width * al;\n"
			"				ga1 = width * ga;\n"
			"			}\n"
			"			else\n"
			"			{\n"
			"				ga1 = fma(width1, ga, width2 * hc * ga / al);\n"
			"				de1 = fma(width1, al, width2 * s2ab * ((real_t)(3.0) - ga / al));\n"
			"			}\n"
			"\n"
			"			*be1 = s2b + fma(-ab, de1, -cb * ga1);\n"
			"			*al1 = de1;\n"
			"		}\n"
			"		else\n"
			"		{\n"
			"			if (ga < be)\n"
			"			{\n"
			"				if (r >= width3)\n"
			"				{\n"
			"					ga1 = width * ga;\n"
			"					de1 = width * al;\n"
			"				}\n"
			"				else\n"
			"				{\n"
			"					de1 = fma(width1, al, width2 * ha * al / ga);\n"
			"					ga1 = fma(width1, ga, width2 * s2bc * ((real_t)(3.0) - al / ga));\n"
			"				}\n"
			"\n"
			"				*be1 = s2b + fma(-ab, de1, -cb * ga1);\n"
			"				*al1 = de1;\n"
			"			}\n"
			"			else\n"
			"			{\n"
			"				if (r >= width3)\n"
			"				{\n"
			"					*be1 = width * be;\n"
			"					*al1 = width * al;\n"
			"				}\n"
			"				else\n"
			"				{\n"
			"					*al1 = fma(width1, al, width2 * ha * al / be);\n"
			"					*be1 = fma(width1, be, width2 * s2bc * ((real_t)(3.0) - al / be));\n"
			"				}\n"
			"			}\n"
			"		}\n"
			"	}\n"
			"}\n"
			"\n"
			;
	}

	virtual void Precalc() override
	{
		T s2, sinA2, cosA2, sinB2, cosB2, sinC2, cosC2;
		T br = T(0.047) + m_A;
		T cr = T(0.047) + m_B;
		T ar = T(M_PI) - br - cr;
		T temp = ar / 2;
		sincos(temp, &sinA2, &cosA2);
		temp = br / 2;
		sincos(temp, &sinB2, &cosB2);
		temp = cr / 2;
		sincos(temp, &sinC2, &cosC2);
		sincos(cr, &m_SinC, &m_CosC);
		T a = m_Radius * (sinC2 / cosC2 + sinB2 / cosB2);
		T b = m_Radius * (sinC2 / cosC2 + sinA2 / cosA2);
		T c = m_Radius * (sinB2 / cosB2 + sinA2 / cosA2);
		m_Width1 = 1 - m_Width;
		m_Width2 = 2 * m_Width;
		m_Width3 = 1 - m_Width * m_Width;
		s2 = m_Radius * (a + b + c);
		m_Ha = s2 / a / 6;
		m_Hb = s2 / b / 6;
		m_Hc = s2 / c / 6;
		m_Ab = a / b;// a div on b
		m_Ac = a / c;
		m_Ba = b / a;
		m_Bc = b / c;
		m_Ca = c / a;
		m_Cb = c / b;
		m_S2a = 6 * m_Ha;
		m_S2b = 6 * m_Hb;
		m_S2c = 6 * m_Hc;
		m_S2bc = s2 / (b + c) / 6;
		m_S2ab = s2 / (a + b) / 6;
		m_S2ac = s2 / (a + c) / 6;

		if (m_Power == 0)
			m_Power = 2;

		m_AbsN = T(int(std::abs(m_Power)));
		m_Cn = m_Dist / m_Power / 2;
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.push_back(ParamWithName<T>(&m_Power, prefix + "xtrb_power", 2, eParamType::INTEGER_NONZERO));
		m_Params.push_back(ParamWithName<T>(&m_Radius, prefix + "xtrb_radius", 1));
		m_Params.push_back(ParamWithName<T>(&m_Width, prefix + "xtrb_width", T(0.5)));
		m_Params.push_back(ParamWithName<T>(&m_Dist, prefix + "xtrb_dist", 1));
		m_Params.push_back(ParamWithName<T>(&m_A, prefix + "xtrb_a", 1));
		m_Params.push_back(ParamWithName<T>(&m_B, prefix + "xtrb_b", 1));
		m_Params.push_back(ParamWithName<T>(true, &m_SinC, prefix + "xtrb_sinc"));//Precalcs.
		m_Params.push_back(ParamWithName<T>(true, &m_CosC, prefix + "xtrb_cosc"));
		m_Params.push_back(ParamWithName<T>(true, &m_Ha, prefix + "xtrb_ha"));
		m_Params.push_back(ParamWithName<T>(true, &m_Hb, prefix + "xtrb_hb"));
		m_Params.push_back(ParamWithName<T>(true, &m_Hc, prefix + "xtrb_hc"));
		m_Params.push_back(ParamWithName<T>(true, &m_Ab, prefix + "xtrb_ab"));
		m_Params.push_back(ParamWithName<T>(true, &m_Ac, prefix + "xtrb_ac"));
		m_Params.push_back(ParamWithName<T>(true, &m_Ba, prefix + "xtrb_ba"));
		m_Params.push_back(ParamWithName<T>(true, &m_Bc, prefix + "xtrb_bc"));
		m_Params.push_back(ParamWithName<T>(true, &m_Ca, prefix + "xtrb_ca"));
		m_Params.push_back(ParamWithName<T>(true, &m_Cb, prefix + "xtrb_cb"));
		m_Params.push_back(ParamWithName<T>(true, &m_S2a, prefix + "xtrb_s2a"));
		m_Params.push_back(ParamWithName<T>(true, &m_S2b, prefix + "xtrb_s2b"));
		m_Params.push_back(ParamWithName<T>(true, &m_S2c, prefix + "xtrb_s2c"));
		m_Params.push_back(ParamWithName<T>(true, &m_S2ab, prefix + "xtrb_s2ab"));
		m_Params.push_back(ParamWithName<T>(true, &m_S2ac, prefix + "xtrb_s2ac"));
		m_Params.push_back(ParamWithName<T>(true, &m_S2bc, prefix + "xtrb_s2bc"));
		m_Params.push_back(ParamWithName<T>(true, &m_Width1, prefix + "xtrb_width1"));
		m_Params.push_back(ParamWithName<T>(true, &m_Width2, prefix + "xtrb_width2"));
		m_Params.push_back(ParamWithName<T>(true, &m_Width3, prefix + "xtrb_width3"));
		m_Params.push_back(ParamWithName<T>(true, &m_AbsN, prefix + "xtrb_absn"));
		m_Params.push_back(ParamWithName<T>(true, &m_Cn, prefix + "xtrb_cn"));
	}

private:
	inline void DirectTrilinear(T x, T y, T& al, T& be)
	{
		al = y + m_Radius;
		be = x * m_SinC - y * m_CosC + m_Radius;
	}

	inline void InverseTrilinear(T al, T be, T& x, T& y, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
	{
		T inx = (be - m_Radius + (al - m_Radius) * m_CosC) / m_SinC;
		T iny = al - m_Radius;
		T angle = (std::atan2(iny, inx) + M_2PI * (rand.Rand(int(m_AbsN)))) / m_Power;
		T r = m_Weight * std::pow(SQR(inx) + SQR(iny), m_Cn);
		x = r * std::cos(angle);
		y = r * std::sin(angle);
	}

	void Hex(T al, T be, T ga, T& al1, T& be1, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
	{
		T ga1, de1, r = rand.Frand01<T>();

		if (be < al)
		{
			if (ga < be)
			{
				if (r >= m_Width3)
				{
					de1 = m_Width * be;
					ga1 = m_Width * ga;
				}
				else
				{
					ga1 = m_Width1 * ga + m_Width2 * m_Hc * ga / be;
					de1 = m_Width1 * be + m_Width2 * m_S2ab * (3 - ga / be);
				}

				al1 = m_S2a - m_Ba * de1 - m_Ca * ga1;
				be1 = de1;
			}
			else
			{
				if (ga < al)
				{
					if (r >= m_Width3)
					{
						ga1 = m_Width * ga;
						de1 = m_Width * be;
					}
					else
					{
						de1 = m_Width1 * be + m_Width2 * m_Hb * be / ga;
						ga1 = m_Width1 * ga + m_Width2 * m_S2ac * (3 - be / ga);
					}

					al1 = m_S2a - m_Ba * de1 - m_Ca * ga1;
					be1 = de1;
				}
				else
				{
					if (r >= m_Width3)
					{
						al1 = m_Width * al;
						be1 = m_Width * be;
					}
					else
					{
						be1 = m_Width1 * be + m_Width2 * m_Hb * be / al;
						al1 = m_Width1 * al + m_Width2 * m_S2ac * (3 - be / al);
					}
				}
			}
		}
		else
		{
			if (ga < al)
			{
				if (r >= m_Width3)
				{
					de1 = m_Width * al;
					ga1 = m_Width * ga;
				}
				else
				{
					ga1 = m_Width1 * ga + m_Width2 * m_Hc * ga / al;
					de1 = m_Width1 * al + m_Width2 * m_S2ab * (3 - ga / al);
				}

				be1 = m_S2b - m_Ab * de1 - m_Cb * ga1;
				al1 = de1;
			}
			else
			{
				if (ga < be)
				{
					if (r >= m_Width3)
					{
						ga1 = m_Width * ga;
						de1 = m_Width * al;
					}
					else
					{
						de1 = m_Width1 * al + m_Width2 * m_Ha * al / ga;
						ga1 = m_Width1 * ga + m_Width2 * m_S2bc * (3 - al / ga);
					}

					be1 = m_S2b - m_Ab * de1 - m_Cb * ga1;
					al1 = de1;
				}
				else
				{
					if (r >= m_Width3)
					{
						be1 = m_Width * be;
						al1 = m_Width * al;
					}
					else
					{
						al1 = m_Width1 * al + m_Width2 * m_Ha * al / be;
						be1 = m_Width1 * be + m_Width2 * m_S2bc * (3 - al / be);
					}
				}
			}
		}
	}

	T m_Power;
	T m_Radius;
	T m_Width;
	T m_Dist;
	T m_A;
	T m_B;
	T m_SinC;//Precalcs.
	T m_CosC;
	T m_Ha;
	T m_Hb;
	T m_Hc;
	T m_Ab;
	T m_Ac;
	T m_Ba;
	T m_Bc;
	T m_Ca;
	T m_Cb;
	T m_S2a;
	T m_S2b;
	T m_S2c;
	T m_S2ab;
	T m_S2ac;
	T m_S2bc;
	T m_Width1;
	T m_Width2;
	T m_Width3;
	T m_AbsN;
	T m_Cn;
};

/// <summary>
/// hexaplay3D.
/// This uses state and the OpenCL version looks different and better than the CPU.
/// </summary>
template <typename T>
class Hexaplay3DVariation : public ParametricVariation<T>
{
public:
	Hexaplay3DVariation(T weight = 1.0) : ParametricVariation<T>("hexaplay3D", eVariationId::VAR_HEXAPLAY3D, weight)
	{
		Init();
	}

	PARVARCOPY(Hexaplay3DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		if (m_FCycle > 5)
		{
			m_FCycle = 0;
			m_RSwtch = T(rand.RandBit());//Chooses 6 or 3 nodes.
		}

		if (m_BCycle > 2)
		{
			m_BCycle = 0;
			m_RSwtch = T(rand.RandBit());//Chooses 6 or 3 nodes.
		}

		T tempx, tempy;
		T boost = 1;//Boost is the separation distance between the two planes.
		T sumX, sumY;

		if (m_VarType == eVariationType::VARTYPE_REG)
		{
			sumX = outPoint.m_X;
			sumY = outPoint.m_Y;
			outPoint.m_X = 0;//Only need to clear regular, pre and post will overwrite by default.
			outPoint.m_Y = 0;
		}
		else
		{
			sumX = helper.In.x;
			sumY = helper.In.y;
		}

		int posNeg = rand.RandBit() ? -1 : 1;

		//Creating Z factors relative to the planes. These will be added, whereas x and y will be assigned.
		//Original does += z *, so using z on the right side of = is intentional.
		if (m_MajPlane == 2)
			helper.Out.z = helper.In.z * T(0.5) * m_ZLift + (posNeg * m_Boost);
		else
			helper.Out.z = helper.In.z * T(0.5) * m_ZLift;

		//Work out the segments and hexagonal nodes.
		if (m_RSwtch)//Occasion to build using 60 degree segments.
		{
			int loc = int(m_FCycle);//Sequential nodes selection.
			tempx = m_Seg60[loc].x;
			tempy = m_Seg60[loc].y;
			m_FCycle++;
		}
		else//Occasion to build on 120 degree segments.
		{
			int loc = int(m_BCycle);//Sequential nodes selection.
			tempx = m_Seg120[loc].x;
			tempy = m_Seg120[loc].y;
			m_BCycle++;
		}

		helper.Out.x = ((sumX + helper.In.x) * m_HalfScale) + (m_Weight * tempx);
		helper.Out.y = ((sumY + helper.In.y) * m_HalfScale) + (m_Weight * tempy);
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber();
		string weight = WeightDefineString();
		string index = ss2.str() + "]";
		string stateIndex = ss2.str();
		string majp              = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string scale             = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string zlift             = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string majplane          = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string boost             = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string seg60xStartIndex  = ToUpper(m_Params[i].Name()) + stateIndex; i += 6;//Precalc.
		string seg60yStartIndex  = ToUpper(m_Params[i].Name()) + stateIndex; i += 6;
		string seg120xStartIndex = ToUpper(m_Params[i].Name()) + stateIndex; i += 3;
		string seg120yStartIndex = ToUpper(m_Params[i].Name()) + stateIndex; i += 3;
		string halfScale         = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string rswtch            = "varState->" + m_Params[i++].Name() + stateIndex;//State.
		string fcycle            = "varState->" + m_Params[i++].Name() + stateIndex;
		string bcycle            = "varState->" + m_Params[i++].Name() + stateIndex;
		ss << "\t{\n"
		   << "\t\tif (" << fcycle << " > 5)\n"
		   << "\t\t{\n"
		   << "\t\t	" << fcycle << " = 0;\n"
		   << "\t\t	" << rswtch << " = (real_t)(MwcNext(mwc) & 1);\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\tif (" << bcycle << " > 2)\n"
		   << "\t\t{\n"
		   << "\t\t	" << bcycle << " = 0;\n"
		   << "\t\t	" << rswtch << " = (real_t)(MwcNext(mwc) & 1);\n"
		   << "\t\t}\n"
		   << "\t\t\n"
		   << "\t\treal_t tempx, tempy;\n"
		   << "\t\treal_t sumX, sumY;\n\n";

		if (m_VarType == eVariationType::VARTYPE_REG)
		{
			ss
					<< "\t\tsumX = outPoint->m_X;\n"
					<< "\t\tsumY = outPoint->m_Y;\n"
					<< "\t\toutPoint->m_X = 0;\n"
					<< "\t\toutPoint->m_Y = 0;\n";
		}
		else
		{
			ss
					<< "\t\tsumX = vIn.x;\n"
					<< "\t\tsumY = vIn.y;\n";
		}

		ss
				<< "\n"
				<< "\t\tint posNeg = (MwcNext(mwc) & 1) ? -1 : 1;\n"
				<< "\n"
				<< "\t\tif (" << majplane << " == 2)\n"
				<< "\t\t	vOut.z = fma(vIn.z * (real_t)(0.5), " << zlift << ", (posNeg * " << boost << "));\n"
				<< "\t\telse\n"
				<< "\t\t	vOut.z = vIn.z * 0.5 * " << zlift << ";\n"
				<< "\n"
				<< "\t\tif (" << rswtch << ")\n"
				<< "\t\t{\n"
				<< "\t\t	int loc = (int)" << fcycle << ";\n"
				<< "\t\t	tempx = parVars[" << seg60xStartIndex << " + loc];\n"
				<< "\t\t	tempy = parVars[" << seg60yStartIndex << " + loc];\n"
				<< "\t\t	" << fcycle << " = " << fcycle << " + 1;\n"
				<< "\t\t}\n"
				<< "\t\telse\n"
				<< "\t\t{\n"
				<< "\t\t	int loc = (int)" << bcycle << ";\n"
				<< "\t\t	tempx = parVars[" << seg120xStartIndex << " + loc];\n"
				<< "\t\t	tempy = parVars[" << seg120yStartIndex << " + loc];\n"
				<< "\t\t	" << bcycle << " = " << bcycle << " + 1;\n"
				<< "\t\t}\n"
				<< "\n"
				<< "\t\tvOut.x = fma(sumX + vIn.x, " << halfScale << ", " << weight << " * tempx);\n"
				<< "\t\tvOut.y = fma(sumY + vIn.y, " << halfScale << ", " << weight << " * tempy);\n"
				<< "\t}\n";
		return ss.str();
	}

	virtual void InitStateVars(T* t, size_t& index) override
	{
		t[index++] = T(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRandBit());
		t[index++] = 0;
		t[index++] = 0;
	}

	virtual void Precalc() override
	{
		T hlift = std::sin(T(M_PI) / 3);
		m_RSwtch = std::trunc(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedFrand01<T>() * 3);//Chooses 6 or 3 nodes.
		m_FCycle = 0;
		m_BCycle = 0;
		T absmajp = std::abs(m_MajP);

		if (absmajp <= 1)
		{
			m_MajPlane = 1;//Want either 1 or 2.
		}
		else
		{
			m_MajPlane = 2;
			m_Boost = (absmajp - 1) * T(0.5);//Distance above and below XY plane.
		}

		m_Seg60[0].x = 1;
		m_Seg60[1].x = T(0.5);
		m_Seg60[2].x = T(-0.5);
		m_Seg60[3].x = -1;
		m_Seg60[4].x = T(-0.5);
		m_Seg60[5].x = T(0.5);
		m_Seg60[0].y = 0;
		m_Seg60[1].y = hlift;
		m_Seg60[2].y = hlift;
		m_Seg60[3].y = 0;
		m_Seg60[4].y = -hlift;
		m_Seg60[5].y = -hlift;
		m_Seg120[0].x = 1;
		m_Seg120[1].x = T(-0.5);
		m_Seg120[2].x = T(-0.5);
		m_Seg120[0].y = 0;
		m_Seg120[1].y = hlift;
		m_Seg120[2].y = -hlift;
		m_HalfScale = m_Scale * T(0.5);
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.reserve(25);
		m_Params.push_back(ParamWithName<T>(&m_MajP, prefix + "hexaplay3D_majp", 1, eParamType::REAL));
		m_Params.push_back(ParamWithName<T>(&m_Scale, prefix + "hexaplay3D_scale", T(0.25), eParamType::REAL));
		m_Params.push_back(ParamWithName<T>(&m_ZLift, prefix + "hexaplay3D_zlift", T(0.25), eParamType::REAL));
		m_Params.push_back(ParamWithName<T>(true, &m_MajPlane,   prefix + "hexaplay3D_majplane"));//Precalc.
		m_Params.push_back(ParamWithName<T>(true, &m_Boost,      prefix + "hexaplay3D_boost"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[0].x, prefix + "hexaplay3D_seg60x0"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[1].x, prefix + "hexaplay3D_seg60x1"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[2].x, prefix + "hexaplay3D_seg60x2"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[3].x, prefix + "hexaplay3D_seg60x3"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[4].x, prefix + "hexaplay3D_seg60x4"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[5].x, prefix + "hexaplay3D_seg60x5"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[0].y, prefix + "hexaplay3D_seg60y0"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[1].y, prefix + "hexaplay3D_seg60y1"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[2].y, prefix + "hexaplay3D_seg60y2"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[3].y, prefix + "hexaplay3D_seg60y3"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[4].y, prefix + "hexaplay3D_seg60y4"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[5].y, prefix + "hexaplay3D_seg60y5"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[0].x, prefix + "hexaplay3D_seg120x0"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[1].x, prefix + "hexaplay3D_seg120x1"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[2].x, prefix + "hexaplay3D_seg120x2"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[0].y, prefix + "hexaplay3D_seg120y0"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[1].y, prefix + "hexaplay3D_seg120y1"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[2].y, prefix + "hexaplay3D_seg120y2"));
		m_Params.push_back(ParamWithName<T>(true, &m_HalfScale, prefix + "hexaplay3D_halfscale"));
		m_Params.push_back(ParamWithName<T>(true, true, &m_RSwtch, prefix + "hexaplay3D_rswtch"));//State.
		m_Params.push_back(ParamWithName<T>(true, true, &m_FCycle, prefix + "hexaplay3D_fcycle"));
		m_Params.push_back(ParamWithName<T>(true, true, &m_BCycle, prefix + "hexaplay3D_bcycle"));
	}

private:
	T m_MajP;
	T m_Scale;
	T m_ZLift;
	T m_MajPlane;//Precalc.
	T m_Boost;
	v2T m_Seg60[6];
	v2T m_Seg120[3];
	T m_HalfScale;
	T m_RSwtch;//State.
	T m_FCycle;
	T m_BCycle;
};

/// <summary>
/// hexnix3D.
/// This uses state and the OpenCL version looks different and better than the CPU.
/// It takes care of doing either a sum or product of the output variables internally,
/// rather than relying on the calling code of Xform::Apply() to do it.
/// This is because different paths do different things to helper.Out.z
/// </summary>
template <typename T>
class Hexnix3DVariation : public ParametricVariation<T>
{
public:
	Hexnix3DVariation(T weight = 1.0) : ParametricVariation<T>("hexnix3D", eVariationId::VAR_HEXNIX3D, weight)
	{
		Init();
	}

	PARVARCOPY(Hexnix3DVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		if (m_FCycle > 5)
		{
			m_FCycle = 0;
			m_RSwtch = T(rand.RandBit());//Chooses 6 or 3 nodes.
		}

		if (m_BCycle > 2)
		{
			m_BCycle = 0;
			m_RSwtch = T(rand.RandBit());//Chooses 6 or 3 nodes.
		}

		T smRotxFP = 0;
		T smRotyFP = 0;
		T smRotxFT = 0;
		T smRotyFT = 0;
		T sumX, sumY, sumZ;

		if (m_VarType == eVariationType::VARTYPE_REG)
		{
			sumX = outPoint.m_X;
			sumY = outPoint.m_Y;
			sumZ = outPoint.m_Z;
			outPoint.m_X = 0;
			outPoint.m_Y = 0;//Z is optionally cleared and assigned to below.
		}
		else
		{
			sumX = helper.In.x;
			sumY = helper.In.y;
			sumZ = helper.In.z;
		}

		int posNeg = rand.RandBit() ? -1 : 1;
		T scale3;
		T tempx, tempy;

		if (m_MajPlane == 0)
		{
			helper.Out.z = m_Smooth * helper.In.z * m_Scale * m_ZLift;
		}
		else if (m_MajPlane == 1 && m_MajP < 0)
		{
			if (posNeg < 0)
				helper.Out.z = -2 * (sumZ * m_GentleZ);
		}
		else if (m_MajPlane == 2 && m_MajP < 0)
		{
			if (posNeg > 0)
			{
				helper.Out.z = (m_Smooth * (helper.In.z * m_Scale * m_ZLift + m_Boost));
			}
			else//For this case when reg, assign and zero out. For all others, sum as usual.
			{
				helper.Out.z = (sumZ - (2 * m_Smooth * sumZ)) + (m_Smooth * posNeg * (helper.In.z * m_Scale * m_ZLift + m_Boost));

				if (m_VarType == eVariationType::VARTYPE_REG)
					outPoint.m_Z = 0;
			}
		}
		else
			helper.Out.z = m_Smooth * (helper.In.z * m_Scale * m_ZLift + (posNeg * m_Boost));

		if (m_RSwtch)
		{
			auto loc = rand.Rand(6);
			tempx = m_Seg60[loc].x;
			tempy = m_Seg60[loc].y;
			scale3 = 1;
			m_FCycle++;
		}
		else
		{
			auto loc = rand.Rand(3);
			tempx = m_Seg120[loc].x;
			tempy = m_Seg120[loc].y;
			scale3 = m_3side;
			m_BCycle++;
		}

		smRotxFP = (m_Smooth * m_Scale * sumX * tempx) - (m_Smooth * m_Scale * sumY * tempy);
		smRotyFP = (m_Smooth * m_Scale * sumY * tempx) + (m_Smooth * m_Scale * sumX * tempy);
		smRotxFT = (helper.In.x * m_Smooth * m_Scale * tempx) - (helper.In.y * m_Smooth * m_Scale * tempy);
		smRotyFT = (helper.In.y * m_Smooth * m_Scale * tempx) + (helper.In.x * m_Smooth * m_Scale * tempy);
		helper.Out.x = sumX * (1 - m_Smooth) + smRotxFP + smRotxFT + m_Smooth * m_Weight * scale3 * tempx;
		helper.Out.y = sumY * (1 - m_Smooth) + smRotyFP + smRotyFT + m_Smooth * m_Weight * scale3 * tempy;
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber();
		string weight = WeightDefineString();
		string index = ss2.str() + "]";
		string stateIndex = ss2.str();
		string majp              = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string scale             = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string zlift             = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string side3             = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string smooth            = "parVars[" + ToUpper(m_Params[i++].Name()) + index;//Precalc.
		string majplane          = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string boost             = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string gentlez           = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string seg60xStartIndex  = ToUpper(m_Params[i].Name()) + stateIndex; i += 6;
		string seg60yStartIndex  = ToUpper(m_Params[i].Name()) + stateIndex; i += 6;
		string seg120xStartIndex = ToUpper(m_Params[i].Name()) + stateIndex; i += 3;
		string seg120yStartIndex = ToUpper(m_Params[i].Name()) + stateIndex; i += 3;
		string rswtch            = "varState->" + m_Params[i++].Name() + stateIndex;//State.
		string fcycle            = "varState->" + m_Params[i++].Name() + stateIndex;
		string bcycle            = "varState->" + m_Params[i++].Name() + stateIndex;
		ss << "\t{\n"
		   << "\t\tif (" << fcycle << " > 5)\n"
		   << "\t\t{\n"
		   << "\t\t	" << fcycle << " = 0;\n"
		   << "\t\t	" << rswtch << " = (real_t)(MwcNext(mwc) & 1);\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\tif (" << bcycle << " > 2)\n"
		   << "\t\t{\n"
		   << "\t\t	" << bcycle << " = 0;\n"
		   << "\t\t	" << rswtch << " = (real_t)(MwcNext(mwc) & 1);\n"
		   << "\t\t}\n"
		   << "\n"
		   << "\t\treal_t scale = " << scale << ";\n"//This is an optimal blend of memory accesses vs. caching to local variables which seems to work best.
		   << "\t\treal_t smooth = " << smooth << ";\n"
		   << "\t\treal_t smRotxFP = 0;\n"
		   << "\t\treal_t smRotyFP = 0;\n"
		   << "\t\treal_t smRotxFT = 0;\n"
		   << "\t\treal_t smRotyFT = 0;\n"
		   << "\t\treal_t sumX, sumY, sumZ;\n\n";

		if (m_VarType == eVariationType::VARTYPE_REG)
		{
			ss
					<< "\t\tsumX = outPoint->m_X;\n"
					<< "\t\tsumY = outPoint->m_Y;\n"
					<< "\t\tsumZ = outPoint->m_Z;\n"
					<< "\t\toutPoint->m_X = 0;\n"
					<< "\t\toutPoint->m_Y = 0;\n";
		}
		else
		{
			ss
					<< "\t\tsumX = vIn.x;\n"
					<< "\t\tsumY = vIn.y;\n"
					<< "\t\tsumZ = vIn.z;\n";
		}

		ss
				<< "\n"
				<< "\t\tint posNeg = (MwcNext(mwc) & 1) ? -1 : 1;\n"
				<< "\t\treal_t scale3;\n"
				<< "\t\treal_t tempx, tempy;\n"
				<< "\n"
				<< "\t\tif (" << majplane << " == 0)\n"
				<< "\t\t{\n"
				<< "\t\t	vOut.z = smooth * vIn.z * scale * " << zlift << ";\n"
				<< "\t\t}\n"
				<< "\t\telse if (" << majplane << " == 1 && " << majp << " < 0)\n"
				<< "\t\t{\n"
				<< "\t\t	if (posNeg < 0)\n"
				<< "\t\t		vOut.z = -2 * (sumZ * " << gentlez << ");\n"
				<< "\t\t}\n"
				<< "\t\telse if (" << majplane << " == 2 && " << majp << " < 0)\n"
				<< "\t\t{\n"
				<< "\t\t   if (posNeg > 0)\n"
				<< "\t\t   {\n"
				<< "\t\t	   vOut.z = (smooth * fma(vIn.z * scale, " << zlift << ", " << boost << "));\n"
				<< "\t\t   }\n"
				<< "\t\t   else\n"
				<< "\t\t   {\n"
				<< "\t\t	   vOut.z = fma(smooth * posNeg, fma(vIn.z * scale, " << zlift << ", " << boost << "), sumZ - ((real_t)(2.0) * smooth * sumZ));\n";

		if (m_VarType == eVariationType::VARTYPE_REG)
			ss << "\t\t	   outPoint->m_Z = 0;\n";

		ss
				<< "\t\t   }\n"
				<< "\t\t}\n"
				<< "\t\telse\n"
				<< "\t\t{\n"
				<< "\t\t   vOut.z = smooth * fma(vIn.z * scale, " << zlift << ", (posNeg * " << boost << "));\n"
				<< "\t\t}\n"
				<< "\n"
				<< "\t\tif (" << rswtch << ")\n"
				<< "\t\t{\n"
				<< "\t\t	uint loc = MwcNextRange(mwc, 6);\n"
				<< "\t\t	tempx = parVars[" << seg60xStartIndex << " + loc];\n"
				<< "\t\t	tempy = parVars[" << seg60yStartIndex << " + loc];\n"
				<< "\t\t	scale3 = 1;\n"
				<< "\t\t	" << fcycle << " = " << fcycle << " + 1;\n"
				<< "\t\t}\n"
				<< "\t\telse\n"
				<< "\t\t{\n"
				<< "\t\t	uint loc = MwcNextRange(mwc, 3);\n"
				<< "\t\t	tempx = parVars[" << seg120xStartIndex << " + loc];\n"
				<< "\t\t	tempy = parVars[" << seg120yStartIndex << " + loc];\n"
				<< "\t\t	scale3 = " << side3 << ";\n"
				<< "\t\t	" << bcycle << " = " << bcycle << " + 1;\n"
				<< "\t\t}\n"
				<< "\n"
				<< "\t\tsmRotxFP = fma(smooth * scale, sumX * tempx, -(smooth * scale * sumY * tempy));\n"
				<< "\t\tsmRotyFP = fma(smooth * scale, sumY * tempx, (smooth * scale * sumX * tempy));\n"
				<< "\t\tsmRotxFT = fma(vIn.x * smooth, scale * tempx, -(vIn.y * smooth * scale * tempy));\n"
				<< "\t\tsmRotyFT = fma(vIn.y * smooth, scale * tempx, (vIn.x * smooth * scale * tempy));\n"
				<< "\t\tvOut.x = fma(sumX, (1 - smooth), fma(smooth * " << weight << ", scale3 * tempx, smRotxFP + smRotxFT));\n"
				<< "\t\tvOut.y = fma(sumY, (1 - smooth), fma(smooth * " << weight << ", scale3 * tempy, smRotyFP + smRotyFT));\n"
				<< "\t}\n";
		return ss.str();
	}

	virtual void InitStateVars(T* t, size_t& index) override
	{
		t[index++] = T(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRandBit());
		t[index++] = 0;
		t[index++] = 0;
	}

	virtual void Precalc() override
	{
		T hlift = std::sin(T(M_PI) / 3);
		m_RSwtch = T(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRandBit());// QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand(4);// //std::trunc(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedFrand01<T>() * 3);//Chooses 6 or 3 nodes.
		m_FCycle = 0;
		m_BCycle = 0;
		auto absmajp = std::abs(m_MajP);

		if (absmajp <= 1)
		{
			m_MajPlane = 0;
			m_Boost = 0;
		}
		else if (absmajp > 1 && absmajp < 2)
		{
			m_MajPlane = 1;
			m_Boost = 0;
		}
		else
		{
			m_MajPlane = 2;
			m_Boost = (absmajp - 2) * T(0.5);
		}

		if (m_MajPlane == 1 && m_MajP < 0)
		{
			if (m_MajP < -1 && m_MajP >= -2)
				m_GentleZ = absmajp - 1;
			else
				m_GentleZ = 1;
		}
		else
			m_GentleZ = 0;

		if (std::abs(m_Weight) <= T(0.5))
			m_Smooth = m_Weight * 2;
		else
			m_Smooth = 1;

		m_Seg60[0].x = 1;
		m_Seg60[1].x = T(0.5);
		m_Seg60[2].x = T(-0.5);
		m_Seg60[3].x = -1;
		m_Seg60[4].x = T(-0.5);
		m_Seg60[5].x = T(0.5);
		m_Seg60[0].y = 0;
		m_Seg60[1].y = -hlift;
		m_Seg60[2].y = -hlift;
		m_Seg60[3].y = 0;
		m_Seg60[4].y = hlift;
		m_Seg60[5].y = hlift;
		m_Seg120[0].x = 0;
		m_Seg120[1].x = std::cos(7 * T(M_PI) / 6);
		m_Seg120[2].x = std::cos(11 * T(M_PI) / 6);
		m_Seg120[0].y = -1;
		m_Seg120[1].y = T(0.5);
		m_Seg120[2].y = T(0.5);
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.reserve(25);
		m_Params.push_back(ParamWithName<T>(&m_MajP, prefix + "hexnix3D_majp", 1, eParamType::REAL));
		m_Params.push_back(ParamWithName<T>(&m_Scale, prefix + "hexnix3D_scale", T(0.25), eParamType::REAL));
		m_Params.push_back(ParamWithName<T>(&m_ZLift, prefix + "hexnix3D_zlift"));
		m_Params.push_back(ParamWithName<T>(&m_3side, prefix + "hexnix3D_3side", T(0.667), eParamType::REAL));
		m_Params.push_back(ParamWithName<T>(true, &m_Smooth,     prefix + "hexnix3D_smooth"));//Precalc.
		m_Params.push_back(ParamWithName<T>(true, &m_MajPlane,   prefix + "hexnix3D_majplane"));
		m_Params.push_back(ParamWithName<T>(true, &m_Boost,      prefix + "hexnix3D_boost"));
		m_Params.push_back(ParamWithName<T>(true, &m_GentleZ,    prefix + "hexnix3D_gentlez"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[0].x, prefix + "hexnix3D_seg60x0"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[1].x, prefix + "hexnix3D_seg60x1"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[2].x, prefix + "hexnix3D_seg60x2"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[3].x, prefix + "hexnix3D_seg60x3"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[4].x, prefix + "hexnix3D_seg60x4"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[5].x, prefix + "hexnix3D_seg60x5"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[0].y, prefix + "hexnix3D_seg60y0"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[1].y, prefix + "hexnix3D_seg60y1"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[2].y, prefix + "hexnix3D_seg60y2"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[3].y, prefix + "hexnix3D_seg60y3"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[4].y, prefix + "hexnix3D_seg60y4"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg60[5].y, prefix + "hexnix3D_seg60y5"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[0].x, prefix + "hexnix3D_seg120x0"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[1].x, prefix + "hexnix3D_seg120x1"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[2].x, prefix + "hexnix3D_seg120x2"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[0].y, prefix + "hexnix3D_seg120y0"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[1].y, prefix + "hexnix3D_seg120y1"));
		m_Params.push_back(ParamWithName<T>(true, &m_Seg120[2].y, prefix + "hexnix3D_seg120y2"));
		m_Params.push_back(ParamWithName<T>(true, true, &m_RSwtch, prefix + "hexnix3D_rswtch"));//State.
		m_Params.push_back(ParamWithName<T>(true, true, &m_FCycle, prefix + "hexnix3D_fcycle"));
		m_Params.push_back(ParamWithName<T>(true, true, &m_BCycle, prefix + "hexnix3D_bcycle"));
	}

private:
	T m_MajP;
	T m_Scale;
	T m_ZLift;
	T m_3side;
	T m_Smooth;//Precalc.
	T m_MajPlane;
	T m_Boost;
	T m_GentleZ;
	v2T m_Seg60[6];
	v2T m_Seg120[3];
	T m_RSwtch;//State.
	T m_FCycle;
	T m_BCycle;
};

/// <summary>
/// hexcrop.
/// </summary>
template <typename T>
class HexcropVariation : public ParametricVariation<T>
{
public:
	HexcropVariation(T weight = 1.0) : ParametricVariation<T>("hexcrop", eVariationId::VAR_HEXCROP, weight)
	{
		Init();
	}

	PARVARCOPY(HexcropVariation)

	virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
	{
		v2T i;
		int c = 0, n = -1, j = 5;
		i.x = helper.In.x + m_CenterX;
		i.y = helper.In.y + m_CenterY;

		while (++n < 6)
		{
			if ((m_P[n].y <= i.y && i.y < m_P[j].y) || (m_P[j].y <= i.y && i.y < m_P[n].y))
				if (i.x < (m_P[j].x - m_P[n].x) * (i.y - m_P[n].y) / Zeps(m_P[j].y - m_P[n].y) + m_P[n].x)
					c ^= 1;

			j = n;
		}

		if (m_VarType == eVariationType::VARTYPE_REG)
		{
			helper.Out.x = c != 0 ? outPoint.m_X + i.x * m_Weight : m_Dropoff;
			helper.Out.y = c != 0 ? outPoint.m_Y + i.y * m_Weight : m_Dropoff;
			outPoint.m_X = 0;
			outPoint.m_Y = 0;
		}
		else
		{
			helper.Out.x = c != 0 ? i.x * m_Weight : m_Dropoff;
			helper.Out.y = c != 0 ? i.y * m_Weight : m_Dropoff;
		}

		helper.Out.z = m_Weight * helper.In.z;
	}

	virtual string OpenCLString() const override
	{
		ostringstream ss, ss2;
		intmax_t i = 0, varIndex = IndexInXform();
		ss2 << "_" << XformIndexInEmber();
		string weight = WeightDefineString();
		string index = ss2.str() + "]";
		string stateIndex = ss2.str();
		string scalex = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string scaley = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string centerx = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string centery = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string dropoff = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
		string pxStartIndex = ToUpper(m_Params[i].Name()) + stateIndex; i += 6;//Precalc.
		string pyStartIndex = ToUpper(m_Params[i].Name()) + stateIndex;
		ss << "\t{\n"
		   << "\t\treal2 i;\n"
		   << "\t\tint c = 0, n = -1, j = 5;\n"
		   << "\n"
		   << "\t\ti.x = vIn.x + " << centerx << ";\n"
		   << "\t\ti.y = vIn.y + " << centery << ";\n"
		   << "\n"
		   << "\t\twhile (++n < 6)\n"
		   << "\t\t{\n"
		   << "\t\t	int xjoff = " << pxStartIndex << " + j;\n"
		   << "\t\t	int xnoff = " << pxStartIndex << " + n;\n"
		   << "\t\t	int yjoff = " << pyStartIndex << " + j;\n"
		   << "\t\t	int ynoff = " << pyStartIndex << " + n;\n"
		   << "\n"
		   << "\t\t	if ((parVars[ynoff] <= i.y && i.y < parVars[yjoff]) || (parVars[yjoff] <= i.y && i.y < parVars[ynoff]))\n"
		   << "\t\t		if (i.x < (parVars[xjoff] - parVars[xnoff]) * (i.y - parVars[ynoff]) / Zeps(parVars[yjoff] - parVars[ynoff]) + parVars[xnoff])\n"
		   << "\t\t			c ^= 1;\n"
		   << "\n"
		   << "\t\t	j = n;\n"
		   << "\t\t}\n"
		   << "\n";

		if (m_VarType == eVariationType::VARTYPE_REG)
		{
			ss
					<< "\t\tvOut.x = c != 0 ? fma(i.x, " << weight << ", outPoint->m_X) : " << dropoff << ";\n"
					<< "\t\tvOut.y = c != 0 ? fma(i.y, " << weight << ", outPoint->m_Y) : " << dropoff << ";\n"
					<< "\t\toutPoint->m_X = 0;\n"
					<< "\t\toutPoint->m_Y = 0;\n";
		}
		else
		{
			ss
					<< "\t\tvOut.x = c != 0 ? i.x * " << weight << " : " << dropoff << ";\n"
					<< "\t\tvOut.y = c != 0 ? i.y * " << weight << " : " << dropoff << ";\n";
		}

		ss
				<< "\t\tvOut.z = " << weight << " * vIn.z;\n"
				<< "\t}\n";
		return ss.str();
	}

	virtual void Precalc() override
	{
		m_P[0].x = T(-0.5000000000000000000000000000000) * m_ScaleX;
		m_P[0].y = T(1.0606601717798212866012665431573)  * m_ScaleY;
		m_P[1].x = T(0.5000000000000000000000000000000)  * m_ScaleX;
		m_P[1].y = T(1.0606601717798212866012665431573)  * m_ScaleY;
		m_P[2].x = T(1.4142135623730950488016887242097)  * m_ScaleX;
		m_P[2].y = T(0.0000000000000000000000000000000)  * m_ScaleY;
		m_P[3].x = T(0.5000000000000000000000000000000)  * m_ScaleX;
		m_P[3].y = T(-1.0606601717798212866012665431573) * m_ScaleY;
		m_P[4].x = T(-0.5000000000000000000000000000000) * m_ScaleX;
		m_P[4].y = T(-1.0606601717798212866012665431573) * m_ScaleY;
		m_P[5].x = T(-1.4142135623730950488016887242097) * m_ScaleX;
		m_P[5].y = T(0.0000000000000000000000000000000)  * m_ScaleY;
	}

	virtual vector<string> OpenCLGlobalFuncNames() const override
	{
		return vector<string> { "Zeps" };
	}

protected:
	void Init()
	{
		string prefix = Prefix();
		m_Params.clear();
		m_Params.reserve(17);
		m_Params.push_back(ParamWithName<T>(&m_ScaleX, prefix + "hexcrop_scale_x", 1));
		m_Params.push_back(ParamWithName<T>(&m_ScaleY, prefix + "hexcrop_scale_y", 1));
		m_Params.push_back(ParamWithName<T>(&m_CenterX, prefix + "hexcrop_center_x"));
		m_Params.push_back(ParamWithName<T>(&m_CenterY, prefix + "hexcrop_center_y"));
		m_Params.push_back(ParamWithName<T>(&m_Dropoff, prefix + "hexcrop_dropoff", T(1E10)));
		m_Params.push_back(ParamWithName<T>(true, &m_P[0].x, prefix + "hexcrop_px0"));//Precalc.
		m_Params.push_back(ParamWithName<T>(true, &m_P[1].x, prefix + "hexcrop_px1"));
		m_Params.push_back(ParamWithName<T>(true, &m_P[2].x, prefix + "hexcrop_px2"));
		m_Params.push_back(ParamWithName<T>(true, &m_P[3].x, prefix + "hexcrop_px3"));
		m_Params.push_back(ParamWithName<T>(true, &m_P[4].x, prefix + "hexcrop_px4"));
		m_Params.push_back(ParamWithName<T>(true, &m_P[5].x, prefix + "hexcrop_px5"));
		m_Params.push_back(ParamWithName<T>(true, &m_P[0].y, prefix + "hexcrop_py0"));
		m_Params.push_back(ParamWithName<T>(true, &m_P[1].y, prefix + "hexcrop_py1"));
		m_Params.push_back(ParamWithName<T>(true, &m_P[2].y, prefix + "hexcrop_py2"));
		m_Params.push_back(ParamWithName<T>(true, &m_P[3].y, prefix + "hexcrop_py3"));
		m_Params.push_back(ParamWithName<T>(true, &m_P[4].y, prefix + "hexcrop_py4"));
		m_Params.push_back(ParamWithName<T>(true, &m_P[5].y, prefix + "hexcrop_py5"));
	}

private:
	T m_ScaleX;
	T m_ScaleY;
	T m_CenterX;
	T m_CenterY;
	T m_Dropoff;
	v2T m_P[6];//Precalc.
};

MAKEPREPOSTPARVAR(Bubble2, bubble2, BUBBLE2)
MAKEPREPOSTPARVAR(CircleLinear, CircleLinear, CIRCLELINEAR)
MAKEPREPOSTPARVARASSIGN(CircleRand, CircleRand, CIRCLERAND, eVariationAssignType::ASSIGNTYPE_SUM)
MAKEPREPOSTPARVAR(CircleTrans1, CircleTrans1, CIRCLETRANS1)
MAKEPREPOSTPARVAR(Cubic3D, cubic3D, CUBIC3D)
MAKEPREPOSTPARVAR(CubicLattice3D, cubicLattice_3D, CUBIC_LATTICE3D)
MAKEPREPOSTVAR(Foci3D, foci_3D, FOCI3D)
MAKEPREPOSTPARVAR(FociP, foci_p, FOCI_P)
MAKEPREPOSTPARVAR(Ho, ho, HO)
MAKEPREPOSTPARVAR(Julia3Dq, julia3Dq, JULIA3DQ)
MAKEPREPOSTPARVARASSIGN(Line, line, LINE, eVariationAssignType::ASSIGNTYPE_SUM)
MAKEPREPOSTPARVAR(Loonie2, loonie2, LOONIE2)
MAKEPREPOSTPARVAR(Loonie3, loonie3, LOONIE3)
MAKEPREPOSTPARVAR(Loonie3D, loonie_3D, LOONIE3D)
MAKEPREPOSTPARVAR(Mcarpet, mcarpet, MCARPET)
MAKEPREPOSTPARVAR(Waves23D, waves2_3D, WAVES23D)
MAKEPREPOSTPARVARASSIGN(Pie3D, pie3D, PIE3D, eVariationAssignType::ASSIGNTYPE_SUM)
MAKEPREPOSTPARVAR(Popcorn23D, popcorn2_3D, POPCORN23D)
MAKEPREPOSTVAR(Sinusoidal3D, sinusoidal3D, SINUSOIDAL3D)
MAKEPREPOSTPARVAR(Scry3D, scry_3D, SCRY3D)
MAKEPREPOSTPARVAR(Shredlin, shredlin, SHRED_LIN)
MAKEPREPOSTPARVAR(SplitBrdr, SplitBrdr, SPLIT_BRDR)
MAKEPREPOSTVAR(Wdisc, wdisc, WDISC)
MAKEPREPOSTPARVAR(Falloff, falloff, FALLOFF)
MAKEPREPOSTPARVAR(Falloff2, falloff2, FALLOFF2)
MAKEPREPOSTPARVAR(Falloff3, falloff3, FALLOFF3)
MAKEPREPOSTPARVAR(Xtrb, xtrb, XTRB)
MAKEPREPOSTPARVAR(Hexaplay3D, hexaplay3D, HEXAPLAY3D)
MAKEPREPOSTPARVAR(Hexnix3D, hexnix3D, HEXNIX3D)
MAKEPREPOSTPARVAR(Hexcrop, hexcrop, HEXCROP)
}
