//////////////////////////////////////////////////////////////////////////////
//
// Asura_PlatformMaths.h
//
// By: Mike Healey, Kevin Lea.
//
// Copyright (c) 2002 Rebellion. All rights reserved.
//
// Platform-specific implementations of Asura_Maths functions. Designed to be
// included by Asura_Maths.h
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef ___ASURA_PLATFORMMATHS_H___
#define ___ASURA_PLATFORMMATHS_H___ 1

// I N C L U D E S ///////////////////////////////////////////////////////////

// Stdlib independent.
#include <limits.h>
#include <math.h>

// Engine platform-specific.
#include "Core/Asura_PlatformBasics.h"

// M A C R O S ///////////////////////////////////////////////////////////////

// Turn on / off asm optimisations.
#ifdef __SNC__
#define ASURA_PS2_INLINE_ASM_MATH (0)	// MH 14.3.05 - TODO figure out why it breaks.
#else
#define ASURA_PS2_INLINE_ASM_MATH (1)
#endif

// C L A S S E S /////////////////////////////////////////////////////////////

class Asura_PS2_Maths
{

    public:
    
        static float Sine(Asura_Angle angle);
        static float Cosine(Asura_Angle angle);
        static void SinCos(Asura_Angle fAngle, float& fOutputSine, float& fOutputCos);
        static void SinCos2(Asura_Angle fAngle1, Asura_Angle fAngle2, float& fOutputSine1, 
                            float& fOutputCos1, float& fOutputSine2, float& fOutputCos2);
        static inline float ArcTan(float fX, float fY)
		{
			return atan2f(fX, fY);
		}

		static float ArcSine( float fSine );
		static float ArcCosine( float fCosine );
        static void Quat_Slerp(Asura_Quat& xOutput, const Asura_Quat& xInput1, const Asura_Quat& xInput2, float l);
        static void MatrixMultiply(Asura_Matrix_3x3& xOutput, const Asura_Matrix_3x3& xInput1, const Asura_Matrix_3x3& xInput2);

        static void CompQuat_ToQuat(const Asura_Comp_Quat* pxCompQuat, Asura_Quat& xQuat);
        static void CompQuat_FromQuat(Asura_Comp_Quat* pxCompQuat, const Asura_Quat& xQuat);

        static void StartSinCos( Asura_Angle fAngle )
        {
            Asura_PS2_QW xTemp;
            xTemp.f32s[0] = fAngle;
            asm __volatile__("QMTC2	%0, $vf01" : : "r"(xTemp.u128));
            // activate microprogram (start at 0)
            asm __volatile__ ("VCALLMS    0");
        }

        static inline void FinishSinCos(float& fOutputSine, float& fOutputCos)
        {
            Asura_PS2_QW xTemp;
            // fetch results. specifying .I to interlock means we stall until the program is completed.
            asm __volatile__ ("QMFC2.I (%0), $vf02" : "=r" (xTemp.u128) );
            fOutputSine = xTemp.f32s[0];
            fOutputCos = xTemp.f32s[1];
        }

        static inline void StartSinCos2(Asura_Angle fAngle1, Asura_Angle fAngle2)
        {
            Asura_PS2_QW xTemp;
            xTemp.f32s[0] = fAngle1;
            xTemp.f32s[2] = fAngle2;
            asm __volatile__("QMTC2	%0, $vf01" : : "r"(xTemp.u128));
            // activate microprogram (start at 0)
            asm __volatile__ ("VCALLMS    0");
        }

        static inline void FinishSinCos2(float& fOutputSine1, float& fOutputCos1, float& fOutputSine2, float& fOutputCos2)
        {
            Asura_PS2_QW xTemp;
            // fetch results. specifying .I to interlock means we stall until the program is completed.
            asm __volatile__ ("QMFC2.I (%0), $vf02" : "=r" (xTemp.u128) );
            fOutputSine1 = xTemp.f32s[0];
            fOutputCos1 = xTemp.f32s[1];
            fOutputSine2 = xTemp.f32s[2];
            fOutputCos2 = xTemp.f32s[3];
        }

};
// End class Asura_PS2_Maths.

// P R O T O T Y P E S   A N D   I N L I N E   F U N C T I O N S /////////////

// return x raised to the y'th power
static inline float Asura_Maths::Power(float x, float y)
{
	// assume powf exists. if not then that platform is going to have to come up with an alternative
	return powf(x, y);
}

static inline Asura_Angle Asura_Maths::DegToRad(float deg)          
{ 
    return (((float)deg)*DegToRadConstant); 
}

static inline float Asura_Maths::RadToDeg(Asura_Angle rad)
{ 
    return (((float)rad)*RadToDegConstant); 
}

template<>
static inline float Asura_Maths::Max<float>( const float& fA, const float& fB )
{
#if ASURA_PS2_INLINE_ASM_MATH
    float fMax;
    asm( "max.s %0, %1, %2" : "=f"(fMax) : "f"(fA), "f"(fB) );
    return fMax;
#else
    return (fA > fB ? fA : fB);
#endif
}

template<>
static inline int Asura_Maths::Max<int>( const int& iA, const int& iB ) 
{
#if ASURA_PS2_INLINE_ASM_MATH
    int iMax;
    asm __volatile__( ".set noreorder		\n"  \
                      "slt  $24, %1, %2		\n"    \
                      "movz %0,  %1, $24	\n"    \
                      "movn %0,  %2, $24	\n"    \
                      ".set reorder			\n"   \
                      : "=&r"(iMax)             \
                      : "r"(iA), "r"(iB)          \
                      : "$24"                   );
    return iMax;
#else
    return (iA > iB ? iA : iB);
#endif
}

template<>
static inline unsigned Asura_Maths::Max<unsigned>( const unsigned& uA, const unsigned& uB )
{
#if ASURA_PS2_INLINE_ASM_MATH
    unsigned int uMax;
    asm __volatile__( ".set noreorder		\n"  \
                      "sltu  $24, %1, %2	\n"    \
                      "movz %0,  %1, $24	\n"    \
                      "movn %0,  %2, $24	\n"    \
                      ".set reorder			\n"   \
                      : "=&r"(uMax)             \
                      : "r"(uA), "r"(uB)          \
                      : "$24"                   );
    return uMax;
#else
    return (uA > uB ? uA : uB);
#endif
} 

template< typename T >
static inline T Asura_Maths::Max( const T& tA, const T& tB )
{
	return (tA > tB) ? tA : tB;
}

template<>
static inline float Asura_Maths::Min<float>( const float& fA, const float& fB ) 
{ 
#if ASURA_PS2_INLINE_ASM_MATH
    float fMin;
    asm ( "min.s %0, %1, %2" : "=f"(fMin) : "f"(fA), "f"(fB) );
    return fMin; 
#else
    return (fA < fB ? fA : fB);
#endif
}

