/*******************************************************************************
 * quatern.cpp
 *
 * This module implements Quaternion algebra julia fractals.
 *
 * This file was written by Pascal Massimino.
 * Revised and updated for POV-Ray 3.x by Tim Wegner
 *
 * ---------------------------------------------------------------------------
 * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
 * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
 *
 * POV-Ray is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * POV-Ray is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * ---------------------------------------------------------------------------
 * POV-Ray is based on the popular DKB raytracer version 2.12.
 * DKBTrace was originally written by David K. Buck.
 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
 * ---------------------------------------------------------------------------
 * $File: //depot/public/povray/3.x/source/backend/math/quatern.cpp $
 * $Revision: #1 $
 * $Change: 6069 $
 * $DateTime: 2013/11/06 11:59:40 $
 * $Author: chrisc $
 *******************************************************************************/

// frame.h must always be the first POV file included (pulls in platform config)
#include "backend/frame.h"
#include "povray.h"
#include "backend/math/vector.h"
#include "backend/shape/fractal.h"
#include "backend/math/quatern.h"
#include "backend/shape/spheres.h"

// this must be the last file included
#include "base/povdebug.h"

namespace pov
{

/*****************************************************************************
* Local preprocessor defines
******************************************************************************/

#define Deriv_z2(n1,n2,n3,n4)               \
{                                           \
	tmp = (n1)*x - (n2)*y - (n3)*z - (n4)*w;  \
	(n2) = (n1)*y + x*(n2);                   \
	(n3) = (n1)*z + x*(n3);                   \
	(n4) = (n1)*w + x*(n4);                   \
	(n1) = tmp;                               \
}

#define Deriv_z3(n1,n2,n3,n4)              \
{                                          \
	dtmp = 2.0*((n2)*y + (n3)*z + (n4)*w);   \
	dtmp2 = 6.0*x*(n1) - dtmp;               \
	(n1) = ( (n1)*x3 - x*dtmp )*3.0;         \
	(n2) = (n2)*x4 + y*dtmp2;                \
	(n3) = (n3)*x4 + z*dtmp2;                \
	(n4) = (n4)*x4 + w*dtmp2;                \
}


/*****************************************************************************
* Local typedefs
******************************************************************************/



/*****************************************************************************
* Static functions
******************************************************************************/



/*****************************************************************************
* Local variables
******************************************************************************/



/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Pascal Massimino
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   Dec 1994 : Creation.
*
******************************************************************************/

int Iteration_z3(const VECTOR point, const Fractal *Julia, DBL **IterStack)
{
	int i;
	DBL x, y, z, w;
	DBL d, x2, tmp;
	DBL Exit_Value;

	IterStack[X][0] = x = point[X];
	IterStack[Y][0] = y = point[Y];
	IterStack[Z][0] = z = point[Z];
	IterStack[W][0] = w = (Julia->SliceDist
	                - Julia->Slice[X]*x
	                - Julia->Slice[Y]*y
	                - Julia->Slice[Z]*z)/Julia->Slice[T];

	Exit_Value = Julia->Exit_Value;

	for (i = 1; i <= Julia->Num_Iterations; ++i)
	{
		d = y * y + z * z + w * w;

		x2 = x * x;

		if ((d + x2) > Exit_Value)
		{
			return (false);
		}

		tmp = 3.0 * x2 - d;

		IterStack[X][i] = x = x * (x2 - 3.0 * d) + Julia->Julia_Parm[X];
		IterStack[Y][i] = y = y * tmp + Julia->Julia_Parm[Y];
		IterStack[Z][i] = z = z * tmp + Julia->Julia_Parm[Z];
		IterStack[W][i] = w = w * tmp + Julia->Julia_Parm[T];
	}

	return (true);
}



/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Pascal Massimino
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   Dec 1994 : Creation.
*
******************************************************************************/

int Iteration_Julia(const VECTOR point, const Fractal *Julia, DBL **IterStack)
{
	int i;
	DBL x, y, z, w;
	DBL d, x2;
	DBL Exit_Value;

	IterStack[X][0] = x = point[X];
	IterStack[Y][0] = y = point[Y];
	IterStack[Z][0] = z = point[Z];
	IterStack[W][0] = w = (Julia->SliceDist
	                - Julia->Slice[X]*x
	                - Julia->Slice[Y]*y
	                - Julia->Slice[Z]*z)/Julia->Slice[T];

	Exit_Value = Julia->Exit_Value;

	for (i = 1; i <= Julia->Num_Iterations; ++i)
	{
		d = y * y + z * z + w * w;

		x2 = x * x;

		if ((d + x2) > Exit_Value)
		{
			return (false);
		}

		x *= 2.0;

		IterStack[Y][i] = y = x * y + Julia->Julia_Parm[Y];
		IterStack[Z][i] = z = x * z + Julia->Julia_Parm[Z];
		IterStack[W][i] = w = x * w + Julia->Julia_Parm[T];
		IterStack[X][i] = x = x2 - d + Julia->Julia_Parm[X];;

	}

	return (true);
}



/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Pascal Massimino
*
* DESCRIPTION
*
* D_Iteration puts in *Dist a lower bound for the distance from *point to the
* set
*
* CHANGES
*
*   Dec 1994 : Creation.
*
******************************************************************************/

/*----------- Distance estimator + iterations ------------*/

int D_Iteration_z3(const VECTOR point, const Fractal *Julia, const VECTOR &, DBL *Dist, DBL **IterStack)
{
	int i, j;
	DBL Norm, d;
	DBL xx, yy, zz;
	DBL x, y, z, w;
	DBL tmp, x2;
	DBL Exit_Value;
	DBL Pow;

	x = IterStack[X][0] = point[X];
	y = IterStack[Y][0] = point[Y];
	z = IterStack[Z][0] = point[Z];
	w = IterStack[W][0] = (Julia->SliceDist
	                - Julia->Slice[X]*x
	                - Julia->Slice[Y]*y
	                - Julia->Slice[Z]*z)/Julia->Slice[T];

	Exit_Value = Julia->Exit_Value;

	for (i = 1; i <= Julia->Num_Iterations; i++)
	{
		d = y * y + z * z + w * w;

		x2 = x * x;

		if ((Norm = d + x2) > Exit_Value)
		{
			/* Distance estimator */

			x = IterStack[X][0];
			y = IterStack[Y][0];
			z = IterStack[Z][0];
			w = IterStack[W][0];

			Pow = 1.0 / 3.0;

			for (j = 1; j < i; ++j)
			{
				xx = x * IterStack[X][j] - y * IterStack[Y][j] - z * IterStack[Z][j] - w * IterStack[W][j];
				yy = x * IterStack[Y][j] + y * IterStack[X][j] - z * IterStack[W][j] + w * IterStack[Z][j];
				zz = x * IterStack[Z][j] + y * IterStack[W][j] + z * IterStack[X][j] - w * IterStack[Y][j];
				w  = x * IterStack[W][j] - y * IterStack[Z][j] + z * IterStack[Y][j] + w * IterStack[X][j];

				x = xx;
				y = yy;
				z = zz;

				Pow /= 3.0;
			}

			*Dist = Pow * sqrt(Norm / (x * x + y * y + z * z + w * w)) * log(Norm);

			return (false);
		}

		tmp = 3.0 * x2 - d;

		IterStack[X][i] = x = x * (x2 - 3.0 * d) + Julia->Julia_Parm[X];
		IterStack[Y][i] = y = y * tmp + Julia->Julia_Parm[Y];
		IterStack[Z][i] = z = z * tmp + Julia->Julia_Parm[Z];
		IterStack[W][i] = w = w * tmp + Julia->Julia_Parm[T];
	}

	*Dist = Julia->Precision;

	return (true);
}



/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Pascal Massimino
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   Dec 1994 : Creation.
*
******************************************************************************/

int D_Iteration_Julia(const VECTOR point, const Fractal *Julia, const VECTOR &, DBL *Dist, DBL **IterStack)
{
	int i, j;
	DBL Norm, d;
	DBL Exit_Value;
	DBL x, y, z, w;
	DBL xx, yy, zz, x2;
	DBL Pow;

	x = IterStack[X][0] = point[X];
	y = IterStack[Y][0] = point[Y];
	z = IterStack[Z][0] = point[Z];
	w = IterStack[W][0] = (Julia->SliceDist
	                - Julia->Slice[X]*x
	                - Julia->Slice[Y]*y
	                - Julia->Slice[Z]*z)/Julia->Slice[T];

	Exit_Value = Julia->Exit_Value;

	for (i = 1; i <= Julia->Num_Iterations; i++)
	{
		d = y * y + z * z + w * w;

		x2 = x * x;

		if ((Norm = d + x2) > Exit_Value)
		{
			/* Distance estimator */

			x = IterStack[X][0];
			y = IterStack[Y][0];
			z = IterStack[Z][0];
			w = IterStack[W][0];

			Pow = 1.0 / 2.0;

			for (j = 1; j < i; ++j)
			{
				xx = x * IterStack[X][j] - y * IterStack[Y][j] - z * IterStack[Z][j] - w * IterStack[W][j];
				yy = x * IterStack[Y][j] + y * IterStack[X][j] + w * IterStack[Z][j] - z * IterStack[W][j];
				zz = x * IterStack[Z][j] + z * IterStack[X][j] + y * IterStack[W][j] - w * IterStack[Y][j];
				w  = x * IterStack[W][j] + w * IterStack[X][j] + z * IterStack[Y][j] - y * IterStack[Z][j];

				x = xx;
				y = yy;
				z = zz;

				Pow /= 2.0;
			}

			*Dist = Pow * sqrt(Norm / (x * x + y * y + z * z + w * w)) * log(Norm);

			return (false);
		}

		x *= 2.0;

		IterStack[Y][i] = y = x * y + Julia->Julia_Parm[Y];
		IterStack[Z][i] = z = x * z + Julia->Julia_Parm[Z];
		IterStack[W][i] = w = x * w + Julia->Julia_Parm[T];
		IterStack[X][i] = x = x2 - d + Julia->Julia_Parm[X];

	}

	*Dist = Julia->Precision;

	return (true);
}

/*****************************************************************************
*
* FUNCTION
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Pascal Massimino
*
* DESCRIPTION
*
* Provided the iterations sequence has been built, perform the computation of
* the Normal
*
* CHANGES
*
*   Dec 1994 : Creation.
*
******************************************************************************/

void Normal_Calc_z3(VECTOR Result, int N_Max, const Fractal *, DBL **IterStack)
{
	DBL
	n11 = 1.0, n12 = 0.0, n13 = 0.0, n14 = 0.0,
	n21 = 0.0, n22 = 1.0, n23 = 0.0, n24 = 0.0,
	n31 = 0.0, n32 = 0.0, n33 = 1.0, n34 = 0.0;

	DBL x, y, z, w;
	int i;
	DBL tmp, dtmp, dtmp2, x2, x3, x4;

	x = IterStack[X][0];
	y = IterStack[Y][0];
	z = IterStack[Z][0];
	w = IterStack[W][0];

	for (i = 1; i <= N_Max; i++)
	{
		tmp = y * y + z * z + w * w;

		x2 = x * x;
		x3 = x2 - tmp;
		x4 = 3.0 * x2 - tmp;

		Deriv_z3(n11, n12, n13, n14);
		Deriv_z3(n21, n22, n23, n24);
		Deriv_z3(n31, n32, n33, n34);

		x = IterStack[X][i];
		y = IterStack[Y][i];
		z = IterStack[Z][i];
		w = IterStack[W][i];
	}

	Result[X] = n11 * x + n12 * y + n13 * z + n14 * w;
	Result[Y] = n21 * x + n22 * y + n23 * z + n24 * w;
	Result[Z] = n31 * x + n32 * y + n33 * z + n34 * w;
}



/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   Pascal Massimino
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   Dec 1994 : Creation.
*
******************************************************************************/

void Normal_Calc_Julia(VECTOR Result, int N_Max, const Fractal *, DBL **IterStack)
{
	DBL
	n11 = 1.0, n12 = 0.0, n13 = 0.0, n14 = 0.0,
	n21 = 0.0, n22 = 1.0, n23 = 0.0, n24 = 0.0,
	n31 = 0.0, n32 = 0.0, n33 = 1.0, n34 = 0.0;
	DBL tmp;
	DBL x, y, z, w;
	int i;

	x = IterStack[X][0];
	y = IterStack[Y][0];
	z = IterStack[Z][0];
	w = IterStack[W][0];

	for (i = 1; i <= N_Max; i++)
	{
		Deriv_z2(n11, n12, n13, n14);
		Deriv_z2(n21, n22, n23, n24);
		Deriv_z2(n31, n32, n33, n34);

		x = IterStack[X][i];
		y = IterStack[Y][i];
		z = IterStack[Z][i];
		w = IterStack[W][i];
	}

	Result[X] = n11 * x + n12 * y + n13 * z + n14 * w;
	Result[Y] = n21 * x + n22 * y + n23 * z + n24 * w;
	Result[Z] = n31 * x + n32 * y + n33 * z + n34 * w;
}



/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*   
* RETURNS
*   
* AUTHOR
*
*   Pascal Massimino
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   Dec 1994 : Creation.
*
******************************************************************************/

int F_Bound_Julia(const Ray& ray, const Fractal *fractal, DBL *Depth_Min, DBL *Depth_Max)
{
	return (Sphere::Intersect(ray, fractal->Center, fractal->Radius_Squared, Depth_Min, Depth_Max));
}

}
