/**
 * Mandelbulber v2, a 3D fractal generator  _%}}i*<.        ____                _______
 * Copyright (C) 2019 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%+++
 *
 * Mandeltorus by Aexion
 * @reference http://www.fractalforums.com/the-3d-mandelbulb/mandeldonuts/

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

REAL4 MandeltorusIteration(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 *= native_divide(length(z), aux->r);
	}

	REAL power1 = fractal->transformCommon.pwr8;	// Longitude power, symmetry
	REAL power2 = fractal->transformCommon.pwr8a; // Latitude power

	REAL rh = native_sqrt(mad(z.x, z.x, z.z * z.z));
	REAL rh1 = 0.0f;
	REAL rh2 = 0.0f;
	REAL phi = atan2(z.z, z.x);
	REAL phipow = phi * power1;

	REAL theta = atan2(rh, z.y);

	if (!fractal->transformCommon.functionEnabledzFalse) // mode 1
	{
		REAL thetapow = theta * power2; // mode1

		REAL px = mad(-1.5f, native_cos(phi), z.x);
		REAL pz = mad(-1.5f, native_sin(phi), z.z);

		REAL rhrad = native_sqrt(px * px + pz * pz + z.y * z.y);

		rh1 = native_powr(rhrad, power2);
		rh2 = native_powr(rhrad, power1);

		REAL sintheta = native_sin(thetapow) * rh2; // mode1

		z.x = sintheta * native_cos(phipow);
		z.z = sintheta * native_sin(phipow);
		z.y = native_cos(thetapow) * rh1; // mode 1
	}
	else // mode 2
	{
		REAL px = mad(-1.5f, native_cos(phi), z.x);
		REAL pz = mad(-1.5f, native_sin(phi), z.z);

		REAL rhrad = native_sqrt(px * px + pz * pz + z.y * z.y);

		REAL tangle = atan2(native_sqrt(mad(px, px, pz * pz)), z.y) * power2; // mode2

		rh1 = native_powr(rhrad, power2);
		rh2 = native_powr(rhrad, power1);

		REAL sintheta = (1.5f + native_cos(tangle)) * rh2; // mode2
		z.x = sintheta * native_cos(phipow);
		z.z = sintheta * native_sin(phipow);
		z.y = native_sin(tangle) * rh1; // mode 2
	}

	// DEcalc
	REAL temp = rh2 * (power1 - fractal->analyticDE.offset2);
	if (fractal->transformCommon.functionEnabledAyFalse)
		temp = min(temp, rh1 * (power2 - fractal->analyticDE.offset2));

	if (!fractal->analyticDE.enabledFalse)
	{
		aux->DE = mad(temp, aux->DE, 1.0f);
	}
	else
	{
		aux->DE = mad(temp * aux->DE, fractal->analyticDE.scale1, fractal->analyticDE.offset1);
	}
	return z;
}