template<>
static inline int Asura_Maths::Min<int>( const int& iA, const int& iB ) 
{ 
#if ASURA_PS2_INLINE_ASM_MATH
    int iMin;
    // %0 = output, %1 = test reg, %2 = fA, %3 = fB
    asm __volatile__( ".set noreorder		\n" \
                      "slt  $24, %1, %2		\n" \
                      "movz %0, %2, $24		\n" \
                      "movn %0, %1, $24		\n" \
                      ".set reorder			\n" \
                      : "=&r"(iMin)             \
                      : "r"(iA), "r"(iB)          \
                      : "$24"                   );
    return iMin;
#else
    return (fA < fB ? fA : fB); 
#endif
}

template<>
static inline unsigned Asura_Maths::Min<unsigned>( const unsigned& uA, const unsigned& uB )
{ 
#if ASURA_PS2_INLINE_ASM_MATH
    unsigned int uMin;
    // %0 = output, %1 = test reg, %2 = uA, %3 = uB
    asm __volatile__( ".set noreorder		\n" \
                      "sltu	$24, %1, %2		\n" \
                      "movz %0,	 %2, $24	\n" \
                      "movn %0,  %1, $24	\n" \
                      ".set reorder			\n" \
                      : "=&r"(uMin)             \
                      : "r"(uA), "r"(uB)          \
                      : "$24"                   );
    return uMin;
#else
    return (uA < uB ? uA : uB); 
#endif
}

template< typename T >
static inline T Asura_Maths::Min( const T& tA, const T& tB )
{
	return (tA < tB) ? tA : tB;
}

static inline float Asura_Maths::Abs( float fIn )
{
#if ASURA_PS2_INLINE_ASM_MATH
    float fRetVal;
    asm( "abs.s %0, %1" : "=f" (fRetVal) : "f" (fIn) );
    return fRetVal;
#else
    return fIn < 0 ? -fIn : fIn;
#endif
}

static inline int Asura_Maths::Abs(int a)
{
    if (a<0)
    {
        return -a;
    }
    else
    {
        return a;
    }
}

static inline float Asura_Maths::Sign(float a)
{
    if (a>0)
    {
        return +1.0f;
    }
    else if (a<0)
    {
        return -1.0f;
    }
    else
    {
        return 0.0f;
    }
}

static inline int Asura_Maths::Sign(int a)
{
    if (a>0)
    {
        return 1;
    }
    else if (a<0)
    {
        return -1;
    }
    else
    {
        return 0;
    }
}

static inline u_int Asura_Maths::GetFloorLog2( u_int uValue )
{
    u_int uLog2 = 0;
    u_int uNext = 2;

    while( uNext <= uValue )
    {
        uNext <<= 1;
        ++uLog2;
    }
    
    return uLog2;
}

static inline u_int Asura_Maths::GetCeilLog2( u_int uValue )
{
    u_int uLog2 = 0;
    u_int uCmp  = 1;

    while( uCmp < uValue )
    {
        uCmp <<= 1;
        ++uLog2;
    }
    
    return uLog2;
}

static inline u_int Asura_Maths::GetFloorPow2( u_int uValueOld )
{
	if( uValueOld <= 1)
	{
		return( uValueOld);
	}

	u_int uNShift = 0;
	u_int uValue = uValueOld;

	for( ; uValue != 0; uValue >>= 1, uNShift++);

	u_int uValueNew = 1 << (uNShift - 1);
	if( uValueOld < uValueNew)
	{
		uValueNew >>= 1;
	}

	return( uValueNew);
}

static inline u_int Asura_Maths::GetCeilPow2( u_int uValueOld )
{
	if( uValueOld <= 1)
	{
		return( uValueOld);
	}

	u_int uNShift = 0;
	u_int uValue = uValueOld;

	for( ; uValue != 0; uValue >>= 1, uNShift++);

	u_int uValueNew = 1 << (uNShift - 1);
	if( uValueNew < uValueOld)
	{
		uValueNew <<= 1;
	}

	return( uValueNew);
}


static inline float Asura_Maths::Sine( Asura_Angle fAngle )
{
    return Asura_PS2_Maths::Sine(fAngle);
}

static inline float Asura_Maths::Cosine(Asura_Angle fAngle)
{
    return Asura_PS2_Maths::Cosine(fAngle);
}

static inline void Asura_Maths::SinCos(Asura_Angle fAngle, float& fOutputSine, float& fOutputCos)
{
    Asura_PS2_Maths::SinCos(fAngle, fOutputSine, fOutputCos);
}

static inline void Asura_Maths::SinCos2(Asura_Angle fAngle1, Asura_Angle fAngle2, float& fOutputSine1, 
                                        float& fOutputCos1, float& fOutputSine2, float& fOutputCos2)
{
    Asura_PS2_Maths::SinCos2(fAngle1, fAngle2, fOutputSine1, fOutputCos1, fOutputSine2, fOutputCos2);
}

static inline float Asura_Maths::Tangent(Asura_Angle fAngle)
{
    float fSin, fCos;
    Asura_PS2_Maths::SinCos( fAngle, fSin, fCos );
    return fSin / fCos;
}

static inline Asura_Angle Asura_Maths::ArcSine( float fSine )
{
	//return asinf( fSine );
    return Asura_PS2_Maths::ArcSine( fSine );
}

static inline Asura_Angle Asura_Maths::ArcCosine( float fCosine )
{
    //return acosf( fCosine );
    return Asura_PS2_Maths::ArcCosine( fCosine );
}

static inline Asura_Angle Asura_Maths::ArcTangent( float fX, float fY )
{
    //return atan2f( fX, fY );
    return Asura_PS2_Maths::ArcTan(fX, fY);
}

static inline float Asura_Maths::Sqrt( float fIn )
{
    float fRetVal;
    asm( "sqrt.s %0, %1" : "=f" (fRetVal) : "f" (fIn) );
    return fRetVal;
}

static inline float Asura_Maths::InvSqrt( float fIn )
{
    const float fOne = 1.0f;
    float fRetVal;
    asm( "rsqrt.s %0, %1, %2" : "=f" (fRetVal) : "f" (fOne), "f" (fIn) );
    return fRetVal;
}

// (MH - 18.04.2002) - since vararg functions implicitly convert floats to doubles,
// (as well as chars to ints) centralise the conversion here. This prevents a warning
// every time vararg functions are used in the code.
static inline double Asura_Maths::FtoD( float f )
{
    double Asura_PS2_FloatToDouble( float f );
    return Asura_PS2_FloatToDouble( f );
}

static inline int Asura_Maths::FloatToInt_Near( float fIn )
{
    // (MP - 22.08.2002) NB: This is nice and fast on the PS2 because the
    // cast can be done in just a couple of assembly instructions.
    // (MP - 22.08.2002) NB: IEEE754 says 2.5 should round to 2 while 3.5
    // should round to 4, ie. in the case of a tie, go for the even number.
    // This code doesn't do that.
    return static_cast<int>( fIn > 0.0f ? fIn + 0.5f : fIn - 0.5f );
}

static inline int Asura_Maths::FloatToInt_Chop( float fIn )
{
    // See comment above.
    return static_cast<int>( fIn );
}

static inline int Asura_Maths::FloatToInt( float fIn )
{
    // See comment above.
    return static_cast<int>( fIn );
}

