/**
 * 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%+++
 *
 * MsltoeToroidalMulti
 * @reference http://www.fractalforums.com/theory/toroidal-coordinates/msg9428/

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

REAL4 MsltoeToroidalMultiIteration(REAL4 z, __constant sFractalCl *fractal, sExtendedAuxCl *aux)
{
	if (fractal->transformCommon.functionEnabledFalse
			&& aux->i >= fractal->transformCommon.startIterationsD
			&& aux->i < fractal->transformCommon.stopIterationsD1) // pre-scale
	{
		z *= fractal->transformCommon.scale3D111;
		aux->DE = aux->DE * length(z) / aux->r;
	}
	// Toroidal bulb multi
	REAL th0 = fractal->bulb.betaAngleOffset;
	REAL ph0 = fractal->bulb.alphaAngleOffset;
	REAL v1, v2, v3;
	switch (fractal->sinTan2Trig.orderOfZYX)
	{
		case multi_OrderOfZYXCl_zyx:
		default:
			v1 = z.z;
			v2 = z.y;
			v3 = z.x;
			break;
		case multi_OrderOfZYXCl_zxy:
			v1 = z.z;
			v2 = z.x;
			v3 = z.y;
			break;
		case multi_OrderOfZYXCl_yzx:
			v1 = z.y;
			v2 = z.z;
			v3 = z.x;
			break;
		case multi_OrderOfZYXCl_yxz:
			v1 = z.y;
			v2 = z.x;
			v3 = z.z;
			break;
		case multi_OrderOfZYXCl_xzy:
			v1 = z.x;
			v2 = z.z;
			v3 = z.y;
			break;
		case multi_OrderOfZYXCl_xyz:
			v1 = z.x;
			v2 = z.y;
			v3 = z.z;
			break;
	}

	switch (fractal->sinTan2Trig.atan2OrAtan)
	{
		case multi_atan2OrAtanCl_atan2: ph0 += atan2(v2, v3); break;
		case multi_atan2OrAtanCl_atan: ph0 += atan(v2 / v3); break;
	}

	REAL r1 = fractal->transformCommon.minR05;
	REAL x1 = r1 * native_cos(ph0);
	REAL y1 = r1 * native_sin(ph0);

	aux->r = (z.x - x1) * (z.x - x1) + (z.y - y1) * (z.y - y1) + z.z * z.z; //+ 1e-030f

	REAL sqrT = aux->r;
	if (fractal->transformCommon.functionEnabledAy) // sqrt
	{
		sqrT = native_sqrt(aux->r);
	}

	switch (fractal->sinTan2Trig.asinOrAcos)
	{
		case multi_asinOrAcosCl_asin: th0 += asin(v1 / sqrT); break;
		case multi_asinOrAcosCl_acos: th0 += acos(v1 / sqrT); break;
	}

	th0 *= fractal->transformCommon.pwr8; // default 8
	ph0 *= fractal->bulb.power;						// default 9 gives 8 symmetry

	REAL rp = pow(aux->r, fractal->transformCommon.pwr4);

	REAL costh = native_cos(th0);
	REAL sinth = native_sin(th0);
	REAL r1RpCosTh = r1 + rp * costh;
	REAL r1RpSinTh = r1 + rp * sinth;

	if (fractal->transformCommon.functionEnabledzFalse)
	{ // cosine mode
		z.x = r1RpSinTh * native_sin(ph0);
		z.y = r1RpSinTh * native_cos(ph0);
		z.z = -rp * costh;
	}
	else
	{ // sine mode default

		z.x = r1RpCosTh * native_cos(ph0);
		z.y = r1RpCosTh * native_sin(ph0);
		z.z = -rp * sinth;
	}

	// DEcalc
	if (!fractal->analyticDE.enabledFalse)
	{
		aux->DE = rp * aux->DE * (fractal->transformCommon.pwr4 + 1.0f) + 1.0f;
	}
	else
	{
		aux->DE = rp * aux->DE * (fractal->transformCommon.pwr4 + fractal->analyticDE.offset2)
								* fractal->analyticDE.scale1
							+ fractal->analyticDE.offset1;
	}

	if (fractal->transformCommon.functionEnabledAxFalse) // spherical offset
	{
		REAL lengthTempZ = -length(z);
		// if (lengthTempZ > -1e-21f) lengthTempZ = -1e-21f;   //  z is neg.)
		z *= 1.0f + fractal->transformCommon.offset / lengthTempZ;
		z *= fractal->transformCommon.scale;
		aux->DE = aux->DE * fabs(fractal->transformCommon.scale) + 1.0f;
	}
	// then add Cpixel constant vector
	return z;
}