/**
 * Mandelbulber v2, a 3D fractal generator  _%}}i*<.        ____                _______
 * Copyright (C) 2020 Mandelbulber Team   _>]|=||i=i<,     / __ \___  ___ ___  / ___/ /
 *                                        \><||i|=>>%)    / /_/ / _ \/ -_) _ \/ /__/ /__
 * This file is part of Mandelbulber.     )<=i=]=|=i<>    \____/ .__/\__/_//_/\___/____/
 * The project is licensed under GPLv3,   -<>>=|><|||`        /_/
 * see also COPYING file in this folder.    ~+{i%+++
 *
 * quadratic iteration in real or imaginary scator algebra
 * @reference
 * http://www.fractalforums.com/new-theories-and-research/
 * ix-possibly-the-holy-grail-fractal-%28in-fff-lore%29
 * https://luz.izt.uam.mx/drupal/en/fractals/ix
 * @author Manuel Fernandez-Guasti
 * This formula contains aux.DE

 * This file has been autogenerated by tools/populateUiInformation.php
 * from the file "fractal_scator_power2.cpp" in the folder formula/definition
 * D O    N O T    E D I T    T H I S    F I L E !
 */

REAL4 ScatorPower2Iteration(REAL4 z, __constant sFractalCl *fractal, sExtendedAuxCl *aux)
{
	// r calc
	REAL r;
	REAL4 zz = z * z;
	if (fractal->transformCommon.functionEnabledXFalse)
	{
		r = aux->r;
	}
	else if (!fractal->transformCommon.functionEnabledYFalse)
	{
		r = native_sqrt(zz.x - zz.y - zz.z + (zz.y * zz.z) / zz.x);
	}
	else
	{ // this should be used for imaginary scators
		r = native_sqrt(zz.x + zz.y + zz.z + (zz.y * zz.z) / zz.x);
	}
	// Scator real enabled by default
	REAL4 newZ = z;
	// REAL temp1;
	if (!fractal->transformCommon.functionEnabledFalse)
	{ // scator real
		newZ.x = zz.x + zz.y + zz.z;
		newZ.y = z.x * z.y;
		newZ.z = z.x * z.z;
		newZ *= fractal->transformCommon.constantMultiplier122;
		// temp1 = length(newZ);
		newZ.x += (zz.y * zz.z) / zz.x;
		newZ.y *= (1.0f + zz.z / zz.x);
		newZ.z *= (1.0f + zz.y / zz.x);
		// r = native_sqrt(zz.x - zz.y - zz.z + (zz.y * zz.z) / zz.x);
	}
	else
	{ // scator imaginary
		newZ.x = zz.x - zz.y - zz.z;
		newZ.y = z.x * z.y;
		newZ.z = z.x * z.z;
		newZ *= fractal->transformCommon.constantMultiplier122;
		// temp1 = length(newZ);
		newZ.x += (zz.y * zz.z) / zz.x;
		newZ.y *= (1.0f - zz.z / zz.x);
		newZ.z *= (1.0f - zz.y / zz.x);
		// r = native_sqrt(zz.x + zz.y + zz.z + (zz.y * zz.z) / zz.x);
	}
	z = newZ;
	// REAL temp2 = length(newZ);
	// temp2 = temp1 / temp2;
	/* aux->DE = aux->DE * 2.0f * aux->r;
	REAL newx = z.x * z.x - z.y * z.y - z.z * z.z;
	REAL newy = 2.0f * z.x * z.y;
	REAL newz = 2.0f * z.x * z.z;
	z.x = newx;
	z.y = newy;
	z.z = newz; */

	// addCpixel
	if (fractal->transformCommon.addCpixelEnabledFalse
			&& aux->i >= fractal->transformCommon.startIterationsE
			&& aux->i < fractal->transformCommon.stopIterationsE)
	{
		REAL4 c = aux->const_c;
		REAL4 tempC = c;
		if (fractal->transformCommon.alternateEnabledFalse) // alternate
		{
			tempC = aux->c;
			switch (fractal->mandelbulbMulti.orderOfXYZ)
			{
				case multi_OrderOfXYZCl_xyz:
				default: tempC = (REAL4){tempC.x, tempC.y, tempC.z, tempC.w}; break;
				case multi_OrderOfXYZCl_xzy: tempC = (REAL4){tempC.x, tempC.z, tempC.y, tempC.w}; break;
				case multi_OrderOfXYZCl_yxz: tempC = (REAL4){tempC.y, tempC.x, tempC.z, tempC.w}; break;
				case multi_OrderOfXYZCl_yzx: tempC = (REAL4){tempC.y, tempC.z, tempC.x, tempC.w}; break;
				case multi_OrderOfXYZCl_zxy: tempC = (REAL4){tempC.z, tempC.x, tempC.y, tempC.w}; break;
				case multi_OrderOfXYZCl_zyx: tempC = (REAL4){tempC.z, tempC.y, tempC.x, tempC.w}; break;
			}
			aux->c = tempC;
		}
		else
		{
			switch (fractal->mandelbulbMulti.orderOfXYZ)
			{
				case multi_OrderOfXYZCl_xyz:
				default: tempC = (REAL4){c.x, c.y, c.z, c.w}; break;
				case multi_OrderOfXYZCl_xzy: tempC = (REAL4){c.x, c.z, c.y, c.w}; break;
				case multi_OrderOfXYZCl_yxz: tempC = (REAL4){c.y, c.x, c.z, c.w}; break;
				case multi_OrderOfXYZCl_yzx: tempC = (REAL4){c.y, c.z, c.x, c.w}; break;
				case multi_OrderOfXYZCl_zxy: tempC = (REAL4){c.z, c.x, c.y, c.w}; break;
				case multi_OrderOfXYZCl_zyx: tempC = (REAL4){c.z, c.y, c.x, c.w}; break;
			}
		}
		z += tempC * fractal->transformCommon.constantMultiplier111;
	}

	// analytic DE calc
	if (fractal->analyticDE.enabled)
	{
		if (!fractal->analyticDE.enabledFalse)
		{
			aux->DE = 2.0f * r * aux->DE * fractal->analyticDE.scale1 + fractal->analyticDE.offset1;
		}
		else
		{ // vec3
			// rd calc
			REAL rd;
			zz = z * z;
			if (fractal->transformCommon.functionEnabledXFalse)
			{
				rd = length(z);
			}
			else if (!fractal->transformCommon.functionEnabledYFalse)
			{
				rd = native_sqrt(zz.x - zz.y - zz.z + (zz.y * zz.z) / zz.x);
			}
			else
			{
				rd = native_sqrt(zz.x + zz.y + zz.z + (zz.y * zz.z) / zz.x);
			}
			REAL vecDE = fractal->transformCommon.scaleA1 * rd / r;
			aux->DE =
				max(r * 2.0f, vecDE) * aux->DE * fractal->analyticDE.scale1 + fractal->analyticDE.offset1;
		}
		aux->dist = 0.5f * r * log(r) / aux->DE;
	}
	// force bailout
	// REAL tp = fractal->transformCommon.scale1;
	// REAL tpz = fabs(z.z);
	// if (tpz < -tp && tpz > tp) tpz = (tpz > 0) ? z.z += 100000.0f : z.z -= 100000.0f;
	if (r > fractal->transformCommon.scale2) z.z += 100000.0f;
	aux->DE0 = r; // temp for testing
	return z;
}