static inline float Asura_Maths::CalculateTriArea( const Asura_Vector_3& xV0, const Asura_Vector_3& xV1, const Asura_Vector_3& xV2 )
{
    return( 0.5f * ((xV1 - xV0) ^ (xV2 - xV0)).Magnitude() );
}

// KOR - 28/11/2005 - floating point modulo (remainder of fX/fY); should
// be fmodf() on most platforms
static inline float Asura_Maths::FloatMod(float fX, float fY)
{
	return fmodf(fX, fY);
}

// KOR - 28/11/2005 - Returns f where f is in the range [fMin, fMax) and
// f = fAngle mod (fMax - fMin)
static inline float Asura_Maths::AngleToRange(register const float fAngle,
	register const float fMin, register const float fMax)
{
	register const float fRange = fMax - fMin;

	// This gets us a fReturnAngle in the range (-fRange, fRange) where
	// fReturnAngle = fAngle mod fRange
	register const float fReturnAngle = FloatMod(fAngle, fRange);

	if (fReturnAngle < fMin)
	{
		// This deals with fReturnAngle in (-fRange, fMin)
		return fReturnAngle + fRange;
	}
	if (fReturnAngle >= fMax)
	{
		// This deals with fReturnAngle in [fMax, fRange)
		return fReturnAngle - fRange;
	}

	// Otherwise fReturnAngle is in [fMin, fMax) as desired.
	return fReturnAngle;
}

static inline float Asura_Maths::RadiansToZeroCentredRange(register const float fRadians)
{
	return AngleToRange(fRadians, -Pi, Pi);
}

static inline float Asura_Maths::RadiansToZeroMinimumRange(register const float fRadians)
{
	return AngleToRange(fRadians, 0.f, Pi * 2.f);
}

static inline bool Asura_Maths::IsNaN ( const float fNum )
{
	// If this platform conforms to IEEE754 try...
	//return (fNum != fNum);
	Asura_Assert( false, "Function not written" );
	return false;
}

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

//
// Asura_Random.
//

inline float Asura_RandomGenerator::GetFloat( void )
{
    return( static_cast<int>(GetIntU()) / static_cast<float>(INT_MAX) );
}

inline float Asura_RandomGenerator::GetFloat( float fMin, float fMax )
{
    return( fMin + GetFloat() * (fMax - fMin) );
}

inline int Asura_RandomGenerator::GetInt( int iMin, int iMax )
{
    // Doing something like (iMin + GetFloat() * (iMax - iMin)) would
    // actually be faster but leads to gaps in the output with really large
    // ranges. While this probably won't ever be an issue, nor should the
    // performance, and so the more robust implementation is probably a 
    // better choice.
    return( iMin + GetIntU() % (iMax - iMin + 1) );
}

inline u_int Asura_RandomGenerator::GetIntU( u_int uMin, u_int uMax )
{
    // This is ok since unsigned range only goes up to INT_MAX as well.
    return GetInt( uMin, uMax );
}


//
// Asura_Matrix_3x3.
//

inline void Asura_Matrix_3x3::Transpose(void)
{
    float t;
    
    t = e12;
    e12 = e21;
    e21 = t;
        
    t = e13;
    e13 = e31;
    e31 = t;    
    
    t = e23;
    e23 = e32;
    e32 = t;    
}

inline Asura_Vector_3 Asura_Matrix_3x3::ApplyTranspose( const Asura_Vector_3& xVec ) const
{
    return Asura_Vector_3( e11 * xVec.x + e21 * xVec.y + e31 * xVec.z,
                           e12 * xVec.x + e22 * xVec.y + e32 * xVec.z,
                           e13 * xVec.x + e23 * xVec.y + e33 * xVec.z );
}

inline void Asura_Matrix_3x3::Identity(void)
{
    e11=e22=e33=1.0f;
    e12=e13=e21=e23=e31=e32=0.0f;
}

inline Asura_Matrix_3x3 Asura_Matrix_3x3::operator * ( const Asura_Matrix_3x3& m) const
{
    class Asura_Matrix_3x3 result;
//
//  result.e11 = e11 * m.e11 + e12 * m.e21 + e13 * m.e31;
//  result.e21 = e21 * m.e11 + e22 * m.e21 + e23 * m.e31;
//  result.e31 = e31 * m.e11 + e32 * m.e21 + e33 * m.e31;
//
//  result.e12 = e11 * m.e12 + e12 * m.e22 + e13 * m.e32;
//  result.e22 = e21 * m.e12 + e22 * m.e22 + e23 * m.e32;
//  result.e32 = e31 * m.e12 + e32 * m.e22 + e33 * m.e32;
//
//  result.e13 = e11 * m.e13 + e12 * m.e23 + e13 * m.e33;
//  result.e23 = e21 * m.e13 + e22 * m.e23 + e23 * m.e33;
//  result.e33 = e31 * m.e13 + e32 * m.e23 + e33 * m.e33;
//
    Asura_PS2_Maths::MatrixMultiply(result, *this, m);
    return result;
}

inline Asura_Matrix_3x3 Asura_Matrix_3x3::operator * ( const float s) const
{
    class Asura_Matrix_3x3 result;

    result.e11 = e11 * s;
    result.e21 = e21 * s;
    result.e31 = e31 * s;

    result.e12 = e12 * s;
    result.e22 = e22 * s;
    result.e32 = e32 * s;

    result.e13 = e13 * s;
    result.e23 = e23 * s;
    result.e33 = e33 * s;

    return result;
}

//
// Asura_Vector_2.
//

inline Asura_Vector_2::Asura_Vector_2(const Asura_Vector_2& v)
{   
    x = v.x;
    y = v.y;
}

inline Asura_Vector_2::Asura_Vector_2(float x0, float y0)
{
    x = x0;
    y = y0;
}

// binary ops

inline Asura_Vector_2 Asura_Vector_2::operator * ( float fScale ) const
{
    return Asura_Vector_2( this->x * fScale, this->y * fScale );
}

inline Asura_Vector_2 Asura_Vector_2::operator + ( const Asura_Vector_2& v) const
{
    return Asura_Vector_2(x + v.x, y + v.y);
}

inline void Asura_Vector_2::operator += ( const Asura_Vector_2& v)
{
    x += v.x;
    y += v.y;
}

inline Asura_Vector_2 Asura_Vector_2::operator - ( const Asura_Vector_2& v) const
{
    return Asura_Vector_2(x - v.x, y - v.y);
}

inline void Asura_Vector_2::operator -= ( const Asura_Vector_2& v)
{
    x -= v.x;
    y -= v.y;
}

inline void Asura_Vector_2::operator *= ( float s )
{
    x *= s;
    y *= s;
}

inline void Asura_Vector_2::operator /= ( float f)
{
    float fInv = 1.0f / f;
    x *= fInv;
    y *= fInv;
}

inline bool Asura_Vector_2::operator == ( const Asura_Vector_2& v) const
{
    return x == v.x && y == v.y;
}

inline bool Asura_Vector_2::operator != ( const Asura_Vector_2& v) const
{
    return x != v.x || y != v.y;
}

//dot product
inline float  Asura_Vector_2::operator * ( const Asura_Vector_2& v) const
{
    return (x*v.x + y*v.y);
}

