/**
 * Mandelbulber v2, a 3D fractal generator  _%}}i*<.        ____                _______
 * Copyright (C) 2023 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%+++
 *
 * TransfDifsTorusTwistIteration  fragmentarium code, mdifs by knighty (jan 2012)
 * M3D difs code by darkbeam
 * and http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm

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

REAL4 TransfDIFSTorusTwistIteration(REAL4 z, __constant sFractalCl *fractal, sExtendedAuxCl *aux)
{
	if (fractal->transformCommon.functionEnabledPFalse
			&& aux->i >= fractal->transformCommon.startIterationsP
			&& aux->i < fractal->transformCommon.stopIterationsP1)
	{
		// pre abs
		if (fractal->transformCommon.functionEnabledx) z.x = fabs(z.x);
		if (fractal->transformCommon.functionEnabledy) z.y = fabs(z.y);
		if (fractal->transformCommon.functionEnabledz) z.z = fabs(z.z);

		if (fractal->transformCommon.functionEnabledCx)
		{
			REAL psi = M_PI_F / fractal->transformCommon.int8X;
			psi = fabs(fmod(atan2(z.y, z.x) + psi, 2.0f * psi) - psi);
			REAL len = native_sqrt(z.x * z.x + z.y * z.y);
			z.x = native_cos(psi) * len;
			z.y = native_sin(psi) * len;
		}

		if (fractal->transformCommon.functionEnabledCyFalse)
		{
			REAL psi = M_PI_F / fractal->transformCommon.int8Y;
			psi = fabs(fmod(atan2(z.z, z.y) + psi, 2.0f * psi) - psi);
			REAL len = native_sqrt(z.y * z.y + z.z * z.z);
			z.y = native_cos(psi) * len;
			z.z = native_sin(psi) * len;
		}

		if (fractal->transformCommon.functionEnabledCzFalse)
		{
			REAL psi = M_PI_F / fractal->transformCommon.int8Z;
			psi = fabs(fmod(atan2(z.x, z.z) + psi, 2.0f * psi) - psi);
			REAL len = native_sqrt(z.z * z.z + z.x * z.x);
			z.z = native_cos(psi) * len;
			z.x = native_sin(psi) * len;
		}

		// addition constant
		z += fractal->transformCommon.additionConstant000;

		// rotation
		if (fractal->transformCommon.rotationEnabledFalse)
		{
			z = Matrix33MulFloat4(fractal->transformCommon.rotationMatrix, z);
		}
	}
	REAL temp;
	REAL4 zc = z;
	REAL ang = atan2(zc.y, zc.x);
	REAL spiral = 0.0f;
	if (fractal->transformCommon.functionEnabledAFalse)
	{
		REAL Voff = fractal->transformCommon.offset02;
		temp = zc.z - 2.0f * Voff * ang * M_PI_2x_INV_F + Voff;
		zc.z = temp - 2.0f * Voff * floor(temp / (2.0f * Voff)) - Voff;
		spiral = z.z * fractal->transformCommon.scaleC0;
	}
	temp = zc.y;
	zc.y = native_sqrt(zc.x * zc.x + zc.y * zc.y) - fractal->transformCommon.radius1 + spiral;

	ang = atan2(temp, zc.x) * fractal->transformCommon.int6 * 0.25f;
	REAL cosA = native_cos(ang);
	REAL sinB = native_sin(ang);
	temp = zc.z;
	zc.z = zc.y * cosA + zc.z * sinB;
	zc.y = temp * cosA + zc.y * -sinB;

	REAL4 d = fabs(zc);
	REAL lenY = fractal->transformCommon.offset01;
	REAL lenZ = fractal->transformCommon.offsetp1;

	if (fractal->transformCommon.functionEnabledMFalse) // y face
		lenY += d.z * fractal->transformCommon.scale0;
	if (fractal->transformCommon.functionEnabledNFalse) // z face
		lenZ += d.z * fractal->transformCommon.scale3D000.x;
	if (fractal->transformCommon.functionEnabledOFalse) // y axis
		lenY += d.x * fractal->transformCommon.scale3D000.y;
	if (fractal->transformCommon.functionEnabledKFalse) // z axis
		lenZ += d.y * fractal->transformCommon.scale3D000.z;

	d.x = 0.0f;
	d.y = max(d.y - lenY, 0.0f);
	d.z = max(d.z - lenZ, 0.0f);
	aux->DE0 =
		length(d) / (aux->DE + fractal->analyticDE.offset0) - fractal->transformCommon.offset0005;

	// clip
	if (fractal->transformCommon.functionEnabledEFalse)
	{
		aux->DE0 = max(fractal->transformCommon.offsetE0 - aux->const_c.z, aux->DE0);
	}

	REAL colDist = aux->dist;
	if (!fractal->analyticDE.enabledFalse)
		aux->dist = aux->DE0;
	else
		aux->dist = min(aux->dist, aux->DE0);

	if (fractal->transformCommon.functionEnabledZcFalse
			&& aux->i >= fractal->transformCommon.startIterationsZc
			&& aux->i < fractal->transformCommon.stopIterationsZc)
		z = zc;

	// aux->color
	if (aux->i >= fractal->foldColor.startIterationsA && aux->i < fractal->foldColor.stopIterationsA)
	{
		REAL addColor = 0.0f;
		if (aux->dist == colDist) addColor += fractal->foldColor.difs0000.x;
		if (aux->dist != colDist) addColor += fractal->foldColor.difs0000.y;
		aux->color += addColor;
	}
	return z;
}