//Pics a random vector in the plane, with random length [0..1] and
//random direction (with symmetrical distribution)
inline Asura_Vector_2& Asura_Vector_2::RandomVector(Asura_RandomGenerator* pxRNG)
{
    //Choose random elements for x and y, until the squared length is
    //within the unit circle.
    do
    {
        x = pxRNG->GetFloat( -1.0f, 1.0f );
        y = pxRNG->GetFloat( -1.0f, 1.0f );
    }
    while(x * x + y * y > 1.f);

    //Return the resulting vector
    return *this;
}


inline Asura_Vector_2& Asura_Vector_2::RandomDirection(Asura_RandomGenerator* pxRNG)
{
    float fLength2;

    //Choose random elements for x and y, until 
    //the squared length is within the unit circle, but large
    //enough that the normalisation won't result in poor conditioning.
    do
    {
        x = pxRNG->GetFloat( -1.0f, 1.0f );
        y = pxRNG->GetFloat( -1.0f, 1.0f );

        fLength2 = x * x + y * y;
    }
    while(fLength2 > 1.f || fLength2 < 0.01f);

    fLength2 = 1.f / Asura_Maths::Sqrt(fLength2);

    x *= fLength2;
    y *= fLength2;

    //Return the resulting vector
    return *this;
}

inline float Asura_Vector_2::MagnitudeSquared( void ) const
{   
    return ((x*x) + (y*y));
}

inline float Asura_Vector_2::Magnitude( void ) const
{   
    return Asura_Maths::Sqrt( (x*x + y*y) );
}

inline float Asura_Vector_2::Normalise(void)
{
    const float m = Magnitude();

    if (m==0.0f)    return m;

    const float rm = 1.0f/m;
    x *= rm;
    y *= rm;

    return m;
}

inline void Asura_Vector_2::Zero(void)
{
    x=y=0.0f;
}

//
// Asura_Vector_3.
//

inline Asura_Vector_3::Asura_Vector_3(const float * fPtr)
{
    x = fPtr[0];
    y = fPtr[1];
    z = fPtr[2];
}

inline Asura_Vector_3::Asura_Vector_3(const Asura_Vector_3& v)
{   
    x = v.x;
    y = v.y;
    z = v.z;
}

inline Asura_Vector_3::Asura_Vector_3(float x0, float y0, float z0)
{
    x = x0;
    y = y0;
    z = z0;
}

// assignment operators
inline void Asura_Vector_3::operator = ( const Asura_Vector_3& v)
{
	x = v.x;
	y = v.y;
	z = v.z;
}
inline void Asura_Vector_3::operator += ( const Asura_Vector_3& v)
{
    x += v.x;
    y += v.y;
    z += v.z;
}
inline void Asura_Vector_3::operator -= ( const Asura_Vector_3& v)
{
    x -= v.x;
    y -= v.y;
    z -= v.z;
}
inline void Asura_Vector_3::operator *= ( float f)
{
    x *= f;
    y *= f;
    z *= f;
}
inline void Asura_Vector_3::operator /= ( float f)
{
    float fInv = 1.0f / f;
    x *= fInv;
    y *= fInv;
    z *= fInv;
}

// unary operators
inline Asura_Vector_3 Asura_Vector_3::operator - () const
{
    return Asura_Vector_3(-x, -y, -z);
}

// binary operators
inline Asura_Vector_3 Asura_Vector_3::operator + ( const Asura_Vector_3& v) const
{
    return Asura_Vector_3(x + v.x, y + v.y, z + v.z);
}
inline Asura_Vector_3 Asura_Vector_3::operator - ( const Asura_Vector_3& v) const
{
    return Asura_Vector_3(x - v.x, y - v.y, z - v.z);
}
inline Asura_Vector_3 Asura_Vector_3::operator * ( float f) const
{
    return Asura_Vector_3(x * f, y * f, z * f);
}
inline Asura_Vector_3 Asura_Vector_3::operator / ( float f) const
{
    float fInv = 1.0f / f;
    return Asura_Vector_3(x * fInv, y * fInv, z * fInv);
}                                               

inline Asura_Vector_3 Asura_Vector_3::operator ^ ( const Asura_Vector_3& v) const
{
    return Asura_Vector_3(y*v.z - z*v.y, z*v.x - x*v.z, x*v.y - y*v.x);
}

inline float  Asura_Vector_3::operator * ( const Asura_Vector_3& v) const
{
    return (x*v.x + y*v.y + z*v.z);
}

inline Asura_Vector_3 Asura_Vector_3::operator * ( const Asura_Matrix_3x3& m) const
{
    return Asura_Vector_3( m.e11 * x + m.e12 * y + m.e13 * z,
                           m.e21 * x + m.e22 * y + m.e23 * z,
                           m.e31 * x + m.e32 * y + m.e33 * z );
}

inline bool Asura_Vector_3::operator == ( const Asura_Vector_3& v) const
{
    return x == v.x && y == v.y && z == v.z;
}

inline bool Asura_Vector_3::ApproximatelyEquals(const Asura_Vector_3& v, float epsilon /*= 1.0e-6f*/) const
{
    Asura_Vector_3 delta;
    delta.x = x-v.x;
    delta.y = y-v.y;
    delta.z = z-v.z;

    return ((delta.x*delta.x+delta.y*delta.y+delta.z*delta.z)<epsilon);
    
}
inline bool Asura_Vector_3::operator != ( const Asura_Vector_3& v) const
{
    return x != v.x || y != v.y || z != v.z;
}

inline Asura_Vector_3 Asura_Vector_3::Vector_Multiply(const Asura_Vector_3& v1) const
{
	return Asura_Vector_3(v1.x * x, v1.y * y, v1.z * z);
}

// useful operations
// useful operations
//Picks a random vector in the plane, with a length of 1
//in a random direction. The distribution is symmetrical
inline Asura_Vector_3& Asura_Vector_3::RandomDirection(Asura_RandomGenerator* pxRNG)
{
    float fLength2;

    //Choose random elements for x and y, until 
    //the squared length is within the unit sphere, but large
    //enough that the normalisation won't result in poor conditioning.
    do
    {
        x = pxRNG->GetFloat( -1.0f, 1.0f );
        y = pxRNG->GetFloat( -1.0f, 1.0f );
        z = pxRNG->GetFloat( -1.0f, 1.0f );

        fLength2 = x * x + y * y + z * z;
    }
    while(fLength2 > 1.f || fLength2 < 0.01f);

    fLength2 = 1.f / Asura_Maths::Sqrt(fLength2);

    x *= fLength2;
    y *= fLength2;
    z *= fLength2;

    //Return the resulting vector
    return *this;
}


//Pics a random vector in a sphere, with random length [0..1] and
//random direction (with symmetrical distribution)
inline Asura_Vector_3& Asura_Vector_3::RandomVector(Asura_RandomGenerator* pxRNG)
{
    //Choose random elements for x, y and z, until the squared length is
    //within the unit sphere.
    do
    {
        x = pxRNG->GetFloat( -1.0f, 1.0f );
        y = pxRNG->GetFloat( -1.0f, 1.0f );
        z = pxRNG->GetFloat( -1.0f, 1.0f );
    }
    while(x * x + y * y + z * z > 1.f);

    //Return the resulting vector
    return *this;
}


inline float Asura_Vector_3::Normalise(void)
{
    const float m = Magnitude();
    
    if (m==0.0f)    return m;

    const float rm = 1.0f/m;
    x *= rm;
    y *= rm;
    z *= rm;

    return m;
}       

inline float Asura_Vector_3::Magnitude(void) const
{   
    return Asura_Maths::Sqrt((x*x) + (y*y) + (z*z));
}

inline float Asura_Vector_3::MagnitudeSquared(void) const
{   
    return ((x*x) + (y*y) + (z*z));
}

inline void Asura_Vector_3::Zero(void)
{
    x=y=z=0.0f;
}

//
// Asura_Euler_3.
//

inline Asura_Euler_3::Asura_Euler_3(float x0, float y0, float z0)
{
    x = x0;
    y = y0;
    z = z0;
}

// assignment operators
inline void Asura_Euler_3::operator += ( const Asura_Euler_3& xEuler)
{
    x += xEuler.x;
    y += xEuler.y;
    z += xEuler.z;
}
inline void Asura_Euler_3::operator -= ( const Asura_Euler_3& xEuler)
{
    x -= xEuler.x;
    y -= xEuler.y;
    z -= xEuler.z;
}
inline void Asura_Euler_3::operator *= ( float f)
{
    x *= f;
    y *= f;
    z *= f;
}

inline void Asura_Euler_3::operator /= ( float f)
{
    float fInv = 1.0f / f;
    x *= fInv;
    y *= fInv;
    z *= fInv;
}

// unary operators
inline Asura_Euler_3 Asura_Euler_3::operator - () const
{
    return Asura_Euler_3(-x, -y, -z);
}

// binary operators
inline Asura_Euler_3 Asura_Euler_3::operator + ( const Asura_Euler_3& xEuler) const
{
    return Asura_Euler_3(x + xEuler.x, y + xEuler.y, z + xEuler.z);
}
inline Asura_Euler_3 Asura_Euler_3::operator - ( const Asura_Euler_3& xEuler) const
{
    return Asura_Euler_3(x - xEuler.x, y - xEuler.y, z - xEuler.z);
}
inline Asura_Euler_3 Asura_Euler_3::operator * ( float f) const
{
    return Asura_Euler_3(x * f, y * f, z * f);
}
inline Asura_Euler_3 Asura_Euler_3::operator / ( float f) const
{
    float fInv = 1.0f / f;
    return Asura_Euler_3(x * fInv, y * fInv, z * fInv);
}                                           

inline void Asura_Euler_3::ToMatrix(Asura_Matrix_3x3& m) const
{
//  float c1 = Asura_Maths::Cosine(x);
//  float s1 = Asura_Maths::Sine(x);
//  float c2 = Asura_Maths::Cosine(y);
//  float s2 = Asura_Maths::Sine(y);
//  float c3 = Asura_Maths::Cosine(z);
//  float s3 = Asura_Maths::Sine(z);
    float s1,c1,s2,c2,s3,c3;
    Asura_PS2_Maths::SinCos2(x, y, s1, c1, s2, c2);
    Asura_PS2_Maths::SinCos(z, s3, c3);

    m.e11 = c2*c3+s1*s2*s3;
    m.e12 = -c2*s3+s1*s2*c3;
    m.e13 = c1*s2;

    m.e21 = c1*s3;
    m.e22 = c1*c3;
    m.e23 = -s1;

    m.e31 = -s2*c3 + s1*c2*s3;
    m.e32 = s2*s3 + s1*c2*c3;
    m.e33 = c1*c2;
}

inline void Asura_Euler_3::FromMatrix(const Asura_Matrix_3x3& m)
{
    float sx = -m.e23;

    if (sx < 1.0f)
    {
        if (sx > -1.0f)
        {
            // cx is non-zero then
            x = Asura_Maths::ArcSine(sx);
            y = Asura_Maths::ArcTangent(m.e13,m.e33);
            z = Asura_Maths::ArcTangent(m.e21,m.e22);
        }
        else
        {
            // non-unique

            // -e31 = s(y)c(z)+c(y)s(z) = s(y+z)
            // e11  = c(y)c(z)-s(y)s(z) = c(y+z))
            // y+z  = atan(-e31/e11)
            // if choose z to be zero

            x = -Asura_Maths::Pi*0.5f;
            y = Asura_Maths::ArcTangent(-m.e31,m.e11);
            z = 0;
        }
    }
    else
    {
        // non-unique

            // e12 = s(y)c(z)-c(y)s(z) = s(y-z)
            // e32 = s(y)s(z)+c(y)c(z) = c(y-z)
            // y-z = atan(e12/e32)
            // if choose z to be zero

            x = Asura_Maths::Pi*0.5f;
            y = Asura_Maths::ArcTangent(m.e12,m.e32);
            z = 0;
    }
}

inline void Asura_Euler_3::Zero(void)
{
    x=y=z=0.0f;
}

//
// Asura_Quat.
//

inline Asura_Quat::Asura_Quat(float x0, float y0, float z0, float w0)
{
    x = x0;
    y = y0;
    z = z0;
    w = w0;
}

inline void Asura_Quat::Identity( void )
{
    x = 0.0f;
    y = 0.0f;
    z = 0.0f;
    w = 1.0f;
}

inline void Asura_Quat::Invert( void )
{
    x = -x;
    y = -y;
    z = -z;
}

inline Asura_Quat Asura_Quat::operator * ( float f) const
{
    return Asura_Quat(x*f, y*f, z*f, w*f);
}

inline Asura_Quat Asura_Quat::operator - () const
{
    return Asura_Quat(-x, -y, -z, -w);
}

inline Asura_Quat Asura_Quat::operator + ( const Asura_Quat& q) const
{
    return Asura_Quat(x+q.x, y+q.y, z+q.z, w+q.w);
}

inline bool Asura_Quat::ApproximatelyEquals(const Asura_Quat& q, float epsilon /*= 1.0e-6f*/) const
{
    float cos_omega = q.w*w + q.x*x + q.y*y + q.z*z;
    return( Asura_Maths::Abs(cos_omega) >= 1.0f - epsilon );
}

inline bool Asura_Quat::operator == ( const Asura_Quat& q) const
{
    return x == q.x && y == q.y && z == q.z && w == q.w;
}

inline float Asura_Quat::MagnitudeSquared(void) const
{
    return x*x+y*y+z*z+w*w;
}

inline void Asura_Quat::ToMatrix(Asura_Matrix_3x3& m) const
{
    /* Implementation from Graphics Gems IV */

    const float s = 2.0f;///Magnitude();        

    const float xs = x*s;
    const float ys = y*s;
    const float zs = z*s;
    
    const float wx = w*xs;
    const float wy = w*ys;
    const float wz = w*zs;

    const float xx = x*xs;
    const float xy = x*ys;
    const float xz = x*zs;

    const float yy = y*ys;
    const float yz = y*zs;
    const float zz = z*zs;
        
    m.e11 = 1.0f - (yy+zz);
    m.e21 = xy + wz;
    m.e31 = xz - wy;

    m.e12 = xy - wz;
    m.e22 = 1.0f - (xx + zz);
    m.e32 = yz + wx;

    m.e13 = xz + wy;
    m.e23 = yz - wx;
    m.e33 = 1.0f - (xx+yy);
}

// (MH - 15.07.2002) adapted this from 3dc tools chunk lib.
// See also, Game Programming Gems I p.202.
inline void Asura_Quat::FromMatrix(const Asura_Matrix_3x3 & m)
{
    const int X=0;
    const int Y=1;
    const int Z=2;
    const int W=3;

    int const nxt[3] = 
    {
        Y,Z,X
    };
    
    const float trace = m[0][0] + m[1][1] + m[2][2];
    
    if (trace > 0.0f)
    {
        float s = Asura_Maths::Sqrt(trace + 1.0f);
        w = s * 0.5f;
        s = 0.5f / s;
        
        x = (m[2][1] - m[1][2]) * s;
        y = (m[0][2] - m[2][0]) * s;
        z = (m[1][0] - m[0][1]) * s;
    }
    else
    {
        int i,j,k;
        float q[4];

        i = X;
        if (m[Y][Y] > m[X][X]) i = Y;
        if (m[Z][Z] > m[i][i]) i = Z;
        j = nxt[i]; 
        k = nxt[j];
        
        float s = Asura_Maths::Sqrt((m[i][i] - (m[j][j]+m[k][k])) + 1.0f);
        
        q[i] = s * 0.5f;
        s = 0.5f / s;
        q[W] = (m[k][j] - m[j][k]) * s;
        q[j] = (m[j][i] + m[i][j]) * s;
        q[k] = (m[k][i] + m[i][k]) * s;

        x = q[0];
        y = q[1];
        z = q[2];
        w = q[3];
    }
    
}

inline void Asura_Quat::ToEuler(Asura_Euler_3& xEuler) const
{
    // KJL - 14/05/2003 13:02:02 - quat to euler. This should return exactly the same results
    // as quat->matrix->euler, except where we have an ambiguity in the euler formulation and 
    // lose a degree of freedom ('gimbal lock' due to 90 degree rotations etc)

    float fXX = x*x;
    float fYY = y*y;
    float fZZ = z*z;

    float fXY = x*y;
    float fXZ = x*z;
    float fYZ = y*z;
    float fWX = w*x;
    float fWY = w*y;
    float fWZ = w*z;

    // KJL - 14/05/2003 12:54:43 - inverse sine is only defined for values in the range -1 to +1 
    // (simply because -1 <= sine(x) <= 1 for all x). At the -1 & +1 points the euler notation 
    // breaks down because there is an infinite number of solutions (gimbal lock etc)

    float fSineX = 2.0f*(fWX-fYZ);

    if (fSineX <= -1.0f)
    {
        xEuler.x = -Asura_Maths::Pi*0.5f;
    }
    else if (fSineX >= +1.0f)
    {
        xEuler.x = Asura_Maths::Pi*0.5f;
    }
    else
    {
        xEuler.x = Asura_Maths::ArcSine(fSineX);
    }

    // KJL - 14/05/2003 12:59:49 - arc tangent is well defined, so we'll let it look after itself

    xEuler.y = Asura_Maths::ArcTangent( 2.0F*(fXZ+fWY), 1.0F-2.0F*(fXX+fYY) );

    xEuler.z = Asura_Maths::ArcTangent( 2.0F*(fXY+fWZ), 1.0F-2.0F*(fXX+fZZ) );
}


inline void Asura_Quat::FromEuler(const Asura_Euler_3& e) 
{
//  float fCx = Asura_Maths::Cosine(e.x/2.0f);
//  float fCy = Asura_Maths::Cosine(e.y/2.0f);
//  float fCz = Asura_Maths::Cosine(e.z/2.0f);
//
//  // KJL - 14/05/2003 16:56:21 -  the sines below could be calculated from the cosines above
//  // by using sin^2+cos^2 == 1. Probably be faster on the PS2 which has a fast square root,
//  // may be faster on the PC also.
//  float fSx = Asura_Maths::Sine(e.x/2.0f);
//  float fSy = Asura_Maths::Sine(e.y/2.0f);
//  float fSz = Asura_Maths::Sine(e.z/2.0f);
    float fSx, fSy, fSz, fCx, fCy, fCz;
    Asura_PS2_Maths::SinCos2(e.x/2.0f, e.y/2.0f, fSx, fCx, fSy, fCy);
    Asura_PS2_Maths::SinCos(e.z/2.0f, fSz, fCz);

    // KJL - 14/05/2003 16:57:56 - you know your maths is right when the ocde makes pretty patterns
    w = fCx * fCy * fCz + fSx * fSy * fSz;
    x = fSx * fCy * fCz + fCx * fSy * fSz;
    y = fCx * fSy * fCz - fSx * fCy * fSz;
    z = fCx * fCy * fSz - fSx * fSy * fCz;
}


// creates a quaternion from an angle and axis of rotation
void Asura_Quat::FromRotation(Asura_Angle angle, const Asura_Vector_3 & axis)
{
    angle *= 0.5f;
//  float s = Asura_Maths::Sine(angle);
//  float c = Asura_Maths::Cosine(angle);
    float s, c;
    Asura_Maths::SinCos(angle, s, c);
    x = axis.x * s;
    y = axis.y * s;
    z = axis.z * s;
    w = c;
}

inline Asura_Vector_3 Asura_Quat::RotateVector(const Asura_Vector_3& p) const
{
    //-----------------------------------------------------
    // 
    // This function rotates a vector p by the quaternion q
    // The formula is:
    //                  p' = (q)(p)(q*)
    //
    // but we calculate it using a vector formula which is an
    // expansion of this that Kev worked out.
    // for q = (v,w)
    // v' = 2w(v^p) + (2(v.p)v + (w^2-v^2)p
    //
    //--------------------------------------------------------

    const Asura_Vector_3 v(x,y,z); // the vector part of q
//  Asura_Vector_3 result;
    Asura_Vector_3 section1, section2, section3;

    float vMag = v.Magnitude();

    section1 = v^p;
    section1 *= (2.0f*w);

    section2 = v;
    section2 *= (2.0f*(v*p));

    section3 = p;
    section3 *= (w*w - vMag*vMag);

    return section1+section2+section3;

//  p = result;
}

inline Asura_Quat Asura_Quat::operator *(const Asura_Quat& q) const
{
    Asura_Vector_3 v1(x,y,z);
    Asura_Vector_3 v2(q.x, q.y, q.z);
    Asura_Vector_3 temp = v1*q.w + v2*w + (v1^v2);

    return Asura_Quat(temp.x, temp.y, temp.z, w*q.w - v1*v2);
}

inline void Asura_Quat::Normalise(void)
{
    const float m = Asura_Maths::Sqrt((x*x)+(y*y)+(z*z)+(w*w));
    
    if (m > 0.0f)
	{
		const float rm = 1.0f/m;
		x *= rm;
		y *= rm;
		z *= rm;
		w *= rm;
	}

//    return m;
}

inline float Asura_Quat::Dot(const Asura_Quat& q2) const
{
    return x*q2.x+y*q2.y+z*q2.z+w*q2.w;
}

inline void Asura_Quat::Slerp(const Asura_Quat& input1, const Asura_Quat& input2, float l)
{
    Asura_PS2_Maths::Quat_Slerp(*this, input1, input2, l);
}


inline void Asura_Quat::NLerp( const Asura_Quat& xInput1, const Asura_Quat& xInput2, float fDist )
{
	// need cosine of angle between quats to determine shortest path. If cos is
	// negative, invert the second input.
	float fCosOmega = xInput1.Dot(xInput2);
	float fSign = fCosOmega >= 0.0f ? 1.0f : -1.0f;
	*this = xInput1 * (1.0f - fDist) + xInput2 * (fDist * fSign);
	this->Normalise();
}

//
// Asura_Comp_Quat.
//

inline void Asura_Comp_Quat::ToQuat( Asura_Quat& xQuat ) const
{
    Asura_PS2_Maths::CompQuat_ToQuat( this, xQuat );
}

inline void Asura_Comp_Quat::FromQuat( const Asura_Quat& xQuat )
{
    Asura_PS2_Maths::CompQuat_FromQuat( this, xQuat );
}

//
// Asura_Comp_Quat_Fast.
//

// JAT - 15/12/2005 PS2 version copied from the PC version
// KOR - 8/11/2005 - This is an implementation of the "smallest three" quaternion compression method
// detailed in Game Programming Gems 3. It appears to be just as accurate as the polar co-ordinate method,
// and much faster to decompress.
const float fSQRT_2 = (1.4142135623730950488016887242097f);
const float fONE_OVER_SQRT_2  = (1.f / fSQRT_2);

void Asura_Comp_Quat_Fast::ToQuat( Asura_Quat& xQuat ) const
{
	// Bottom two bits house the offset to the largest element (0 = w, 1 = x, 2 = y, 3 = z).
	const u_int uOffset = m_uBits & 0x03;
	// Extract the three smallest values.
	const float fValue0 = static_cast<float>( (m_uBits & 0xFFC00000) >> 22) * (fONE_OVER_SQRT_2 * 2.f / 1022.f) - fONE_OVER_SQRT_2;
	const float fValue1 = static_cast<float>( (m_uBits & 0x003FF000) >> 12) * (fONE_OVER_SQRT_2 * 2.f / 1022.f) - fONE_OVER_SQRT_2;
	const float fValue2 = static_cast<float>( (m_uBits & 0x00000FFC) >>  2) * (fONE_OVER_SQRT_2 * 2.f / 1022.f) - fONE_OVER_SQRT_2;
	// Calculate the largest value using the knowledge that the quat is normalised.
	const float fValue3Squared = 1.f - (fValue0 * fValue0) - (fValue1 * fValue1) - (fValue2 * fValue2);
	const float fValue3 = fValue3Squared > 0.f ? Asura_Maths::Sqrt(fValue3Squared) : 0.f;
	// Put the values in the correct places.
	float* const pfArray = reinterpret_cast<float*>(&xQuat);
	pfArray[(0 + uOffset) & 0x03] = fValue0;
	pfArray[(1 + uOffset) & 0x03] = fValue1;
	pfArray[(2 + uOffset) & 0x03] = fValue2;
	pfArray[(3 + uOffset) & 0x03] = fValue3;
}

// JAT - 15/12/2005 PS2 version copied from the PC version
void Asura_Comp_Quat_Fast::FromQuat( const Asura_Quat& xQuat )
{
#ifdef ASURA_ASSERT
	const float fEpsilon = 0.01f;
	const float fMagnitude = xQuat.MagnitudeSquared();
	Asura_Assert((1.f - fEpsilon) < fMagnitude && fMagnitude < (1.f + fEpsilon), "Quat must be normalised");
#endif
	// Find the offset of the largest value (0 = w, 1 = x, 2 = y, 3 = z). Slightly clunky way of doing it.
	Asura_Quat xAbsQuat(Asura_Maths::Abs(xQuat.x), Asura_Maths::Abs(xQuat.y), Asura_Maths::Abs(xQuat.z), Asura_Maths::Abs(xQuat.w));
	const float fLargestValue = Asura_Maths::Max(Asura_Maths::Max(xAbsQuat.x, xAbsQuat.y), Asura_Maths::Max(xAbsQuat.z, xAbsQuat.w));
	if(fLargestValue == xAbsQuat.x)			m_uBits = 1;
	else if(fLargestValue == xAbsQuat.y)	m_uBits = 2;
	else if(fLargestValue == xAbsQuat.z)	m_uBits = 3;
	else if(fLargestValue == xAbsQuat.w)	m_uBits = 0;
	// We store 0, 1, 2, and calculate 3 (the largest)
	float fValue0, fValue1, fValue2, fValue3;
	const float* pfArray = reinterpret_cast<const float*>(&xQuat);
	fValue0 = pfArray[(0 + m_uBits) & 0x03];
	fValue1 = pfArray[(1 + m_uBits) & 0x03];
	fValue2 = pfArray[(2 + m_uBits) & 0x03];
	fValue3 = pfArray[(3 + m_uBits) & 0x03];
	// If the largest is negative, we negate all the other elements using the fact that q = -q
	if(fValue3 < 0.f)
	{
		fValue0 = -fValue0;
		fValue1 = -fValue1;
		fValue2 = -fValue2;
	}
	// Pack the three smallest values into the remaining 30 bits (10 bits each)
	// Using a range of 1022 so that we can get exactly zero while allowing the maximum and minimum values
	// Regarding use of FloatToInt_Near, I tried doing a RMS comparison of either side of each float to int,
	// but it made no discernable difference, so I dropped it.
	m_uBits |= Asura_Maths::FloatToInt_Near( (fONE_OVER_SQRT_2 + fValue0) * (fSQRT_2 * 1022.f / 2.f) ) << 22;
	m_uBits |= Asura_Maths::FloatToInt_Near( (fONE_OVER_SQRT_2 + fValue1) * (fSQRT_2 * 1022.f / 2.f) ) << 12;
	m_uBits |= Asura_Maths::FloatToInt_Near( (fONE_OVER_SQRT_2 + fValue2) * (fSQRT_2 * 1022.f / 2.f) ) <<  2;
}


//
// Asura_Bounding_Rectangle.
//

inline void Asura_Bounding_Rectangle::Reset(void)
{
    // set to exaggeratedly large values
    MinX = Asura_Maths::LargeFloat;
    MinY = Asura_Maths::LargeFloat;

    MaxX = -Asura_Maths::LargeFloat;
    MaxY = -Asura_Maths::LargeFloat;
}

inline void Asura_Bounding_Rectangle::Set( const Asura_Vector_2& v )
{
    if (v.x > MaxX) MaxX = v.x;
    if (v.y > MaxY) MaxY = v.y;

    if (v.x < MinX) MinX = v.x;
    if (v.y < MinY) MinY = v.y;
}

inline bool Asura_Bounding_Rectangle::Intersects(const Asura_Bounding_Rectangle& xRect) const
{
    if ( ((MaxX >= xRect.MinX) && (MinX <= xRect.MaxX))
      && ((MaxY >= xRect.MinY) && (MinY <= xRect.MaxY)) )
    {
        return true;
    }
    else return false;
}

inline bool Asura_Bounding_Rectangle::Contains( const Asura_Vector_2& point ) const
{
    if ((point.x>=MinX && point.x<=MaxX)
      &&(point.y>=MinY && point.y<=MaxY))
    {
        return true;
    }
    return false;
}

inline Asura_Vector_2 Asura_Bounding_Rectangle::CalculateCentre(void) const
{
    return Asura_Vector_2( (MinX + MaxX) * 0.5f, (MinY + MaxY) * 0.5f );
}

inline void Asura_Bounding_Rectangle::Displace( const Asura_Vector_2& xDisp )
{
	MinX += xDisp.x; 
	MaxX += xDisp.x; 
	MinY += xDisp.y; 
	MaxY += xDisp.y;	
}

//
// Asura_Bounding_Box.
//

inline void Asura_Bounding_Box::Reset(void)
{
    // set to exaggeratedly large values
    MinX = Asura_Maths::LargeFloat;
    MinY = Asura_Maths::LargeFloat;
    MinZ = Asura_Maths::LargeFloat;

    MaxX = -Asura_Maths::LargeFloat;
    MaxY = -Asura_Maths::LargeFloat;
    MaxZ = -Asura_Maths::LargeFloat;
}

inline void Asura_Bounding_Box::Set(const class Asura_Vector_3& v)
{
    if (v.x > MaxX) MaxX = v.x;
    if (v.y > MaxY) MaxY = v.y;
    if (v.z > MaxZ) MaxZ = v.z;

    if (v.x < MinX) MinX = v.x;
    if (v.y < MinY) MinY = v.y;
    if (v.z < MinZ) MinZ = v.z;
}

inline void Asura_Bounding_Box::Set(const Asura_Bounding_Box& bb)
{
    if (bb.MaxX > MaxX) MaxX = bb.MaxX;
    if (bb.MaxY > MaxY) MaxY = bb.MaxY;
    if (bb.MaxZ > MaxZ) MaxZ = bb.MaxZ;

    if (bb.MinX < MinX) MinX = bb.MinX;
    if (bb.MinY < MinY) MinY = bb.MinY;
    if (bb.MinZ < MinZ) MinZ = bb.MinZ;
}

inline Asura_Vector_3 Asura_Bounding_Box::CalculateCentre(void) const
{
    return Asura_Vector_3( (MinX + MaxX) * 0.5f, (MinY + MaxY) * 0.5f, (MinZ + MaxZ) * 0.5f );
}

inline Asura_Vector_3 Asura_Bounding_Box::CalculateExtents( void ) const
{
    return Asura_Vector_3( MaxX - MinX, MaxY - MinY, MaxZ - MinZ );
}

inline float Asura_Bounding_Box::GetVolume(void) const 
{
    float x = MaxX-MinX;
    float y = MaxY-MinY;
    float z = MaxZ-MinZ;
    return x*y*z;
}

inline bool Asura_Bounding_Box::Intersects(const Asura_Bounding_Box& b) const
{
    if ( ((MaxX >= b.MinX) && (MinX <= b.MaxX))
      && ((MaxY >= b.MinY) && (MinY <= b.MaxY))
      && ((MaxZ >= b.MinZ) && (MinZ <= b.MaxZ)) )
    {
        return true;
    }
    else return false;
}


inline bool Asura_Bounding_Box::Intersects(const Asura_Bounding_Box& b, const Asura_Vector_3 & offset) const
{
    if ( ((MaxX >= b.MinX+offset.x) && (MinX <= b.MaxX+offset.x))
      && ((MaxY >= b.MinY+offset.y) && (MinY <= b.MaxY+offset.y))
      && ((MaxZ >= b.MinZ+offset.z) && (MinZ <= b.MaxZ+offset.z)) )
    {
        return true;
    }
    else return false;
}

inline bool Asura_Bounding_Box::IntersectsSphere(const Asura_Vector_3& centre, const float radius) const
{
    if ( ((MaxX>=centre.x-radius) && (MinX <= centre.x+radius))
      && ((MaxY>=centre.y-radius) && (MinY <= centre.y+radius))
      && ((MaxZ>=centre.z-radius) && (MinZ <= centre.z+radius)) )
    {
        return true;
    }
    else return false;
}

inline bool Asura_Bounding_Box::Contains(const Asura_Vector_3& point) const 
{
    if ((point.x>=MinX && point.x<=MaxX)
      &&(point.y>=MinY && point.y<=MaxY)
      &&(point.z>=MinZ && point.z<=MaxZ))
        return true;

    return false;

}

inline bool Asura_Bounding_Box::Contains(const Asura_Bounding_Box& b) const
{
    if ((b.MinX>=MinX && b.MaxX<=MaxX)
      &&(b.MinY>=MinY && b.MaxY<=MaxY)
      &&(b.MinZ>=MinZ && b.MaxZ<=MaxZ))
    {
        return true;
    }

    return false;
}

inline void Asura_Bounding_Box::Displace(const Asura_Vector_3& displacement)
{
    MaxX += displacement.x;
    MinX += displacement.x;
    MaxY += displacement.y;
    MinY += displacement.y;
    MaxZ += displacement.z;
    MinZ += displacement.z;
}

//
// Asura_Complex.
//

inline Asura_Complex& Asura_Complex::operator*=( const Asura_Complex& xOther )
{
    // (a + ib) * (c + id)
    float fAC = m_fR * xOther.m_fR;
    float fAD = m_fR * xOther.m_fI;
    float fBC = m_fI * xOther.m_fR;
    float fBD = m_fI * xOther.m_fI;
    m_fR = fAC - fBD;
    m_fI = fAD + fBC;
    return *this;
}

inline Asura_Complex& Asura_Complex::operator+=( const Asura_Complex& xOther )
{
    m_fR += xOther.m_fR;
    m_fI += xOther.m_fI;
    return *this;
}

inline Asura_Complex& Asura_Complex::operator-=( const Asura_Complex& xOther )
{
    m_fR -= xOther.m_fR;
    m_fI -= xOther.m_fI;
    return *this;
}

inline Asura_Complex Asura_Complex::operator*( const Asura_Complex& xOther ) const
{
    return Asura_Complex( m_fR * xOther.m_fR - m_fI * xOther.m_fI, m_fR * xOther.m_fI + m_fI * xOther.m_fR );
}

inline Asura_Complex Asura_Complex::operator+( const Asura_Complex& xOther ) const
{
    return Asura_Complex( m_fR + xOther.m_fR, m_fI + xOther.m_fI );
}

inline Asura_Complex Asura_Complex::operator-( const Asura_Complex& xOther ) const
{
    return Asura_Complex( m_fR - xOther.m_fR, m_fI - xOther.m_fI );
}

// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

#endif // #ifndef ___ASURA_PLATFORMMATHS_H___
