//////////////////////////////////////////////////////////////////////////////
//
// Asura_PSP_VectorMaths.h
//
// Copyright (c) 2005 Rebellion. All rights	reserved.
//
// Description:
//
//	   classes optimised for the PSP's VFPU. Included by Asura_PlatformMaths.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_PSP_VECTORMATHS_H___
#define	___ASURA_PSP_VECTORMATHS_H___ 1

// Asura Platform-independent
#include "Core/Asura_HalfFloat.h"

// Asura PSP
#include "Core/Asura_PlatformBasics.h"

#if defined __SNC__
struct SNPokeVectorData { float m_fX, m_fY, m_fZ; }
    __attribute__((aligned(16))) ;
struct SNPokeVectorDataArray { float m_afElement[3]; }
     __attribute__((aligned(16))) ;
#endif

// Asura_AVector3...
#define Asura_AVector3_InParam		const Asura_AVector3&
#define Asura_AVector3_OutParam		Asura_AVector3&
#define Asura_AVector3_InOutParam	Asura_AVector3&

	 // Asura_AMatrix3x3...
#define Asura_AMatrix3x3_InParam	const Asura_AMatrix3x3&
#define Asura_AMatrix3x3_OutParam	Asura_AMatrix3x3&
#define Asura_AMatrix3x3_InOutParam	Asura_AMatrix3x3&

// Asura_AVector4...
#define Asura_AVector4_InParam		const Asura_AVector4&
#define Asura_AVector4_OutParam		Asura_AVector4&
#define Asura_AVector4_InOutParam	Asura_AVector4&

// Asura_AMatrix4x4...
#define Asura_AMatrix4x4_InParam	const Asura_AMatrix4x4&
#define Asura_AMatrix4x4_OutParam	Asura_AMatrix4x4&
#define Asura_AMatrix4x4_InOutParam	Asura_AMatrix4x4&


/*
	Note: the '__asm__' style GCC inlines below are deprecated and
	eventually only the intrinsics version of the code will remain.
*/

class Asura_AVector3
{
private:
	friend void Asura_Quat::FromRotation(Asura_Angle fAngle,const Asura_AVector3& xAxis);
	friend void Asura_Vector_3::operator = ( const Asura_AVector3& xV4);
	friend Asura_Vector_3::Asura_Vector_3( const Asura_AVector3& xV4 );
	friend class Asura_AMatrix3x3;
	friend class Asura_SoA_Vector_3;

#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
	SNVectorType m_xVec;
#else
	union
	{
		struct
		{
			float m_fX;
			float m_fY;
			float m_fZ;
		};

		/*
          'm_fW' below is a dummy, to ensure asm inlines get the correct
          clobbers.
        */
		struct
		{
			float m_fX;
			float m_fY;
			float m_fZ;
			float m_fW;
		} m_xVec;

		vfpu_v4 m_afVec;
	} __attribute__((aligned(16)));
#endif

	public:

		Asura_AVector3()
		{
		}


		Asura_AVector3( const float fX, const float fY, const float fZ )
		{
        #if defined __SNC__
			SNPokeVectorData* const pxData
                = reinterpret_cast<SNPokeVectorData* const>(&m_xVec);

			pxData->m_fX = fX;
			pxData->m_fY = fY;
			pxData->m_fZ = fZ;
        #else
			m_fX = fX;
			m_fY = fY;
			m_fZ = fZ;
        #endif
		}

		explicit Asura_AVector3 ( const Asura_Vector_3& xVec3 )
		{
        #if defined __SNC__
			SNPokeVectorData* const pxData
                = reinterpret_cast<SNPokeVectorData* const>(&m_xVec);

			pxData->m_fX = xVec3.x;
			pxData->m_fY = xVec3.y;
			pxData->m_fZ = xVec3.z;
        #else
			m_fX = xVec3.x;
			m_fY = xVec3.y;
			m_fZ = xVec3.z;
        #endif
		}

		inline void	operator = ( const Asura_Vector_3& xVec3 ) 
		{
        #if defined __SNC__
			SNPokeVectorData* const pxData
                = reinterpret_cast<SNPokeVectorData* const>(&m_xVec);

			pxData->m_fX = xVec3.x;
			pxData->m_fY = xVec3.y;
			pxData->m_fZ = xVec3.z;
        #else
			m_fX = xVec3.x;
			m_fY = xVec3.y;
			m_fZ = xVec3.z;
        #endif
		}

		// TODO - use sub object access for these 4 functions rather
		// than going through memory!
		inline void	LoadAligned( const float* pfFloats )
		{
		#if	defined __SNC__
			SNPokeVectorData* pxData = (SNPokeVectorData*)&m_xVec;
			pxData->m_fX = pfFloats[0];
			pxData->m_fY = pfFloats[1];
			pxData->m_fZ = pfFloats[2];
		#else
			m_fX = pfFloats[0];
			m_fY = pfFloats[1];
			m_fZ = pfFloats[2];
		#endif
		}

		inline void	LoadUnaligned( const float*	pfFloats )
		{
		#if	defined __SNC__
			SNPokeVectorData* pxData = (SNPokeVectorData*)&m_xVec;
			pxData->m_fX = pfFloats[0];
			pxData->m_fY = pfFloats[1];
			pxData->m_fZ = pfFloats[2];
		#else
			m_fX = pfFloats[0];
			m_fY = pfFloats[1];
			m_fZ = pfFloats[2];
		#endif
		}

		inline void	StoreAligned( float* pfFloats )	const 
		{
		#if	defined __SNC__
			SNPokeVectorData* pxData = (SNPokeVectorData*)&m_xVec;
			pfFloats[0]	= pxData->m_fX;
			pfFloats[1]	= pxData->m_fY;
			pfFloats[2]	= pxData->m_fZ;
		#else
			pfFloats[0] = m_fX;
			pfFloats[1] = m_fY;
			pfFloats[2] = m_fZ;
		#endif
		}

		inline void	StoreUnaligned(	float* pfFloats	) const	
		{
		#if	defined __SNC__
			SNPokeVectorData* pxData = (SNPokeVectorData*)&m_xVec;
			pfFloats[0]	= pxData->m_fX;
			pfFloats[1]	= pxData->m_fY;
			pfFloats[2]	= pxData->m_fZ;
		#else
			pfFloats[0] = m_fX;
			pfFloats[1] = m_fY;
			pfFloats[2] = m_fZ;
		#endif
		}

		inline void	StoreAlignedHalf( Asura_HalfFloat* pxHalves )	const 
		{
		#if	defined __SNC__
			SNPokeVectorData* pxData = (SNPokeVectorData*)&m_xVec;
			pxHalves[0]	= pxData->m_fX;
			pxHalves[1]	= pxData->m_fY;
			pxHalves[2]	= pxData->m_fZ;
		#else
			pxHalves[0] = m_fX;
			pxHalves[1] = m_fY;
			pxHalves[2] = m_fZ;
		#endif
		}

		// assignment operators

		inline void operator +=(Asura_AVector3_InParam xV)
		{
        #if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			// note * suffix to indicate subobject mode i.e. we don't
			// care about w.
			_asm
            {
                vadd.t* m_xVec, m_xVec, xV.m_xVec
            };
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			#if defined ASURA_PSP_GCC_BUILTINS
                m_afVec = vfpu_vadd_t(m_afVec,xV.m_afVec,0);
            #else
                __asm__ (
                    "vadd.t %0, %0, %1"
                    : VFPU_PLS_C ((*this).m_afVec)
                    : VFPU_C (xV.m_afVec)
                );
            #endif
        #else
			m_fX += xV.m_fX;
			m_fY += xV.m_fY;
			m_fZ += xV.m_fZ;
        #endif
		}

		inline void operator -=(Asura_AVector3_InParam xV)
		{
        #if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm
            {
                vsub.t* m_xVec, m_xVec, xV.m_xVec
            };
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if defined ASURA_PSP_GCC_BUILTINS
                m_afVec = vfpu_vsub_t(m_afVec,xV.m_afVec,0);
            #else
                __asm__ (
                    "vsub.t %0, %0, %1"
                    : VFPU_PLS_C ((*this).m_afVec)
                    : VFPU_C (xV.m_afVec)
                );
            #endif
        #else
			m_fX -= xV.m_fX;
			m_fY -= xV.m_fY;
			m_fZ -= xV.m_fZ;
        #endif
		}

		inline void operator *=(const float f)
		{
        #if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			SNScalarType xTemp = __builtin_ftovs( f );

			_asm
            {
                 vscl.t* m_xVec, m_xVec, xTemp
            };
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if defined ASURA_PSP_GCC_BUILTINS
                const vfpu_v4 afT = vfpu_v4_q(f);
                m_afVec = vfpu_vmul_t(m_afVec,afT,0);
            #else
                vfpu_v4 afT;

                __asm__ (
                    "mtv %2, %x1\n\t"
                    "vmul.t %0, %0, %1[X,X,X]"
                    : VFPU_PLS_C ((*this).m_afVec), VFPU_EQL_C (afT)
                    : "r" (f)
                );
            #endif
        #else
			m_fX *= f;
			m_fY *= f;
			m_fZ *= f;
        #endif
		}

		inline void operator /=(const float f)
		{
        #if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			SNScalarType xTemp = __builtin_ftovs( f );

			_asm
			{
				vrcp.s* xTemp, xTemp
				vscl.t* m_xVec, m_xVec, xTemp
			};
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if defined ASURA_PSP_GCC_BUILTINS
                vfpu_v4 afT = vfpu_v4_s(f);
				afT = vfpu_v4s_inv(afT);
                m_afVec = vfpu_vscl_t(m_afVec,afT,0);
            #else
                vfpu_v4 afInv;

                __asm__ (
                    "mtv %2, %x1\n\t"
                    "vrcp.s %x1, %x1\n\t"
                    "vmul.t %0, %0, %1[X,X,X]"
                    : VFPU_PLS_C ((*this).m_afVec), VFPU_EQL_C (afInv)
                    : "r" (f)
                );
            #endif
        #else
            const float fInv = 1.0f / f;

			m_fX *= fInv;
			m_fY *= fInv;
			m_fZ *= fInv;
        #endif
		}

		inline void operator *=(Asura_AVector3_InParam xV) 
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm 
			{
				vmul.t*	m_xVec,	m_xVec,	xV.m_xVec
			};
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if defined ASURA_PSP_GCC_BUILTINS
                m_afVec = vfpu_vmul_t(m_afVec,xV.m_afVec,0);
            #else
				__asm__
				(
					"vmul.t	%0, %0,	%1 \n"
					: VFPU_PLS_C ((*this).m_xVec)
					: VFPU_C (xV.m_xVec)
				);
			#endif
		#else
			m_fX *=	xV.m_fX;
			m_fY *=	xV.m_fY;
			m_fZ *=	xV.m_fZ;
		#endif
		}

		/*
			Unary operators
		*/

		inline Asura_AVector3 operator -() const
		{
        #if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			Asura_AVector3 xVec;

			_asm
            {
                vneg.t* xVec.m_xVec, m_xVec
            };

			return xVec;
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			Asura_AVector3 xVR;

            #if defined ASURA_PSP_GCC_BUILTINS
                xVR.m_afVec = vfpu_vneg_t(m_afVec,0);
            #else
                __asm__ (
                    "vneg.t %0, %1"
                    : VFPU_PLS_C (xVR.m_afVec)
                    : VFPU_C ((*this).m_afVec)
                );
            #endif

			return xVR;
        #else
			return Asura_AVector3(-m_fX, -m_fY, -m_fZ);
        #endif
		}

        /*
            Binary additive operators
        */

		inline Asura_AVector3 operator +(Asura_AVector3_InParam xV) const
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			Asura_AVector3 xVec;

			_asm
            {
                vadd.t* xVec.m_xVec, xV.m_xVec, m_xVec
            };

			return xVec;
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			Asura_AVector3 xVR;

            #if defined ASURA_PSP_GCC_BUILTINS
                xVR.m_afVec = this->m_afVec + xV.m_afVec;
            #else
                __asm__ (
                    "vadd.t %0, %1, %2"
                    : VFPU_EQL_C (xVR.m_afVec)
                    : VFPU_C ((*this).m_afVec), VFPU_C (xV.m_afVec)
                );
            #endif

			return xVR;
		#else
			return Asura_AVector3(m_fX + xV.m_fX,
                m_fY + xV.m_fY, m_fZ + xV.m_fZ);
		#endif
		}

		inline Asura_AVector3 operator -(Asura_AVector3_InParam xV) const
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			Asura_AVector3 xVec;
			_asm
            {
                vsub.t* xVec.m_xVec, m_xVec, xV.m_xVec
            };
			return xVec;
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			Asura_AVector3 xVR;

            #if defined ASURA_PSP_GCC_BUILTINS
                xVR.m_afVec = this->m_afVec - xV.m_afVec;
            #else
                __asm__ (
                    "vsub.t %0, %1, %2"
                    : VFPU_EQL_C (xVR.m_afVec)
                    : VFPU_C ((*this).m_afVec), VFPU_C (xV.m_afVec)
                );
            #endif

			return xVR;
		#else
			return Asura_AVector3(m_fX - xV.m_fX,
                m_fY - xV.m_fY, m_fZ - xV.m_fZ );
		#endif
		}

        /*
            Binary multiplicative operators
        */

		inline Asura_AVector3 operator *(const float f) const
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			Asura_AVector3 xVec;
			SNScalarType xTemp = __builtin_ftovs( f );

			_asm
			{
				vscl.t* xVec.m_xVec, m_xVec, xTemp
			};

			return xVec;
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			Asura_AVector3 xVR;

            #if defined ASURA_PSP_GCC_BUILTINS
                xVR.m_afVec = vfpu_vscl_t(m_afVec,vfpu_v4_q(f),VFPU_VS_X);
            #else
                vfpu_v4 afT;

                __asm__ (
                    "mtv %3, %x1 \n\t"
                    "vmul.t %0, %2, %1[X,X,X]"
                    : VFPU_EQL_C (xVR.m_afVec), VFPU_EQL_C (afT)
                    : VFPU_C ((*this).m_afVec), "r" (f)
                );
            #endif

			return xVR;
		#else
			return Asura_AVector3(m_fX * f, m_fY * f, m_fZ * f );
		#endif
		}

		inline Asura_AVector3 operator /(const float f) const
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			Asura_AVector3 xVec;
			SNScalarType xTemp = __builtin_ftovs( f	);

			_asm
			{
				vrcp.s* xTemp, xTemp
				vscl.t* xVec.m_xVec, m_xVec, xTemp
			};

			return xVec;
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			Asura_AVector3 xVR;

            #if defined ASURA_PSP_GCC_BUILTINS
                /*
                    Here we use the 'vfpu_v4::operator /' instead of
                    'vfpu_vdiv_t' because the operator expands to either
                    'vdiv' or 'vrcp'+'vscl' depending on the setting of
                    '-mfast-vfpu-math', and that feels nicer.
                */
                const vfpu_v4 afT = vfpu_v4_q(f);
                xVR.m_afVec = m_afVec / afT;
            #else
                vfpu_v4 afInv;

                __asm__ (
                    "mtv %3, %x1 \n\t"
                    "vrcp.s %x1, %x1 \n\t"
                    "vmul.t %0, %2, %1[X,X,X]"
                    : VFPU_EQL_C (xVR.m_afVec), VFPU_EQL_C (afInv)
                    : VFPU_C ((*this).m_afVec), "r" (f)
                );
            #endif

			return xVR;
		#else
			const float fInv = 1.0f / f;
			return Asura_AVector3(m_fX * fInv, m_fY * fInv, m_fZ * fInv );
		#endif
		}

		inline Asura_AVector3 operator ^(Asura_AVector3_InParam xV) const
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			Asura_AVector3 xRet;
            #if 0
                // SNC bug? end up with overlapping registers when passed by
                // reference.
                _asm
                {
                    vcrsp.t* xRet.m_xVec, m_xVec, xV.m_xVec
                };
            #else
                const SNPokeVectorData* const pxD1 = reinterpret_cast
					<const SNPokeVectorData* const>(&m_xVec);
                const SNPokeVectorData* const pxD2 = reinterpret_cast
					<const SNPokeVectorData* const>(&xV.m_xVec);

                return Asura_AVector3
                    (pxD1->m_fY*pxD2->m_fZ - pxD1->m_fZ*pxD2->m_fY,
                     pxD1->m_fZ*pxD2->m_fX - pxD1->m_fX*pxD2->m_fZ,
                     pxD1->m_fX*pxD2->m_fY - pxD1->m_fY*pxD2->m_fX);
            #endif
			return xRet;
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			Asura_AVector3 xVR;

            #if defined ASURA_PSP_GCC_BUILTINS
                xVR.m_afVec = vfpu_vcrsp_t(this->m_afVec,xV.m_afVec,0);
            #else
                __asm__ (
                    "vcrsp.t %0, %1, %2"
                    : VFPU_AND_C (xVR.m_afVec)
                    : VFPU_C ((*this).m_afVec), VFPU_C (xV.m_afVec)
                );
            #endif

			return xVR;
		#else
			return Asura_AVector3(m_fY*xV.m_fZ - m_fZ*xV.m_fY,
                m_fZ*xV.m_fX - m_fX*xV.m_fZ, m_fX*xV.m_fY - m_fY*xV.m_fX );
		#endif
		}

		inline float operator *(Asura_AVector3_InParam xV) const
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			SNScalarType xRet;

			_asm
			{
				vdot.t* xRet, m_xVec, xV.m_xVec
				//sv.s xRet, 0(&f)
			};

			return __builtin_vstof(xRet);
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if defined ASURA_PSP_GCC_BUILTINS
                return vfpu_extract_float_from_v4x
					(vfpu_vdot_t(this->m_afVec,xV.m_afVec,0));
            #else
                vfpu_v4 afT;
                float fR;

                __asm__ (
                    "vdot.t %x0, %2, %3 \n\t"
                    "mfv  %1, %x0"
                    : VFPU_EQL_C (afT), "=r" (fR)
                    : VFPU_C ((*this).m_afVec), VFPU_C (xV.m_afVec)
                );

                return fR;
            #endif
		#else
			return (m_fX*xV.m_fX + m_fY*xV.m_fY + m_fZ*xV.m_fZ);
		#endif
		}

		#if 0
		inline Asura_AVector3 operator *(const Asura_Matrix_3x3& xM) const
		{
			class Asura_AVector3 result;

			result.m_fX = xM.e11 * m_fX + xM.e12 * m_fY + xM.e13 * m_fZ;
			result.m_fY = xM.e21 * m_fX + xM.e22 * m_fY + xM.e23 * m_fZ;
			result.m_fZ = xM.e31 * m_fX + xM.e32 * m_fY + xM.e33 * m_fZ;

			return result;
		}
		#endif

		inline Asura_AVector3 operator *(Asura_AMatrix3x3_InParam xMat) const;

		// hack	- aligned vector * non aligned matrix
		inline Asura_AVector3 operator *(const Asura_Matrix_3x3& xMat) const;

		inline bool operator ==(Asura_AVector3_InParam xV) const
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			int iRet;

			_asm
			{
				// note the limitation on using C/C++ variable names in
				// asm blocks - you can't use them where there is "flow"
				// e.g. in this case we branch to label 0. Use $t0 to
				// workaround this.
				li $t0, 0

				vcmp.t* EQ, m_xVec, xV.m_xVec
				bvf 5, 0f	// bit 5 of CC is x1==x2 && y1==y2 && z1==z2

				li $t0, 1

			0:
				move iRet, $t0
			};

			return iRet == 1;
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if 1 || defined ASURA_PSP_GCC_BUILTINS
                /*
                    Some version of the compiler generate the wrong code
                    for 'vfpu_v4::operator !=' (and presumably also for
                    'operator =='; the builtin is the recommended
                    workaround.
                */
                return vfpu_vcmp_t(VFPU_EQ,this->m_afVec,xV.m_afVec,0) != 0;
            #else
                unsigned int iR;
                vfpu_v4 afT;

                __asm__ (
                    /* Not quite right yet */
                    "vzero.q %1 \n\t"
                    "vcmp.t NE, %2, %3 \n\t"
                    "vcmovt.s %x1, %x1[1], 4 \n\t"
                    "mfv %0, %x1"

                    : "=r" (iR), VFPU_EQL_C (afT)
                    : VFPU_C ((*this).m_afVec), VFPU_C (xV.m_afVec)
                );

                return iR != 0;
            #endif
		#else
			return m_fX == xV.m_fX && m_fY == xV.m_fY && m_fZ == xV.m_fZ;
		#endif
		}

		inline bool operator !=(Asura_AVector3_InParam xV) const
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			int iRet;

			_asm
			{
				// note the limitation on using C/C++ variable names in
				// asm blocks - you can't use them where there is "flow"
				// e.g. in this case we branch to label 0. Use $t0 to
				// workaround this.
				li $t0, 0

				vcmp.t* NE, m_xVec, xV.m_xVec
				bvf 4, 0f	// bit 4 of CC is x1!=x2 || y1!=y2 || z1!=z2

				li $t0, 1

			0:
				move iRet, $t0
			};
			return iRet == 1;
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if 1 || defined ASURA_PSP_GCC_BUILTINS
                /*
                    Some version of the compiler generate the wrong
                    code for 'vfpu_v4::operator ==', the builtin is
                    the recommended workaround.
                */
                return vfpu_vcmp_t(VFPU_NE,this->m_afVec,xV.m_afVec,0) != 0;
            #else
                unsigned int iR;
                vfpu_v4 afT;

                __asm__ (
                    /* Not quite right yet */
                    "vone.q %1 \n"
                    "vcmp.t NE, %2, %3 \n"
                    "vcmovt.s %x1, %x1[0], 4 \n"
                    "mfv %0, %x1 \n"

                    : "=r" (iR), VFPU_PLS_C (afT)
                    : VFPU_C ((*this).m_afVec), VFPU_C (xV.m_afVec)
                );

                return iR != 0;
            #endif
		#else
			return m_fX != xV.m_fX || m_fY != xV.m_fY || m_fZ != xV.m_fZ;
		#endif
		}

		inline bool ApproximatelyEquals(const Asura_AVector3& xV,
            const float epsilon = 1.0e-6f) const
		{
			Asura_AVector3 xDelta(*this - xV);
			return xDelta.MagnitudeSquared() < epsilon;
		}

#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
		inline bool	operator > ( Asura_AVector3_InParam v) const
		{
			int	iRet;
			_asm
			{
				// note	the	limitation on using	C/C++ variable names in	asm
				// blocks -	you	can't use them where there is "flow" e.g. in 
				// this	case we	branch to label	0. Use $t0 to workaround this.
				li $t0,	0
				vcmp.t*	GT,	m_xVec,	v.m_xVec
				bvf	5, 0f	// bit 5 of	CC is x1>x2	&& y1>y2 &&	z1>z2
				li $t0,	1
				0:
				move iRet, $t0
			};
			return iRet	== 1;
		}

		inline bool	operator < ( Asura_AVector3_InParam v) const
		{
			int	iRet;
			_asm
			{
				// note	the	limitation on using	C/C++ variable names in	asm
				// blocks -	you	can't use them where there is "flow" e.g. in 
				// this	case we	branch to label	0. Use $t0 to workaround this.
				li $t0,	0
				vcmp.t*	LT,	m_xVec,	v.m_xVec
				bvf	5, 0f	// bit 5 of	CC is x1<x2	&& y1<y2 &&	z1<z2
				li $t0,	1
				0:
				move iRet, $t0
			};
			return iRet	== 1;
		}

		inline bool	operator >=	( Asura_AVector3_InParam	v) const
		{
			int	iRet;
			_asm
			{
				// note	the	limitation on using	C/C++ variable names in	asm
				// blocks -	you	can't use them where there is "flow" e.g. in 
				// this	case we	branch to label	0. Use $t0 to workaround this.
				li $t0,	0
				vcmp.t*	GE,	m_xVec,	v.m_xVec
				bvf	5, 0f	// bit 5 of	CC is x1>=x2 &&	y1>=y2 && z1>=z2
				li $t0,	1
				0:
				move iRet, $t0
			};
			return iRet	== 1;
		}

		inline bool	operator <=	( Asura_AVector3_InParam	v) const
		{
			int	iRet;
			_asm
			{
				// note	the	limitation on using	C/C++ variable names in	asm
				// blocks -	you	can't use them where there is "flow" e.g. in 
				// this	case we	branch to label	0. Use $t0 to workaround this.
				li $t0,	0
				vcmp.t*	LE,	m_xVec,	v.m_xVec
				bvf	5, 0f	// bit 5 of	CC is x1<=x2 &&	y1<=y2 && z1<=z2
				li $t0,	1
				0:
				move iRet, $t0
			};
			return iRet	== 1;
		}
#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER && defined ASURA_PSP_GCC_BUILTINS
		inline bool	operator >(Asura_AVector3_InParam xV) const
		{
			return vfpu_vcmp_t(VFPU_GT,this->m_afVec,xV.m_afVec,0) != 0;
		}

		inline bool	operator <(Asura_AVector3_InParam xV) const
		{
			return vfpu_vcmp_t(VFPU_LT,this->m_afVec,xV.m_afVec,0) != 0;
		}

		inline bool	operator >=(Asura_AVector3_InParam xV) const
		{
			return vfpu_vcmp_t(VFPU_GE,this->m_afVec,xV.m_afVec,0) != 0;
		}

		inline bool	operator <=(Asura_AVector3_InParam xV) const
		{
			return vfpu_vcmp_t(VFPU_LE,this->m_afVec,xV.m_afVec,0) != 0;
		}
#else
		inline bool	operator > ( Asura_AVector3_InParam xV) const
		{
			return (m_fX > xV.m_fX) && (m_fY > xV.m_fY) && (m_fZ > xV.m_fZ);
		}

		inline bool	operator < ( Asura_AVector3_InParam xV) const
		{
			return (m_fX < xV.m_fX) && (m_fY < xV.m_fY) && (m_fZ < xV.m_fZ);
		}

		inline bool	operator >=	( Asura_AVector3_InParam xV) const
		{
			return (m_fX >= xV.m_fX) && (m_fY >= xV.m_fY) && (m_fZ >= xV.m_fZ);
		}

		inline bool	operator <=	( Asura_AVector3_InParam xV) const
		{
			return (m_fX <= xV.m_fX) && (m_fY <= xV.m_fY) && (m_fZ <= xV.m_fZ);
		}
#endif

		inline float MagnitudeSquared(void)	const
		{	
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			SNScalarType xTemp;

			_asm
			{
				vdot.t*	xTemp, m_xVec, m_xVec
				//sv.s xTemp, 0(&fRet)
			};

			return __builtin_vstof(xTemp);
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if defined ASURA_PSP_GCC_BUILTINS
                return vfpu_extract_float_from_v4x
					(vfpu_vdot_t(this->m_afVec,this->m_afVec,0));
            #else
                vfpu_v4 afT;
                float fR;

                __asm__ (
                    "vdot.t %x0, %2, %2 \n\t"
                    "mfv %1, %x0"
                    : VFPU_EQL_C (afT), "=r" (fR)
                    : VFPU_C ((*this).m_afVec)
                );

                return fR;
			#endif
		#else
			return ((m_fX*m_fX) + (m_fY*m_fY) + (m_fZ*m_fZ));
		#endif
		}

		inline float Magnitude(void) const
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			SNScalarType xTemp;
			_asm
			{
				vdot.t*	xTemp, m_xVec, m_xVec
				vsqrt.s* xTemp,	xTemp
			};
			return __builtin_vstof(xTemp);
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if defined ASURA_PSP_GCC_BUILTINS
                return vfpu_extract_float_from_v4x
					(vfpu_vsqrt_s
						(vfpu_vdot_t(this->m_afVec,this->m_afVec,0),0));
			#else
                vfpu_v4 afT;
				float fR;

				__asm__ (
					"vdot.t %x0, %1, %1 \n\t"
					"vsqrt.s %x0, %x0 \n\t"
					"mfv %1, %x0"

					: VFPU_EQL_C (afT), "=r" (fR)
					: VFPU_C ((*this).m_xVec)
				);
				return fR;
			#endif
		#else
			return Asura_Maths::Sqrt((m_fX*m_fX) + (m_fY*m_fY) + (m_fZ*m_fZ));
		#endif
		}

		inline void	Normalise(void)
		{
			Asura_Assert(MagnitudeSquared() != 0.0f,
                "trying to Normalise Zero vector");

		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			SNScalarType xTemp;

			_asm
			{
				vdot.t* xTemp, m_xVec, m_xVec
				vrsq.s* xTemp, xTemp
				vscl.t* m_xVec, m_xVec, xTemp
			};
		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if defined ASURA_PSP_GCC_BUILTINS
				this->m_afVec = vfpu_vscl_t(this->m_afVec,
					vfpu_v4s_inv(vfpu_vsqrt_s
						(vfpu_vdot_t(this->m_afVec,this->m_afVec,0),0)),0);
							
			#else
				vfpu_v4 afT;

				__asm__
				(
					"vdot.t %1, %0, %0 \n\t"
					"vsqrt.s %1, %1 \n\t"
					"vrcp.s	 %1, %1 \n\t"
					"vscl.t %0, %0, %x1"

					: VFPU_PLS_C ((*this).m_xVec), VFPU_EQL_C (afT)
				);
			#endif
		#else
			const float fInvMag = 1.0f/Magnitude();

			m_fX *= fInvMag;
			m_fY *= fInvMag;
			m_fZ *= fInvMag;
		#endif
		}

		inline float NormaliseReturningMagnitude(void)
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX

			// test if magnitude is zero (true iff vector is zero)
			int iIsZero;
			_asm
			{
				// note the limitation on using C/C++ variable names in
				// asm blocks - you can't use them where there is "flow"
				// e.g. in this case we branch to label 0. Use $t0 to
				// workaround this.
				li $t0, 0

				vcmp.t* EZ, m_xVec
				bvf 5, 0f	// bit 5 of CC is x1==x2 && y1==y2 && z1==z2

				li $t0, 1

			0:
				move iIsZero, $t0
			};

			if (iIsZero)
			{
				return 0.0f;
			}

			SNScalarType xMag;
			SNScalarType xInvMag;

			_asm
			{
				vdot.t*	xMag, m_xVec, m_xVec
				vsqrt.s* xMag,	xMag
				vrcp.s xInvMag, xMag
				vscl.t*	m_xVec,	m_xVec,	xInvMag
			};

			return __builtin_vstof(xMag);

		#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if defined ASURA_PSP_GCC_BUILTINS
				const vfpu_v4 afMag = vfpu_vsqrt_s
						(vfpu_vdot_t(this->m_afVec,this->m_afVec,0),0);

				/* Iff magnitude nonzero, scale vector by it */
				if (vfpu_vcmp_s(VFPU_NZ,afMag,afMag,0) != 0)
				{
					this->m_afVec = vfpu_vscl_t
						(this->m_afVec,vfpu_v4s_inv(afMag),0);
				}

				return vfpu_extract_float_from_v4x(afMag);
			#else
				vfpu_v4 afMag;
				vfpu_v4 afInvMag;
				float fR;

				__asm__ (
					"vdot.t %x2, %0, %0 \n\t"
					"vsqrt.s %x2, %x2 \n\t"

					"vcmp.s EZ, %x2 \n\t"
					"bvt 0, 0f \n\t"

					"  vrcp.s %x3, %x2 \n\t"
					"  vscl.t %0, %0, %x3\n\t"

				"0: \n\t"
					"mfv %1, %x2"

					: VFPU_PLS_C ((*this).m_xVec), "=r" (fR),
					  VFPU_EQL_C (afMag), VFPU_EQL_C (afInvMag)
				);

				return fR;
			#endif
		#else
			register const float fMag = Magnitude();

			if (fMag == 0.0f)
            {
                return fMag;
            }

			register const float fInvMag = 1.0f/fMag;

			m_fX *=	fInvMag;
			m_fY *=	fInvMag;
			m_fZ *=	fInvMag;

			return fMag;
		#endif
		}

		inline void Lerp(Asura_AVector3_InParam xTo, const float fWeight)
		{
			// RJT TODO:
			*this = ( *this * ( 1.0f - fWeight ) ) + ( xTo * fWeight );
		}

		inline void Lerp(Asura_AVector3_InParam xFrom, Asura_AVector3_InParam xTo, const float fWeight)
		{
			// RJT TODO:
			*this = ( xFrom * ( 1.0f - fWeight ) ) + ( xTo * fWeight );
		}

		inline void Max(Asura_AVector3_InParam xV1, Asura_AVector3_InParam xV2)
		{
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm {
				vmax.t*		m_xVec, xV1.m_xVec, xV2.m_xVec
			};
#elif ASURA_PSP_ASM_USE_SN_SYNTAX

#elif ASURA_PSP_GCC_BUILTINS
			m_afVec = vfpu_vmax_t( xV1.m_afVec, xV2.m_afVec, 0 );
#else
			m_fX = ( xV2.m_fX > xV1.m_fX ) ? xV2.m_fX : xV1.m_fX;
			m_fY = ( xV2.m_fY > xV1.m_fY ) ? xV2.m_fY : xV1.m_fY;
			m_fZ = ( xV2.m_fZ > xV1.m_fZ ) ? xV2.m_fZ : xV1.m_fZ;
#endif	
		}

		inline void Min(Asura_AVector3_InParam xV1, Asura_AVector3_InParam xV2)
		{
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm {
				vmin.t*		m_xVec, xV1.m_xVec, xV2.m_xVec
			};
#elif ASURA_PSP_GCC_BUILTINS
			m_afVec = vfpu_vmin_t( xV1.m_afVec, xV2.m_afVec, 0 );
#else
			m_fX = ( xV2.m_fX < xV1.m_fX ) ? xV2.m_fX : xV1.m_fX;
			m_fY = ( xV2.m_fY < xV1.m_fY ) ? xV2.m_fY : xV1.m_fY;
			m_fZ = ( xV2.m_fZ < xV1.m_fZ ) ? xV2.m_fZ : xV1.m_fZ;
#endif	
		}

		inline void Zero(void)
		{
        #if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm
            {
                vzero.t* m_xVec
            };
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
            #if defined ASURA_PSP_GCC_BUILTINS
                m_afVec = vfpu_vzero_t(0);
            #else
                __asm__
				(
                   "vzero.t %0"
                   : VFPU_EQL_C ((*this).m_afVec)
                );
            #endif
        #else
			m_fX = m_fY = m_fZ = 0.0f;
        #endif
		}

		/*
			Compare *elementwise* 'xLHS' and 'xRHS', and for each
			element where the comparison is true return the
			corresponding elements of 'xTrue', else that of 'xFalse'.

			Therefore 'SelectLT(a,b,a,b)' is then elementwise
			minimum, 'SelectGT(a,b,a,b)' is elementwise maximum.

			In the assembler inlines below there is one vital, not
			very well documented, detail: condition specifier 6
			means elementwise move.

			In the GCC case the assembler inline should be done only
			with '__asm__', not with VFPU builtins, to get better
			'vcmp' efficiency (but perhaps the compiler is really
			really clever).
		*/

		inline void SelectGE(
			register Asura_AVector3_InParam xLHS,register Asura_AVector3_InParam xRHS,
			register Asura_AVector3_InParam xTrue,register Asura_AVector3_InParam xFalse
		)
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm
			{ 
				vcmp.t*		GE,	xLHS.m_xVec, xRHS.m_xVec
				vomov.t*	m_xVec, xTrue.m_xVec
				vcmovf.t*	m_xVec, xFalse.m_xVec, 6
			};
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			__asm__
			(
				ASURA_PSP_ASM_NRBEGIN
				"vcmp.t		GE, %1, %2 \n\t"
				"vmov.t		%0, %3 \n\t"
				"vcmovf.t	%0, %4, 6 \n\t"
				ASURA_PSP_ASM_NREND
				: VFPU_AND_C (m_afVec)
				: VFPU_C (xLHS.m_afVec), VFPU_C (xRHS.m_afVec),
				  VFPU_C (xTrue.m_afVec), VFPU_C (xFalse.m_afVec)
			);
		#else
			m_fX = xLHS.m_fX >= xRHS.m_fX ? xTrue.m_fX : xFalse.m_fX;
			m_fY = xLHS.m_fY >= xRHS.m_fY ? xTrue.m_fY : xFalse.m_fY;
			m_fZ = xLHS.m_fZ >= xRHS.m_fZ ? xTrue.m_fZ : xFalse.m_fZ;
		#endif
		}

		inline void SelectGT(
			register Asura_AVector3_InParam xLHS,register Asura_AVector3_InParam xRHS,
			register Asura_AVector3_InParam xTrue,register Asura_AVector3_InParam xFalse
		)
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm
			{ 
				vcmp.t*		GT, xLHS.m_xVec, xRHS.m_xVec
				vomov.t*	m_xVec, xTrue.m_xVec
				vcmovf.t*	m_xVec, xFalse.m_xVec, 6
			};
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			__asm__
			(
				ASURA_PSP_ASM_NRBEGIN
				"vcmp.t		GT, %1, %2 \n\t"
				"vmov.t		%0, %3 \n\t"
				"vcmovf.t	%0, %4, 6 \n\t"
				ASURA_PSP_ASM_NREND
				: VFPU_AND_C (m_afVec)
				: VFPU_C (xLHS.m_afVec), VFPU_C (xRHS.m_afVec),
				  VFPU_C (xTrue.m_afVec), VFPU_C (xFalse.m_afVec)
			);
		#else
			m_fX = xLHS.m_fX > xRHS.m_fX ? xTrue.m_fX : xFalse.m_fX;
			m_fY = xLHS.m_fY > xRHS.m_fY ? xTrue.m_fY : xFalse.m_fY;
			m_fZ = xLHS.m_fZ > xRHS.m_fZ ? xTrue.m_fZ : xFalse.m_fZ;
		#endif
		}

		inline void SelectLE(
			register Asura_AVector3_InParam xLHS,register Asura_AVector3_InParam xRHS,
			register Asura_AVector3_InParam xTrue,register Asura_AVector3_InParam xFalse
		)
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm
			{ 
				vcmp.t*		LE, xLHS.m_xVec, xRHS.m_xVec
				vomov.t*	m_xVec, xTrue.m_xVec
				vcmovf.t*	m_xVec, xFalse.m_xVec, 6
			};
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			__asm__
			(
				ASURA_PSP_ASM_NRBEGIN
				"vcmp.t		LE, %1, %2 \n\t"
				"vmov.t		%0, %3 \n\t"
				"vcmovf.t	%0, %4, 6 \n\t"
				ASURA_PSP_ASM_NREND
				: VFPU_AND_C (m_afVec)
				: VFPU_C (xLHS.m_afVec), VFPU_C (xRHS.m_afVec),
				  VFPU_C (xTrue.m_afVec), VFPU_C (xFalse.m_afVec)
			);
		#else
			m_fX = xLHS.m_fX <= xRHS.m_fX ? xTrue.m_fX : xFalse.m_fX;
			m_fY = xLHS.m_fY <= xRHS.m_fY ? xTrue.m_fY : xFalse.m_fY;
			m_fZ = xLHS.m_fZ <= xRHS.m_fZ ? xTrue.m_fZ : xFalse.m_fZ;
		#endif
		}

		inline void SelectLT(
			register Asura_AVector3_InParam xLHS,register Asura_AVector3_InParam xRHS,
			register Asura_AVector3_InParam xTrue,register Asura_AVector3_InParam xFalse
		)
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm
			{ 
				vcmp.t*		LT, xLHS.m_xVec, xRHS.m_xVec
				vomov.t*	m_xVec, xTrue.m_xVec
				vcmovf.t*	m_xVec, xFalse.m_xVec, 6
			};
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			__asm__
			(
				ASURA_PSP_ASM_NRBEGIN
				"vcmp.t		LT, %1, %2 \n\t"
				"vmov.t		%0, %3 \n\t"
				"vcmovf.t	%0, %4, 6 \n\t"
				ASURA_PSP_ASM_NREND
				: VFPU_AND_C (m_afVec)
				: VFPU_C (xLHS.m_afVec), VFPU_C (xRHS.m_afVec),
				  VFPU_C (xTrue.m_afVec), VFPU_C (xFalse.m_afVec)
			);
		#else
			m_fX = xLHS.m_fX < xRHS.m_fX ? xTrue.m_fX : xFalse.m_fX;
			m_fY = xLHS.m_fY < xRHS.m_fY ? xTrue.m_fY : xFalse.m_fY;
			m_fZ = xLHS.m_fZ < xRHS.m_fZ ? xTrue.m_fZ : xFalse.m_fZ;
		#endif
		}

		inline void SelectEQ(
			register Asura_AVector3_InParam xLHS,register Asura_AVector3_InParam xRHS,
			register Asura_AVector3_InParam xTrue,register Asura_AVector3_InParam xFalse
		)
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm
			{ 
				vcmp.t*		EQ, xLHS.m_xVec, xRHS.m_xVec
				vomov.t*	m_xVec, xTrue.m_xVec
				vcmovf.t*	m_xVec, xFalse.m_xVec, 6
			};
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			__asm__
			(
				ASURA_PSP_ASM_NRBEGIN
				"vcmp.t		EQ, %1, %2 \n\t"
				"vmov.t		%0, %3 \n\t"
				"vcmovf.t	%0, %4, 6 \n\t"
				ASURA_PSP_ASM_NREND
				: VFPU_AND_C (m_afVec)
				: VFPU_C (xLHS.m_afVec), VFPU_C (xRHS.m_afVec),
				  VFPU_C (xTrue.m_afVec), VFPU_C (xFalse.m_afVec)
			);
		#else
			m_fX = xLHS.m_fX == xRHS.m_fX ? xTrue.m_fX : xFalse.m_fX;
			m_fY = xLHS.m_fY == xRHS.m_fY ? xTrue.m_fY : xFalse.m_fY;
			m_fZ = xLHS.m_fZ == xRHS.m_fZ ? xTrue.m_fZ : xFalse.m_fZ;
		#endif
		}

		inline void SelectNE(
			register Asura_AVector3_InParam xLHS,register Asura_AVector3_InParam xRHS,
			register Asura_AVector3_InParam xTrue,register Asura_AVector3_InParam xFalse
		)
		{
		#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
			_asm
			{ 
				vcmp.t*		NE, xLHS.m_xVec, xRHS.m_xVec
				vomov.t*	m_xVec, xTrue.m_xVec
				vcmovf.t*	m_xVec, xFalse.m_xVec, 6
			};
        #elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
			__asm__
			(
				ASURA_PSP_ASM_NRBEGIN
				"vcmp.t		NE, %1, %2 \n\t"
				"vmov.t		%0, %3 \n\t"
				"vcmovf.t	%0, %4, 6 \n\t"
				ASURA_PSP_ASM_NREND
				: VFPU_AND_C (m_afVec)
				: VFPU_C (xLHS.m_afVec), VFPU_C (xRHS.m_afVec),
				  VFPU_C (xTrue.m_afVec), VFPU_C (xFalse.m_afVec)
			);
		#else
			m_fX = xLHS.m_fX != xRHS.m_fX ? xTrue.m_fX : xFalse.m_fX;
			m_fY = xLHS.m_fY != xRHS.m_fY ? xTrue.m_fY : xFalse.m_fY;
			m_fZ = xLHS.m_fZ != xRHS.m_fZ ? xTrue.m_fZ : xFalse.m_fZ;
		#endif
		}

        #if defined __SNC__
            inline float GetX(void) const
            {
                const SNPokeVectorData* const pxData = reinterpret_cast
					<const SNPokeVectorData* const>(&m_xVec);
                return pxData->m_fX;
            }
            inline float GetY(void) const
            {
                const SNPokeVectorData* const pxData = reinterpret_cast
					<const SNPokeVectorData* const>(&m_xVec);
                return pxData->m_fY;
            }
            inline float GetZ(void) const
            {
                const SNPokeVectorData* const pxData = reinterpret_cast
					<const SNPokeVectorData* const>(&m_xVec);
                return pxData->m_fZ;
            }

            inline void SetX(const float fX)
            {
                SNPokeVectorData* const pxData = reinterpret_cast
					<SNPokeVectorData* const>(&m_xVec);
                pxData->m_fX = fX;
            }
            inline void SetY(const float fY)
            {
                SNPokeVectorData* const pxData = reinterpret_cast
					<SNPokeVectorData* const>(&m_xVec);
                pxData->m_fY = fY;
            }
            inline void SetZ(const float fZ)
            {
                SNPokeVectorData* const pxData = reinterpret_cast
					<SNPokeVectorData* const>(&m_xVec);
                pxData->m_fZ = fZ;
            }

            inline float GetElement(const u_int u) const
            {
                const SNPokeVectorDataArray* const pxData = reinterpret_cast
					<const SNPokeVectorDataArray* const>(&m_xVec);
                return *(reinterpret_cast<const float* const>(pxData) + u);
            }
            inline void SetElement(const u_int u,const float f)
            {
                SNPokeVectorDataArray* const pxData = reinterpret_cast
					<SNPokeVectorDataArray* const>(&m_xVec);
                *(reinterpret_cast<float* const>(pxData) + u) = f;
            }
        #else
            inline float GetX(void) const { return m_fX; }
            inline float GetY(void) const { return m_fY; }
            inline float GetZ(void) const { return m_fZ; }

            inline void SetX(const float fX) { m_fX = fX; }
            inline void SetY(const float fY) { m_fY = fY; }
            inline void SetZ(const float fZ) { m_fZ = fZ; }

            inline float GetElement(const u_int u) const
                { return *(reinterpret_cast<const float* const>(this) + u); }
            inline void SetElement(const u_int u,const float f)
                { *(reinterpret_cast<float* const>(this) + u) = f; }
		#endif
};

inline Asura_Vector_3::Asura_Vector_3(const Asura_AVector3& xV)
{
	x = xV.GetX();
	y = xV.GetY();
	z = xV.GetZ();
}

inline void Asura_Vector_3::operator = ( const Asura_AVector3& xV)
{
	x = xV.GetX();
	y = xV.GetY();
	z = xV.GetZ();
}


// Note that in Asura_Matrix_3x3 the data is stored as 3 rows. For
// efficiency when using vector instructions it's better for us to store
// 3 columns. Since the old 3x3 matrix use e11 to e33 I'm keeping the
// convention of starting named columns at 1 rather than 0.
class Asura_AMatrix3x3
{
	friend Asura_AVector3 Asura_AVector3::operator * ( const Asura_AMatrix3x3& xMat) const;

	Asura_AVector3 m_xCol0;
	Asura_AVector3 m_xCol1;
	Asura_AVector3 m_xCol2;

	public:

		inline Asura_AMatrix3x3( void )	{ }

        explicit Asura_AMatrix3x3( const Asura_Matrix_3x3& xMat3 )
        {
            FromMat3( xMat3 );
        }

		Asura_AMatrix3x3( float	e11, float e12,	float e13,
						  float	e21, float e22,	float e23,
						  float	e31, float e32,	float e33 )
		{
			m_xCol0	= Asura_AVector3(e11, e21, e31);
			m_xCol1	= Asura_AVector3(e12, e22, e32);
			m_xCol2	= Asura_AVector3(e13, e23, e33);
		}

		Asura_AMatrix3x3( const	Asura_AVector3&	xCol0, const Asura_AVector3& xCol1,	const Asura_AVector3& xCol2	)
		{
			m_xCol0	= xCol0;
			m_xCol1	= xCol1;
			m_xCol2	= xCol2;
		}

		void ToMat3(Asura_Matrix_3x3& xMat3) const
		{
			xMat3.e11 =	GetElement(0,0);
			xMat3.e12 =	GetElement(0,1);
			xMat3.e13 =	GetElement(0,2);
			xMat3.e21 =	GetElement(1,0);
			xMat3.e22 =	GetElement(1,1);
			xMat3.e23 =	GetElement(1,2);
			xMat3.e31 =	GetElement(2,0);
			xMat3.e32 =	GetElement(2,1);
			xMat3.e33 =	GetElement(2,2);
		}
		void FromMat3(const	Asura_Matrix_3x3& xMat3)
		{
			m_xCol0	= Asura_AVector3(xMat3.e11,	xMat3.e21, xMat3.e31);
			m_xCol1	= Asura_AVector3(xMat3.e12,	xMat3.e22, xMat3.e32);
			m_xCol2	= Asura_AVector3(xMat3.e13,	xMat3.e23, xMat3.e33);
		}

		Asura_AVector3 GetColumn0(void)	const {	return m_xCol0;	}
		Asura_AVector3 GetColumn1(void)	const {	return m_xCol1;	}
		Asura_AVector3 GetColumn2(void)	const {	return m_xCol2;	}
		Asura_AVector3 GetColumn(u_int u) const	{ return *(&m_xCol0	+ u); }
		float GetElement(u_int uRow, u_int uCol) const { return	GetColumn(uCol).GetElement(uRow); }

		void SetColumn0(Asura_AVector3_InParam xCol0) { m_xCol0 = xCol0;	}
		void SetColumn1(Asura_AVector3_InParam xCol1) { m_xCol1 = xCol1;	}
		void SetColumn2(Asura_AVector3_InParam xCol2) { m_xCol2 = xCol2;	}

		// Tilde operator to make a skew-symmetric matrix from a vector
		inline void Tilde( Asura_AVector3_InParam xVector )
		{
			m_xCol0 = Asura_AVector3( 0.0f,				+xVector.GetZ(),	-xVector.GetY() );
			m_xCol1 = Asura_AVector3( -xVector.GetZ(),	0.0f,				+xVector.GetX() );
			m_xCol2 = Asura_AVector3( +xVector.GetY(),	-xVector.GetX(),	0.0f			);
		}

		// Try to make the matrix orthogonal by forcing the axes represented by the matrix
		// columns to be a) unit-length and b) perpendicular.
		inline void Orthogonalise( void )
		{
			// normalise "down" column
			m_xCol1.Normalise();

			// make "forwards" column perpendicular to this, and normalise
			m_xCol2 -= m_xCol1 * ( m_xCol2 * m_xCol1 );
			m_xCol2.Normalise();

			// and now the "right" column must be perpendicular to both of these: x = + y^z
			m_xCol0 = m_xCol1 ^ m_xCol2;

		}

		// matrix-matrix per component addition - used in integration
		inline void Add( Asura_AMatrix3x3_InParam xM ) 
		{
			m_xCol0 += xM.m_xCol0;
			m_xCol1 += xM.m_xCol1;
			m_xCol2 += xM.m_xCol2;
		}

		inline void	Transpose(void)
		{
			float f	= m_xCol0.GetY();
			m_xCol0.SetY( m_xCol1.GetX() );
			m_xCol1.SetX( f	);

			f =	m_xCol0.GetZ();
			m_xCol0.SetZ( m_xCol2.GetX() );
			m_xCol2.SetX( f	);

			f =	m_xCol1.GetZ();
			m_xCol1.SetZ( m_xCol2.GetY() );
			m_xCol2.SetY( f	);
		}

		inline Asura_AVector3 ApplyTranspose( Asura_AVector3_InParam	xVec ) const
		{
			Asura_AMatrix3x3 xTemp(*this);
			xTemp.Transpose();
			return xVec	* xTemp;
		}

		inline void	Identity(void)
		{
			m_xCol0	= Asura_AVector3( 1.0f,	0.0f, 0.0f );
			m_xCol1	= Asura_AVector3( 0.0f,	1.0f, 0.0f );
			m_xCol2	= Asura_AVector3( 0.0f,	0.0f, 1.0f );
		}

		inline Asura_AMatrix3x3	operator * ( Asura_AMatrix3x3_InParam m)	const
		{
			return Asura_AMatrix3x3( m_xCol0 * m, m_xCol1 *	m, m_xCol2 * m );
		}

		inline Asura_AMatrix3x3	operator * ( const float s)	const
		{
			return Asura_AMatrix3x3(m_xCol0	* s, m_xCol1 * s, m_xCol2 *	s);
		}

		inline void	Zero(void)
		{
			m_xCol0.Zero();
			m_xCol1.Zero();
			m_xCol2.Zero();
		}

		inline bool	operator ==	( Asura_AMatrix3x3_InParam xMat)	const
		{
			return	m_xCol0	== xMat.m_xCol0	&&
					m_xCol1	== xMat.m_xCol1	&&
					m_xCol2	== xMat.m_xCol2;
		}
		inline bool	operator !=	( Asura_AMatrix3x3_InParam xMat)	const
		{
			return	m_xCol0	!= xMat.m_xCol0	||
					m_xCol1	!= xMat.m_xCol1	||
					m_xCol2	!= xMat.m_xCol2;
		}
};

inline Asura_AVector3 Asura_AVector3::operator *(Asura_AMatrix3x3_InParam xMat) const
{
#if defined ASURA_PSP_ASM_USE_SN_SYNTAX
	Asura_AVector3 xResult;

	_asm
	{
		lv.q r700, 0(&xMat.m_xCol0.m_xVec)
		lv.q r701, 0(&xMat.m_xCol1.m_xVec)
		lv.q r702, 0(&xMat.m_xCol2.m_xVec)

		lv.q r703, 0(&m_xVec)

		vtfm3.t* xResult.m_xVec, m700, r703
	};

	return xResult;
#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
	Asura_AVector3 afR;
	vfpu_m4x4 aafM;

	#if 1 && defined ASURA_PSP_GCC_BUILTINS
		aafM = vfpu_insert_v4_into_m4x(aafM,xMat.m_xCol0.m_afVec);
		aafM = vfpu_insert_v4_into_m4y(aafM,xMat.m_xCol1.m_afVec);
		aafM = vfpu_insert_v4_into_m4z(aafM,xMat.m_xCol2.m_afVec);
	#else
		__asm__("vmov.t %[aafM]%:00, %[xM1]"
				 : [aafM] VFPU_PLS_C (aafM) : [xM1] VFPU_C (xMat.m_xCol0.m_afVec));
		__asm__("vmov.t %[aafM]%:10, %[xM2]"
				 : [aafM] VFPU_PLS_C (aafM) : [xM2] VFPU_C (xMat.m_xCol1.m_afVec));
		__asm__("vmov.t %[aafM]%:20, %[xM3]"
				 : [aafM] VFPU_PLS_C (aafM) : [xM3] VFPU_C (xMat.m_xCol2.m_afVec));
	#endif

	/* No 3x3 intrinsics, so we can use only inline asm here */

	__asm__
	(
		"vtfm3.t %[afR], %e[aafM], %[afVec]"

		: [afR] VFPU_AND_C (afR.m_afVec)
		: [aafM] VFPU_C (aafM), [afVec] VFPU_C ((*this).m_afVec)
	);

	return afR;
#elif 0
	// PS3 version.
	vec_float4 res;
	vec_float4 xxxx, yyyy, zzzz;

	xxxx = vec_splat( m_xVec128, 0 );
	yyyy = vec_splat( m_xVec128, 1 );
	zzzz = vec_splat( m_xVec128, 2 );

	res = vec_madd( xMat.GetColumn0().m_xVec128, xxxx, (vec_float4)(0.0f) );
	res = vec_madd( xMat.GetColumn1().m_xVec128, yyyy, res );
	res = vec_madd( xMat.GetColumn2().m_xVec128, zzzz, res );
	return Asura_AVector3( res );
#else
	// naive implementation
	class Asura_AVector3 xResult;

	xResult.SetX( xMat.GetElement(0,0) * m_fX
        + xMat.GetElement(0,1) * m_fY + xMat.GetElement(0,2) * m_fZ );
	xResult.SetY( xMat.GetElement(1,0) * m_fX
        + xMat.GetElement(1,1) * m_fY + xMat.GetElement(1,2) * m_fZ );
	xResult.SetZ( xMat.GetElement(2,0) * m_fX
        + xMat.GetElement(2,1) * m_fY + xMat.GetElement(2,2) * m_fZ );

	return xResult;
#endif
}

// hack	- aligned vector * non aligned matrix
inline Asura_AVector3 Asura_AVector3::operator *(const Asura_Matrix_3x3& m) const
{
#if defined ASURA_PSP_USE_VFPU_ASSEMBLER && !defined __SNC__
	vfpu_m4x4 aafM;
	Asura_AVector3 afR;

	#if 1 && defined ASURA_PSP_GCC_BUILTINS
		vfpu_v4 afT;

		aafM = vfpu_insert_v4_into_m4x(aafM,
			vfpu_insert_float_into_v4xyz(afT,m.e11,m.e21,m.e31));

		aafM = vfpu_insert_v4_into_m4y(aafM,
			vfpu_insert_float_into_v4xyz(afT,m.e12,m.e22,m.e32));

		aafM = vfpu_insert_v4_into_m4z(aafM,
			vfpu_insert_float_into_v4xyz(afT,m.e13,m.e23,m.e33));
	#else
		__asm__("mtv %[e11], %[aafM]%:00"
				 : [aafM] VFPU_PLS_C (aafM) : [e11] "r" (m.e11));
		__asm__("mtv %[e12], %[aafM]%:01"
				 : [aafM] VFPU_PLS_C (aafM) : [e12] "r" (m.e12));
		__asm__("mtv %[e13], %[aafM]%:02"
				 : [aafM] VFPU_PLS_C (aafM) : [e13] "r" (m.e13));

		__asm__("mtv %[e21], %[aafM]%:10"
				 : [aafM] VFPU_PLS_C (aafM) : [e21] "r" (m.e21));
		__asm__("mtv %[e22], %[aafM]%:11"
				 : [aafM] VFPU_PLS_C (aafM) : [e22] "r" (m.e22));
		__asm__("mtv %[e23], %[aafM]%:12"
				 : [aafM] VFPU_PLS_C (aafM) : [e23] "r" (m.e23));

		__asm__("mtv %[e31], %[aafM]%:20"
				 : [aafM] VFPU_PLS_C (aafM) : [e31] "r" (m.e31));
		__asm__("mtv %[e32], %[aafM]%:21"
				 : [aafM] VFPU_PLS_C (aafM) : [e32] "r" (m.e32));
		__asm__("mtv %[e33], %[aafM]%:22"
				 : [aafM] VFPU_PLS_C (aafM) : [e33] "r" (m.e33));
	#endif

	/* No 3x3 intrinsics, so we can only use inline asm here */

	__asm__
	(
		"vtfm3.t %[afR], %e[aafM], %[afVec]"

		: [afR] VFPU_AND_C (afR.m_afVec)
		: [aafM] VFPU_C (aafM), [afVec] VFPU_C ((*this).m_afVec)
	);

	return afR;
#else
	Asura_AVector3 xResult;

	xResult.SetX(m.e11 * GetX() + m.e12 * GetY() + m.e13 * GetZ());
	xResult.SetY(m.e21 * GetX() + m.e22 * GetY() + m.e23 * GetZ());
	xResult.SetZ(m.e31 * GetX() + m.e32 * GetY() + m.e33 * GetZ());

	return xResult;
#endif
}

class Asura_SoA_Float
{
	float m_afValues[4];

public:

	Asura_SoA_Float() {}
	Asura_SoA_Float(const float fSplat )
	{
		m_afValues[0] =	fSplat;
		m_afValues[1] =	fSplat;
		m_afValues[2] =	fSplat;
		m_afValues[3] =	fSplat;
	}
	Asura_SoA_Float(const float f0,const float f1,
        const float f2,const float f3)
	{
		m_afValues[0] =	f0;
		m_afValues[1] =	f1;
		m_afValues[2] =	f2;
		m_afValues[3] =	f3;
	}
	inline void	operator = ( const Asura_SoA_Float&	xRHS )
	{
		m_afValues[0] =	xRHS.m_afValues[0];
		m_afValues[1] =	xRHS.m_afValues[1];
		m_afValues[2] =	xRHS.m_afValues[2];
		m_afValues[3] =	xRHS.m_afValues[3];
	}
	Asura_SoA_Float( const Asura_SoA_Float&	xRHS )
	{
		m_afValues[0] =	xRHS.m_afValues[0];
		m_afValues[1] =	xRHS.m_afValues[1];
		m_afValues[2] =	xRHS.m_afValues[2];
		m_afValues[3] =	xRHS.m_afValues[3];
	}

	inline Asura_SoA_Float operator	+ (	const Asura_SoA_Float& xRHS	) const
	{
		return Asura_SoA_Float(	m_afValues[0] +	xRHS.m_afValues[0],
								m_afValues[1] +	xRHS.m_afValues[1],
								m_afValues[2] +	xRHS.m_afValues[2],
								m_afValues[3] +	xRHS.m_afValues[3] );
	}

	inline Asura_SoA_Float operator	* (	const Asura_SoA_Float& xRHS	) const
	{
		return Asura_SoA_Float(	m_afValues[0] *	xRHS.m_afValues[0],
								m_afValues[1] *	xRHS.m_afValues[1],
								m_afValues[2] *	xRHS.m_afValues[2],
								m_afValues[3] *	xRHS.m_afValues[3] );
	}

	inline bool	operator ==	( const	Asura_SoA_Float& xRHS )	const
	{
		return	m_afValues[0] == xRHS.m_afValues[0]	&& 
				m_afValues[1] == xRHS.m_afValues[1]	&& 
				m_afValues[2] == xRHS.m_afValues[2]	&& 
				m_afValues[3] == xRHS.m_afValues[3];
	}
	inline bool	operator !=	( const	Asura_SoA_Float& xRHS )	const
	{
		return	m_afValues[0] != xRHS.m_afValues[0]	|| 
				m_afValues[1] != xRHS.m_afValues[1]	|| 
				m_afValues[2] != xRHS.m_afValues[2]	|| 
				m_afValues[3] != xRHS.m_afValues[3];
	}

	friend class Asura_SoA_Vector_3;
};

class Asura_SoA_Vector_3
{
	Asura_SoA_Float	m_xX;
	Asura_SoA_Float	m_xY;
	Asura_SoA_Float	m_xZ;

public:

	Asura_SoA_Vector_3() {}
	Asura_SoA_Vector_3(	const Asura_SoA_Float& xX, const Asura_SoA_Float& xY, const	Asura_SoA_Float& xZ	) 
	{
		m_xX = xX;
		m_xY = xY;
		m_xZ = xZ;
	}
	Asura_SoA_Vector_3(	const Asura_SoA_Vector_3& xRHS)
	{
		m_xX = xRHS.m_xX;
		m_xY = xRHS.m_xY;
		m_xZ = xRHS.m_xZ;
	}
	void	operator = (const Asura_SoA_Vector_3& xRHS)
	{
		m_xX = xRHS.m_xX;
		m_xY = xRHS.m_xY;
		m_xZ = xRHS.m_xZ;
	}

	// create new SoA floats from members
	inline Asura_SoA_Float GetXXXX() const { return	m_xX; }
	inline Asura_SoA_Float GetYYYY() const { return	m_xY; }
	inline Asura_SoA_Float GetZZZZ() const { return	m_xZ; }

	// dot product
	inline Asura_SoA_Float operator	* (	const Asura_SoA_Vector_3& xRHS ) const
	{
//		return	m_xX * xRHS.m_xX + 
//				m_xY * xRHS.m_xY + 
//				m_xZ * xRHS.m_xZ;
		float fOut0, fOut1,	fOut2, fOut3;

		float fX0 =	m_xX.m_afValues[0];
		float fX1 =	xRHS.m_xX.m_afValues[0];
		float fY0 =	m_xY.m_afValues[0];
		float fY1 =	xRHS.m_xY.m_afValues[0];
		float fZ0 =	m_xZ.m_afValues[0];
		float fZ1 =	xRHS.m_xZ.m_afValues[0];
		float fX2 =	m_xX.m_afValues[1];
		float fX3 =	xRHS.m_xX.m_afValues[1];
		float fY2 =	m_xY.m_afValues[1];
		float fY3 =	xRHS.m_xY.m_afValues[1];
		float fZ2 =	m_xZ.m_afValues[1];
		float fZ3 =	xRHS.m_xZ.m_afValues[1];

		fOut0 =	fX0	* fX1 +	fY0	* fY1 +	fZ0	* fZ1;
		fOut1 =	fX2	* fX3 +	fY2	* fY3 +	fZ2	* fZ3;

		fX0	= m_xX.m_afValues[2];
		fX1	= xRHS.m_xX.m_afValues[2];
		fY0	= m_xY.m_afValues[2];
		fY1	= xRHS.m_xY.m_afValues[2];
		fZ0	= m_xZ.m_afValues[2];
		fZ1	= xRHS.m_xZ.m_afValues[2];
		fX2	= m_xX.m_afValues[3];
		fX3	= xRHS.m_xX.m_afValues[3];
		fY2	= m_xY.m_afValues[3];
		fY3	= xRHS.m_xY.m_afValues[3];
		fZ2	= m_xZ.m_afValues[3];
		fZ3	= xRHS.m_xZ.m_afValues[3];

		fOut2 =	fX0	* fX1 +	fY0	* fY1 +	fZ0	* fZ1;
		fOut3 =	fX2	* fX3 +	fY2	* fY3 +	fZ2	* fZ3;

		return Asura_SoA_Float(	fOut0, fOut1, fOut2, fOut3 );
	}

	// transform by	matrix
	inline const Asura_SoA_Vector_3	operator * ( const Asura_AMatrix3x3& xMat )	const
	{
		const Asura_AVector3 xCol0 = xMat.GetColumn0();
		// splat M11 to	all	elements of	xM11, etc.
		Asura_SoA_Float	xM11(xCol0.GetX());
		Asura_SoA_Float	xM21(xCol0.GetY());
		Asura_SoA_Float	xM31(xCol0.GetZ());

		Asura_SoA_Float	xOutX =	m_xX * xM11;
		Asura_SoA_Float	xOutY =	m_xX * xM21;
		Asura_SoA_Float	xOutZ =	m_xX * xM31;

		const Asura_AVector3 xCol1 = xMat.GetColumn1();
		Asura_SoA_Float	xM12(xCol1.GetX());
		Asura_SoA_Float	xM22(xCol1.GetY());
		Asura_SoA_Float	xM32(xCol1.GetZ());

		xOutX =	m_xY * xM12	+ xOutX;
		xOutY =	m_xY * xM22	+ xOutY;
		xOutZ =	m_xY * xM32	+ xOutZ;

		const Asura_AVector3 xCol2 = xMat.GetColumn2();
		Asura_SoA_Float	xM13(xCol2.GetX());
		Asura_SoA_Float	xM23(xCol2.GetY());
		Asura_SoA_Float	xM33(xCol2.GetZ());

		xOutX =	m_xZ * xM13	+ xOutX;
		xOutY =	m_xZ * xM23	+ xOutY;
		xOutZ =	m_xZ * xM33	+ xOutZ;

		return Asura_SoA_Vector_3( xOutX, xOutY, xOutZ );
	}
};

// RJT NOTE: This define should really be enabled but requires SNC version 1.2.825.0 to compile (due to a bug in previous versions)
// RJT NOTE: Ultimately, once everyone is using the aforementioned compiler this define should be removed and all references changed to ASURA_PSP_ASM_USE_SN_SYNTAX
//#define ASURA_PSP_ASM_USE_SN_SYNTAX_NEW

// RJF 19-9-06: reference implementation, needs optimizing
class Asura_AVector4
{
public:
	// Constructors...
	Asura_AVector4(void);
	Asura_AVector4(const float fX, const float fY, const float fZ, const float fW);
	Asura_AVector4(const Asura_AVector4& xOther);
	Asura_AVector4(const Asura_AVector3& xOther, const float fW);
	Asura_AVector4(const Asura_Vector_3& xOther, const float fW);

	// Zero functions...
	void ZeroW0(void);
	void ZeroW1(void);

	// Selection functions...
	void SelectGE(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS, Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse);
	void SelectGT(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS, Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse);
	void SelectLE(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS, Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse);
	void SelectLT(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS, Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse);
	void SelectEQ(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS, Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse);
	void SelectNE(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS, Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse);

	// Load/store functions...
	void LoadAligned(const float* pfFloats);
	void LoadUnaligned(const float* pfFloats);
	void StoreAligned(float* pfFloats) const;
	void StoreUnaligned(float* pfFloats) const;
	void StoreAlignedHalf(Asura_HalfFloat* pxHalves) const;

	// Manipulation functions...
	Asura_AVector4 ComponentwiseProduct(Asura_AVector4_InParam xOther) const;
	float DotProduct3(Asura_AVector4_InParam xOther) const;
	float DotProduct4(Asura_AVector4_InParam xOther) const;

	float MagnitudeSquared(void) const;
	float Magnitude(void) const;

	void Normalise(void);
	float NormaliseReturningMagnitude(void);
	void ApproximateNormalise(void);
	float ApproximateNormaliseReturningMagnitude(void);

	// Assignment operators...
	void operator=(Asura_AVector4_InParam xOther);

	void operator+=(Asura_AVector4_InParam xOther);
	void operator-=(Asura_AVector4_InParam xOther);

	void operator*=(const float fScale);
	void operator/=(const float fScale);

	// Unary operators...
	Asura_AVector4 operator-(void) const;

	// Binary operators...
	Asura_AVector4 operator+(Asura_AVector4_InParam xOther) const;
	Asura_AVector4 operator-(Asura_AVector4_InParam xOther) const;
	Asura_AVector4 operator^(Asura_AVector4_InParam xOther) const;

	float operator*(Asura_AVector4_InParam xOther) const;

	Asura_AVector4 operator*(const float fScale) const;
	Asura_AVector4 operator/(const float fScale) const;

	// Comparison operators...
	bool operator==(Asura_AVector4_InParam xOther) const;
	bool ApproximatelyEquals(Asura_AVector4_InParam xOther, const float fEpsilon = 1.0e-6f) const;
	bool operator!=(Asura_AVector4_InParam xOther) const;

	// Minimum/maximum functions...
	void Max(Asura_AVector4_InParam xV1, Asura_AVector4_InParam xV2);
	void Min(Asura_AVector4_InParam xV1, Asura_AVector4_InParam xV2);

	// Properties...
	float GetX(void) const;
	float GetY(void) const;
	float GetZ(void) const;
	float GetW(void) const;

	void SetX(const float fX);
	void SetY(const float fY);
	void SetZ(const float fZ);
	void SetW(const float fW);

	float GetElement(const u_int uIndex) const;
	void SetElement(const u_int uIndex, const float fValue);
	void SetElements(const float fX, const float fY, const float fZ, const float fW);

private:

	friend class Asura_AMatrix4x4;
	friend Asura_AVector4 operator *(Asura_AVector4_InParam xVec, Asura_AMatrix4x4_InParam xMat);
	friend Asura_AMatrix4x4 operator *(Asura_AMatrix4x4_InParam xM1, Asura_AMatrix4x4_InParam xM2);

#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	union
	{
		SNVectorType m_xVec;
		struct
		{
			float m_fX;
			float m_fY;
			float m_fZ;
			float m_fW;
		};
	};
#elif ASURA_PSP_GCC_BUILTINS
	union
	{
		vfpu_v4 m_xVec;
		struct
		{
			float m_fX;
			float m_fY;
			float m_fZ;
			float m_fW;
		};
	};
#elif defined ASURA_PSP_USE_VFPU_ASSEMBLER
	struct
	{
		float m_fX;
		float m_fY;
		float m_fZ;
		float m_fW;
	} __attribute__( ( aligned( 16 ) ) );
#else
	struct
	{
		float m_fX;
		float m_fY;
		float m_fZ;
		float m_fW;
	};
#endif
};


//////////////////////////////////////////////////////////////////////////
// Asura_AMatrix4x4
//////////////////////////////////////////////////////////////////////////
class Asura_AMatrix4x4
{
public:
	// Constructors/initializers
	Asura_AMatrix4x4(void);
	Asura_AMatrix4x4(const float fE11, const float fE12, const float fE13, const float fE14,
					 const float fE21, const float fE22, const float fE23, const float fE24,
					 const float fE31, const float fE32, const float fE33, const float fE34,
					 const float fE41, const float fE42, const float fE43, const float fE44);
	Asura_AMatrix4x4(const Asura_AMatrix4x4& xOther);
	Asura_AMatrix4x4(const Asura_Matrix_3x3& xOrientation, const Asura_Vector_3& xTranslation);
	Asura_AMatrix4x4(const Asura_Quat& xOrientation, const Asura_Vector_3& xTranslation);

	// Constant functions...
	void Identity(void);
	void Zero(void);

	// Orientation/translation functions...
	void ToMatrix(Asura_Matrix_3x3& xOrientation) const;
	void FromMatrix(const Asura_Matrix_3x3& xOrientation);

	Asura_Vector_3 GetTranslation() const;
	void SetTranslation(const Asura_Vector_3& xTranslation);

	Asura_AVector3 GetATranslation() const;
	void SetTranslation(Asura_AVector3_InParam xTranslation);

	Asura_AVector4 GetA4Translation() const;
	void SetTranslation(Asura_AVector4_InParam xTranslation);

	// Manipulation functions...
	Asura_AVector4 Apply(Asura_AVector4_InParam xInput) const;
	void ApplyToArray(const Asura_AVector4* pxInput, const u_int uNumElements, Asura_AVector4* pxOutput) const;

	void Apply(Asura_AMatrix4x4_InParam xInput, Asura_AMatrix4x4_OutParam xOutput) const;
	void ApplyToArray(const Asura_AMatrix4x4* pxInput, const u_int uNumElements, Asura_AMatrix4x4* pxOutput) const;

	float Cofactor(const u_int uRow, const u_int uCol) const;
	float Determinant(void) const;

	void Invert(void);
	void InvertOrthogonal(void);
	void Transpose(void);

	void Invert(Asura_AMatrix4x4_OutParam xOther) const;
	void InvertOrthogonal(Asura_AMatrix4x4_OutParam xOther) const;
	void Transpose(Asura_AMatrix4x4_OutParam xOther) const;

	// Unary operators
	Asura_AMatrix4x4 operator-(void) const;

	// Binary operators
	Asura_AMatrix4x4 operator*(const float fScale) const;
	Asura_AMatrix4x4 operator/(const float fScale) const;

	// Assignment operators
	void operator=(Asura_AMatrix4x4_InParam xOther);
	void operator*=(const float fScale);
	void operator/=(const float fScale);

	// Conditional operators
	bool operator==(Asura_AMatrix4x4_InParam xOther) const;
	bool ApproximatelyEquals(Asura_AMatrix4x4_InParam xOther, const float fEpsilon = 1.0e-6f) const;
	bool operator!=(Asura_AMatrix4x4_InParam xOther) const;

	// Elementary properties...
	float GetElement(const u_int uRow, const u_int uCol) const;
	void SetElement(const u_int uRow, const u_int uCol, const float fValue);

	Asura_AVector4 GetColumn(const u_int uColumnIndex) const;
	Asura_AVector4 GetColumn0() const;
	Asura_AVector4 GetColumn1() const;
	Asura_AVector4 GetColumn2() const;
	Asura_AVector4 GetColumn3() const;

	void SetColumn(const u_int uColumnIndex, Asura_AVector4_InParam xColumn);
	void SetColumn0(Asura_AVector4_InParam xCol0);
	void SetColumn1(Asura_AVector4_InParam xCol1);
	void SetColumn2(Asura_AVector4_InParam xCol2);
	void SetColumn3(Asura_AVector4_InParam xCol3);

	Asura_AVector4 GetRow(const u_int uRowIndex) const;
	Asura_AVector4 GetRow0() const;
	Asura_AVector4 GetRow1() const;
	Asura_AVector4 GetRow2() const;
	Asura_AVector4 GetRow3() const;

	void SetRow(const u_int uRowIndex, Asura_AVector4_InParam xRow);
	void SetRow0(Asura_AVector4_InParam xRow0);
	void SetRow1(Asura_AVector4_InParam xRow1);
	void SetRow2(Asura_AVector4_InParam xRow2);
	void SetRow3(Asura_AVector4_InParam xRow3);

	void SetColumns(Asura_AVector4_InParam xCol0, Asura_AVector4_InParam xCol1, Asura_AVector4_InParam xCol2, Asura_AVector4_InParam xCol3);
	void SetRows(Asura_AVector4_InParam xRow0, Asura_AVector4_InParam xRow1, Asura_AVector4_InParam xRow2, Asura_AVector4_InParam xRow3);

	// Load/store functions...
	void LoadColsAligned(const float* pfFloats);
	void LoadRowsAligned(const float* pfFloats);
	void LoadColsUnaligned(const float* pfFloats);
	void LoadRowsUnaligned(const float* pfFloats);

	void StoreColsAligned(float* pfFloats) const;
	void StoreRowsAligned(float* pfFloats) const;
	void StoreColsUnaligned(float* pfFloats) const;
	void StoreRowsUnaligned(float* pfFloats) const;

private:
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	union
	{
		SNMatrixType m_xMat;
		struct
		{
			Asura_AVector4 m_axColumns[ 4 ];
		};
	};
#elif ASURA_PSP_GCC_BUILTINS || defined ASURA_PSP_USE_VFPU_ASSEMBLER
	// RJT NOTE: A union isn't used here as the Asura_AVector4 class has constructors which isn't allowed
	Asura_AVector4 m_axColumns[ 4 ] __attribute__( ( aligned( 64 ) ) );
#else
	Asura_AVector4 m_axColumns[4];
#endif
};


//////////////////////////////////////////////////////////////////////////
// Inline Function Definitions
//////////////////////////////////////////////////////////////////////////

// scalar * 4x1 Vector - to match the member function in Asura_AVector4
inline Asura_AVector4 operator*(const float fScale, const Asura_AVector4& xOther)
{
	return(xOther * fScale);
}

inline Asura_AVector4 operator*(Asura_AVector4_InParam xVec, Asura_AMatrix4x4_InParam xMat)
{
	return xMat.Apply(xVec);
}

// To match the backwards vector-matrix multiply, we do a backwards matrix-vector multiply too.
inline Asura_AMatrix4x4 operator*(Asura_AMatrix4x4_InParam xM1, Asura_AMatrix4x4_InParam xM2)
{
	Asura_AMatrix4x4	xResult;

	xM2.Apply(xM1, xResult);
	return(xResult);
}


//////////////////////////////////////////////////////////////////////////
// Asura_AVector4
//////////////////////////////////////////////////////////////////////////
inline Asura_AVector4::Asura_AVector4(void)
{
}

inline Asura_AVector4::Asura_AVector4(const float fX, const float fY, const float fZ, const float fW)
: m_fX(fX)
, m_fY(fY)
, m_fZ(fZ)
, m_fW(fW)
{
}

inline Asura_AVector4::Asura_AVector4(const Asura_AVector4& xOther)
: m_fX(xOther.m_fX)
, m_fY(xOther.m_fY)
, m_fZ(xOther.m_fZ)
, m_fW(xOther.m_fW)
{
}

inline Asura_AVector4::Asura_AVector4(const Asura_AVector3& xOther, const float fW)
: m_fX(xOther.GetX())
, m_fY(xOther.GetY())
, m_fZ(xOther.GetZ())
, m_fW(fW)
{
}

inline Asura_AVector4::Asura_AVector4(const Asura_Vector_3& xOther, const float fW)
: m_fX(xOther.x)
, m_fY(xOther.y)
, m_fZ(xOther.z)
, m_fW(fW)
{
}

inline void Asura_AVector4::ZeroW0(void) 
{
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	_asm
	{
		vzero.q		m_xVec
	};
#elif ASURA_PSP_GCC_BUILTINS
	m_xVec = vfpu_vzero_q( 0 );
#else
	m_fX = m_fY = m_fZ = m_fW = 0.f;
#endif
}

inline void Asura_AVector4::ZeroW1(void) 
{
	m_fX = m_fY = m_fZ = 0.0f;
	m_fW = 1.0f;
}

inline void	Asura_AVector4::SelectGE(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS,
									 Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse)
{
	m_fX 	= (xLHS.m_fX >= xRHS.m_fX) ? xTrue.m_fX : xFalse.m_fX;
	m_fY 	= (xLHS.m_fY >= xRHS.m_fY) ? xTrue.m_fY : xFalse.m_fY;
	m_fZ 	= (xLHS.m_fZ >= xRHS.m_fZ) ? xTrue.m_fZ : xFalse.m_fZ;
	m_fW 	= (xLHS.m_fW >= xRHS.m_fW) ? xTrue.m_fW : xFalse.m_fW;
}

inline void	Asura_AVector4::SelectGT(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS,
									 Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse)
{
	m_fX 	= (xLHS.m_fX > xRHS.m_fX) ? xTrue.m_fX : xFalse.m_fX;
	m_fY 	= (xLHS.m_fY > xRHS.m_fY) ? xTrue.m_fY : xFalse.m_fY;
	m_fZ 	= (xLHS.m_fZ > xRHS.m_fZ) ? xTrue.m_fZ : xFalse.m_fZ;
	m_fW 	= (xLHS.m_fW > xRHS.m_fW) ? xTrue.m_fW : xFalse.m_fW;
}

inline void	Asura_AVector4::SelectLE(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS,
									 Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse)
{
	m_fX 	= (xLHS.m_fX <= xRHS.m_fX) ? xTrue.m_fX : xFalse.m_fX;
	m_fY 	= (xLHS.m_fY <= xRHS.m_fY) ? xTrue.m_fY : xFalse.m_fY;
	m_fZ 	= (xLHS.m_fZ <= xRHS.m_fZ) ? xTrue.m_fZ : xFalse.m_fZ;
	m_fW 	= (xLHS.m_fW <= xRHS.m_fW) ? xTrue.m_fW : xFalse.m_fW;
}

inline void	Asura_AVector4::SelectLT(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS,
									 Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse)
{
	m_fX 	= (xLHS.m_fX < xRHS.m_fX) ? xTrue.m_fX : xFalse.m_fX;
	m_fY 	= (xLHS.m_fY < xRHS.m_fY) ? xTrue.m_fY : xFalse.m_fY;
	m_fZ 	= (xLHS.m_fZ < xRHS.m_fZ) ? xTrue.m_fZ : xFalse.m_fZ;
	m_fW 	= (xLHS.m_fW < xRHS.m_fW) ? xTrue.m_fW : xFalse.m_fW;
}

inline void	Asura_AVector4::SelectEQ(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS,
									 Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse)
{
	m_fX 	= (xLHS.m_fX == xRHS.m_fX) ? xTrue.m_fX : xFalse.m_fX;
	m_fY 	= (xLHS.m_fY == xRHS.m_fY) ? xTrue.m_fY : xFalse.m_fY;
	m_fZ 	= (xLHS.m_fZ == xRHS.m_fZ) ? xTrue.m_fZ : xFalse.m_fZ;
	m_fW 	= (xLHS.m_fW == xRHS.m_fW) ? xTrue.m_fW : xFalse.m_fW;
}

inline void	Asura_AVector4::SelectNE(Asura_AVector4_InParam xLHS, Asura_AVector4_InParam xRHS,
									 Asura_AVector4_InParam xTrue, Asura_AVector4_InParam xFalse)
{
	m_fX 	= (xLHS.m_fX != xRHS.m_fX) ? xTrue.m_fX : xFalse.m_fX;
	m_fY 	= (xLHS.m_fY != xRHS.m_fY) ? xTrue.m_fY : xFalse.m_fY;
	m_fZ 	= (xLHS.m_fZ != xRHS.m_fZ) ? xTrue.m_fZ : xFalse.m_fZ;
	m_fW 	= (xLHS.m_fW != xRHS.m_fW) ? xTrue.m_fW : xFalse.m_fW;
}

inline void Asura_AVector4::operator=(const Asura_AVector4& xOther)
{ 
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	m_xVec = xOther.m_xVec;
#elif ASURA_PSP_GCC_BUILTINS
	m_xVec = xOther.m_xVec;
#else
	m_fX = xOther.m_fX; m_fY = xOther.m_fY; m_fZ = xOther.m_fZ; m_fW = xOther.m_fW;
#endif	
}

inline void Asura_AVector4::LoadAligned(const float* pfFloats)
{
	m_fX	= pfFloats[0];
	m_fY	= pfFloats[1];
	m_fZ	= pfFloats[2];
	m_fW	= pfFloats[3];
}

inline void Asura_AVector4::LoadUnaligned(const float* pfFloats)
{
	m_fX	= pfFloats[0];
	m_fY	= pfFloats[1];
	m_fZ	= pfFloats[2];
	m_fW	= pfFloats[3];
}

inline void Asura_AVector4::StoreAligned(float* pfFloats) const
{
	pfFloats[0]	= m_fX;
	pfFloats[1]	= m_fY;
	pfFloats[2]	= m_fZ;
	pfFloats[3]	= m_fW;
}

inline void Asura_AVector4::StoreUnaligned(float* pfFloats) const
{
	pfFloats[0]	= m_fX;
	pfFloats[1]	= m_fY;
	pfFloats[2]	= m_fZ;
	pfFloats[3]	= m_fW;
}

inline void Asura_AVector4::StoreAlignedHalf(Asura_HalfFloat* pxHalves) const
{
	pxHalves[0]	= m_fX;
	pxHalves[1]	= m_fY;
	pxHalves[2]	= m_fZ;
	pxHalves[3]	= m_fW;
}

inline Asura_AVector4 Asura_AVector4::ComponentwiseProduct(Asura_AVector4_InParam xOther) const
{
	Asura_AVector4 xResult;
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	__asm{
		vmul.q		xResult.m_xVec, m_xVec, xOther.m_xVec
	};
#elif ASURA_PSP_GCC_BUILTINS
	xResult.m_xVec = vfpu_vmul_q( m_xVec, xOther.m_xVec, 0 );
#else
	xResult = Asura_AVector4(m_fX * xOther.m_fX, m_fY * xOther.m_fY, m_fZ * xOther.m_fZ, m_fW * xOther.m_fW);
#endif
	return xResult;
}

inline float Asura_AVector4::DotProduct3(Asura_AVector4_InParam xOther) const
{
	return((m_fX * xOther.m_fX) + (m_fY * xOther.m_fY) + (m_fZ * xOther.m_fZ));
}

inline float Asura_AVector4::DotProduct4(Asura_AVector4_InParam xOther) const
{
	float fResult;
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	SNScalarType fVFPUScalar;
	_asm {
		vdot.q		fVFPUScalar, m_xVec, xOther.m_xVec
	};
	fResult = __builtin_vstof( fVFPUScalar );
#elif ASURA_PSP_GCC_BUILTINS
	fResult = vfpu_extract_float_from_v4x( vfpu_vdot_q( m_xVec, xOther.m_xVec, 0 ) );
#else
	fResult = (m_fX * xOther.m_fX) + (m_fY * xOther.m_fY) + (m_fZ * xOther.m_fZ) + (m_fW * xOther.m_fW);
#endif
	return fResult;
}

inline float Asura_AVector4::MagnitudeSquared(void) const 
{
	return(DotProduct4(*this));
}

inline float Asura_AVector4::Magnitude(void) const 
{
	return(Asura_Maths::Sqrt(MagnitudeSquared()));
}

inline void Asura_AVector4::Normalise(void) 
{
	const float	fMagnitude	= Magnitude();

	*this	/= fMagnitude;
}

inline float Asura_AVector4::NormaliseReturningMagnitude(void)
{
	const float	fMagnitude	= Magnitude();

	if(fMagnitude == 0.0f)
	{
		return(0.0f);
	}
	else
	{
		*this	/= fMagnitude;
		return(fMagnitude);
	}
}

inline void Asura_AVector4::operator+=(Asura_AVector4_InParam xOther)
{
	*this	= *this + xOther;
}

inline void Asura_AVector4::operator-=(Asura_AVector4_InParam xOther)
{
	*this	= *this - xOther;
}

inline void Asura_AVector4::operator*=(const float fScale)
{
	*this	= *this * fScale;
}

inline void Asura_AVector4::operator/=(const float fScale) 
{
	*this	= *this / fScale;
}

inline Asura_AVector4 Asura_AVector4::operator-(void) const 
{
	return(Asura_AVector4(-m_fX, -m_fY, -m_fZ, -m_fW));
}

inline Asura_AVector4 Asura_AVector4::operator+(Asura_AVector4_InParam xOther) const
{
	Asura_AVector4 xResult;
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	__asm{
		vadd.q		xResult.m_xVec, m_xVec, xOther.m_xVec
	};
#elif ASURA_PSP_GCC_BUILTINS
	xResult.m_xVec = vfpu_vadd_q( m_xVec, xOther.m_xVec, 0 );
#else
	xResult = Asura_AVector4(m_fX + xOther.m_fX, m_fY + xOther.m_fY, m_fZ + xOther.m_fZ, m_fW + xOther.m_fW);
#endif
	return xResult;
}

inline Asura_AVector4 Asura_AVector4::operator-(Asura_AVector4_InParam xOther) const
{
	Asura_AVector4 xResult;
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	__asm{
		vsub.q		xResult.m_xVec, m_xVec, xOther.m_xVec
	};
#elif ASURA_PSP_GCC_BUILTINS
	xResult.m_xVec = vfpu_vsub_q( m_xVec, xOther.m_xVec, 0 );
#else
	xResult = Asura_AVector4(m_fX - xOther.m_fX, m_fY - xOther.m_fY, m_fZ - xOther.m_fZ, m_fW - xOther.m_fW);
#endif
	return xResult;
}

inline float Asura_AVector4::operator*(Asura_AVector4_InParam xOther) const
{
	float fResult;
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	SNScalarType fVFPUScalar;
	_asm {
		vdot.q		fVFPUScalar, m_xVec, xOther.m_xVec
	};
	fResult = __builtin_vstof( fVFPUScalar );
#elif ASURA_PSP_GCC_BUILTINS
	fResult = vfpu_extract_float_from_v4x( vfpu_vdot_q( m_xVec, xOther.m_xVec, 0 ) );
#else
	fResult = (m_fX * xOther.m_fX) + (m_fY * xOther.m_fY) + (m_fZ * xOther.m_fZ) + (m_fW * xOther.m_fW);
#endif
	return fResult;
}

inline Asura_AVector4 Asura_AVector4::operator^(Asura_AVector4_InParam xOther) const
{
	return(Asura_AVector4((m_fY * xOther.m_fZ) - (m_fZ * xOther.m_fY), (m_fZ * xOther.m_fX) - (m_fX * xOther.m_fZ), (m_fX * xOther.m_fY) - (m_fY * xOther.m_fX), 0.0f));
}

inline Asura_AVector4 Asura_AVector4::operator*(const float fScale) const 
{
	Asura_AVector4 xResult;
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	SNScalarType fVFPUScalar = __builtin_ftovs(fScale);
	_asm {
		vscl.q		xResult.m_xVec, m_xVec, fVFPUScalar
	};
#elif ASURA_PSP_GCC_BUILTINS
	xResult.m_xVec = vfpu_vscl_q( m_xVec, vfpu_v4_s(fScale), 0 );
#else
	xResult = Asura_AVector4(m_fX * fScale, m_fY * fScale, m_fZ * fScale, m_fW * fScale);
#endif
	return xResult;
}

inline Asura_AVector4 Asura_AVector4::operator/(const float fScale) const
{
	return(*this * (1.0f / fScale));
}


inline bool Asura_AVector4::operator==(Asura_AVector4_InParam xOther) const
{
	return((m_fX == xOther.m_fX) && (m_fY == xOther.m_fY) && (m_fZ == xOther.m_fZ) && (m_fW == xOther.m_fW));
}

inline bool Asura_AVector4::ApproximatelyEquals(Asura_AVector4_InParam xOther, const float fEpsilon) const
{
	Asura_AVector4	xDifference(*this - xOther);

	return(xDifference.MagnitudeSquared() < fEpsilon);
}

inline bool Asura_AVector4::operator!=(Asura_AVector4_InParam xOther) const
{
	return((m_fX != xOther.m_fX) || (m_fY != xOther.m_fY) || (m_fZ != xOther.m_fZ) || (m_fW != xOther.m_fW));
}

inline void Asura_AVector4::Min(Asura_AVector4_InParam xV1, Asura_AVector4_InParam xV2)
{
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	_asm {
		vmin.q		m_xVec, xV1.m_xVec, xV2.m_xVec
	};
#elif ASURA_PSP_GCC_BUILTINS
	m_xVec = vfpu_vmin_q( xV1.m_xVec, xV2.m_xVec, 0 );
#else
	m_fX = ( xV2.m_fX < xV1.m_fX ) ? xV2.m_fX : xV1.m_fX;
	m_fY = ( xV2.m_fY < xV1.m_fY ) ? xV2.m_fY : xV1.m_fY;
	m_fZ = ( xV2.m_fZ < xV1.m_fZ ) ? xV2.m_fZ : xV1.m_fZ;
	m_fW = ( xV2.m_fW < xV1.m_fW ) ? xV2.m_fW : xV1.m_fW;
#endif	
}

inline void Asura_AVector4::Max(Asura_AVector4_InParam xV1, Asura_AVector4_InParam xV2)
{
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	_asm {
		vmax.q		m_xVec, xV1.m_xVec, xV2.m_xVec
	};
#elif ASURA_PSP_GCC_BUILTINS
	m_xVec = vfpu_vmax_q( xV1.m_xVec, xV2.m_xVec, 0 );
#else
	m_fX = ( xV2.m_fX > xV1.m_fX ) ? xV2.m_fX : xV1.m_fX;
	m_fY = ( xV2.m_fY > xV1.m_fY ) ? xV2.m_fY : xV1.m_fY;
	m_fZ = ( xV2.m_fZ > xV1.m_fZ ) ? xV2.m_fZ : xV1.m_fZ;
	m_fW = ( xV2.m_fW > xV1.m_fW ) ? xV2.m_fW : xV1.m_fW;
#endif	
}

inline float Asura_AVector4::GetX(void) const
{
	return(m_fX);
}

inline float Asura_AVector4::GetY(void) const
{ 
	return(m_fY);
}

inline float Asura_AVector4::GetZ(void) const
{
	return(m_fZ);
}

inline float Asura_AVector4::GetW(void) const
{
	return(m_fW);
}

inline void Asura_AVector4::SetX(const float fX)
{
	m_fX	= fX;
}

inline void Asura_AVector4::SetY(const float fY)
{
	m_fY	= fY;
}

inline void Asura_AVector4::SetZ(const float fZ)
{
	m_fZ	= fZ;
}

inline void Asura_AVector4::SetW(const float fW)
{
	m_fW	= fW;
}

inline float Asura_AVector4::GetElement(const u_int uIndex) const
{ 
	// This is not the fastest implementation - we could be using pointer math - but it's the most correct, and for this reference
	// implementation that is my priority. It won't break with weird packing, it detects out-of-bounds cases, and it's not /that/ slow.
	switch(uIndex)
	{
	case 0: return m_fX;
	case 1: return m_fY;
	case 2: return m_fZ;
	case 3: return m_fW;
	default: Asura_Assert(false, "Invalid index passed to Asura_AVector4::GetElement"); return 0.f;
	}
}

inline void Asura_AVector4::SetElement(const u_int uIndex, const float fValue) 
{
	// Implemented mirroring GetElement - see comment there
	switch(uIndex)
	{
	case 0: m_fX = fValue; return;
	case 1: m_fY = fValue; return;
	case 2: m_fZ = fValue; return;
	case 3: m_fW = fValue; return;
	default: Asura_Assert(false, "Invalid index passed to Asura_AVector4::SetElement");
	}
}		

inline void Asura_AVector4::SetElements(const float fX, const float fY, const float fZ, const float fW)
{
	m_fX	= fX;
	m_fY	= fY;
	m_fZ	= fZ;
	m_fW	= fW;
}


//////////////////////////////////////////////////////////////////////////
// Asura_AMatrix4x4
//////////////////////////////////////////////////////////////////////////
inline Asura_AMatrix4x4::Asura_AMatrix4x4(void)
{
}

inline Asura_AMatrix4x4::Asura_AMatrix4x4(const float fE11, const float fE12, const float fE13, const float fE14,
										  const float fE21, const float fE22, const float fE23, const float fE24,
										  const float fE31, const float fE32, const float fE33, const float fE34,
										  const float fE41, const float fE42, const float fE43, const float fE44)
{
	m_axColumns[0].SetElements(fE11, fE21, fE31, fE41);
	m_axColumns[1].SetElements(fE12, fE22, fE32, fE42);
	m_axColumns[2].SetElements(fE13, fE23, fE33, fE43);
	m_axColumns[3].SetElements(fE14, fE24, fE34, fE44);
}

inline Asura_AMatrix4x4::Asura_AMatrix4x4(const Asura_AMatrix4x4& xOther)
{
	m_axColumns[0]	= xOther.m_axColumns[0];
	m_axColumns[1]	= xOther.m_axColumns[1];
	m_axColumns[2]	= xOther.m_axColumns[2];
	m_axColumns[3]	= xOther.m_axColumns[3];
}

inline Asura_AMatrix4x4::Asura_AMatrix4x4(const Asura_Matrix_3x3& xRotation, const Asura_Vector_3& xPosition)
{
	m_axColumns[0].SetElements(xRotation.e11,		xRotation.e21,		xRotation.e31,		0.0f);
	m_axColumns[1].SetElements(xRotation.e12,		xRotation.e22,		xRotation.e32,		0.0f);
	m_axColumns[2].SetElements(xRotation.e13,		xRotation.e23,		xRotation.e33,		0.0f);
	m_axColumns[3].SetElements(xPosition.GetX(),	xPosition.GetY(),	xPosition.GetZ(),	1.0f);
}

inline Asura_AMatrix4x4::Asura_AMatrix4x4(const Asura_Quat& xRotation, const Asura_Vector_3& xPosition)
{
	// RJT TODO: Intrinsics
/*#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
#elif ASURA_PSP_GCC_BUILTINS
#elif*/#ifdef ASURA_PSP_USE_VFPU_ASSEMBLER
	__asm__ (
	".set			push\n"							// Save assembler option
	".set			noreorder\n"					// Suppress reordering
	//			"lv.q			c130,  %1\n"					// c130 = *pq = (x, y, z, w)
	"lv.s			s130,  0 + %1\n"				// c130 = *pq = (x, y, z, w)
	"lv.s			s131,  4 + %1\n"				// c130 = *pq = (x, y, z, w)
	"lv.s			s132,  8 + %1\n"				// c130 = *pq = (x, y, z, w)
	"lv.s			s133,  12 + %1\n"				// c130 = *pq = (x, y, z, w)
	"vmov.q			c100, c130[ W,  Z, -Y, -X]\n"	// c100 = ( w,  z, -y, -x)
	"vmov.q			c110, c130[-Z,  W,  X, -Y]\n"	// c110 = (-z,  w,  x, -y)
	"vmov.q			c120, c130[ Y, -X,  W, -Z]\n"	// c120 = ( y, -x,  w, -z)
	"vmov.q			c200, c130[ W,  Z, -Y,  X]\n"	// c200 = ( w,  z, -y,  x)
	"vmov.q			c210, c130[-Z,  W,  X,  Y]\n"	// c210 = (-z,  w,  x,  y)
	"vmov.q			c220, c130[ Y, -X,  W,  Z]\n"	// c220 = ( y, -x,  w,  z)
	"vmov.q			c230, c130[-X, -Y, -Z,  W]\n"	// c230 = (-x, -y, -z,  w)
	"vmmul.q		e000, e100, e200\n"				// e000 = e100 * e200
	"sv.q			c000,  0 + %0\n"				// pm->x = c000
	"sv.q			c010, 16 + %0\n"				// pm->y = c010
	"sv.q			c020, 32 + %0\n"				// pm->z = c020
	".set			pop\n"							// Restore assembler option
	: "=m"( *this )
	: "m"( xRotation )
	);

	m_axColumns[3].SetElements(xPosition.GetX(),	xPosition.GetY(),	xPosition.GetZ(),	1.0f);
#else
	const float	fS	= 2.0f;	//xRotation.Magnitude();

	const float	fXS	= xRotation.x * fS;
	const float	fYS	= xRotation.y * fS;
	const float	fZS	= xRotation.z * fS;

	const float	fWX	= xRotation.w * fXS;
	const float	fWY	= xRotation.w * fYS;
	const float	fWZ	= xRotation.w * fZS;

	const float	fXX	= xRotation.x * fXS;
	const float	fXY	= xRotation.x * fYS;
	const float	fXZ	= xRotation.x * fZS;

	const float	fYY	= xRotation.y * fYS;
	const float	fYZ	= xRotation.y * fZS;
	const float	fZZ	= xRotation.z * fZS;

	// Implementation from Graphics Gems IV...
	m_axColumns[0].SetElements(1.0f - (fYY + fZZ),	fXY + fWZ,			fXZ - fWY,			0.0f);
	m_axColumns[1].SetElements(fXY - fWZ,			1.0f - (fXX + fZZ),	fYZ + fWX,			0.0f);
	m_axColumns[2].SetElements(fXZ + fWY,			fYZ - fWX,			1.0f - (fXX + fYY),	0.0f);
	m_axColumns[3].SetElements(xPosition.GetX(),	xPosition.GetY(),	xPosition.GetZ(),	1.0f);
#endif
}

inline void Asura_AMatrix4x4::SetColumns(Asura_AVector4_InParam xCol0, Asura_AVector4_InParam xCol1, Asura_AVector4_InParam xCol2, Asura_AVector4_InParam xCol3)
{
	m_axColumns[0]	= xCol0;
	m_axColumns[1]	= xCol1;
	m_axColumns[2]	= xCol2;
	m_axColumns[3]	= xCol3;
}

inline void Asura_AMatrix4x4::SetRows(Asura_AVector4_InParam xRow0, Asura_AVector4_InParam xRow1, Asura_AVector4_InParam xRow2, Asura_AVector4_InParam xRow3)
{
	SetRow0(xRow0);
	SetRow1(xRow1);
	SetRow2(xRow2);
	SetRow3(xRow3);
}

inline void Asura_AMatrix4x4::Identity(void)
{
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	__asm
	{
		vmidt.q m_xMat;
	}
#elif ASURA_PSP_GCC_BUILTINS
	*reinterpret_cast< vfpu_m4x4 * >( this ) = vfpu_vmidt_q( 0 );
#else
	m_axColumns[0].SetElements(1.0f, 0.0f, 0.0f, 0.0f);
	m_axColumns[1].SetElements(0.0f, 1.0f, 0.0f, 0.0f);
	m_axColumns[2].SetElements(0.0f, 0.0f, 1.0f, 0.0f);
	m_axColumns[3].ZeroW1();
#endif
}

inline void Asura_AMatrix4x4::Zero(void)
{
	m_axColumns[0].ZeroW0();
	m_axColumns[1].ZeroW0();
	m_axColumns[2].ZeroW0();
	m_axColumns[3].ZeroW0();
}

inline void Asura_AMatrix4x4::ToMatrix(Asura_Matrix_3x3& xOrientation) const
{
	xOrientation.e11	= m_axColumns[0].GetX();
	xOrientation.e21	= m_axColumns[0].GetY();
	xOrientation.e31	= m_axColumns[0].GetZ();

	xOrientation.e12	= m_axColumns[1].GetX();
	xOrientation.e22	= m_axColumns[1].GetY();
	xOrientation.e32	= m_axColumns[1].GetZ();

	xOrientation.e13	= m_axColumns[2].GetX();
	xOrientation.e23	= m_axColumns[2].GetY();
	xOrientation.e33	= m_axColumns[2].GetZ();
}

inline void Asura_AMatrix4x4::FromMatrix(const Asura_Matrix_3x3& xOrientation)
{
	m_axColumns[0].SetElements(xOrientation.e11, xOrientation.e21, xOrientation.e31, 0.0f);
	m_axColumns[1].SetElements(xOrientation.e12, xOrientation.e22, xOrientation.e32, 0.0f);
	m_axColumns[2].SetElements(xOrientation.e13, xOrientation.e23, xOrientation.e33, 0.0f);
	m_axColumns[3].ZeroW1();
}

inline Asura_Vector_3 Asura_AMatrix4x4::GetTranslation() const
{
	return Asura_Vector_3(m_axColumns[3].GetX(), m_axColumns[3].GetY(), m_axColumns[3].GetZ());
}

inline void Asura_AMatrix4x4::SetTranslation(const Asura_Vector_3& xTranslation)
{
	m_axColumns[3]	= Asura_AVector4(xTranslation, 1.0f);
}

inline Asura_AVector3 Asura_AMatrix4x4::GetATranslation() const
{
	return Asura_AVector3(m_axColumns[3].GetX(), m_axColumns[3].GetY(), m_axColumns[3].GetZ());
}

inline void Asura_AMatrix4x4::SetTranslation(Asura_AVector3_InParam xTranslation)
{
	m_axColumns[3].SetElements(xTranslation.GetX(), xTranslation.GetY(), xTranslation.GetZ(), 1.0f);
}

inline Asura_AVector4 Asura_AMatrix4x4::GetA4Translation() const
{
	return m_axColumns[3];
}

inline void Asura_AMatrix4x4::SetTranslation(Asura_AVector4_InParam xTranslation)
{
	m_axColumns[3]	= xTranslation;
}

inline Asura_AMatrix4x4 Asura_AMatrix4x4::operator-(void) const
{
	Asura_AMatrix4x4	xTemp;

	xTemp.SetColumns(-m_axColumns[0], -m_axColumns[1], -m_axColumns[2], -m_axColumns[3]);
	return(xTemp);
}

inline Asura_AVector4 Asura_AMatrix4x4::Apply(Asura_AVector4_InParam xInput) const
{
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	Asura_AVector4 xOutput;
	__asm
	{
		vtfm4.q		xOutput.m_xVec, m_xMat( exx ), xInput.m_xVec
	};
	return xOutput;
#elif ASURA_PSP_GCC_BUILTINS
	Asura_AVector4 xOutput;
	xOutput.m_xVec = vfpu_vtfm4_q( reinterpret_cast< const vfpu_m4x4& >( *this ), xInput.m_xVec, VFPU_VS_E );
	return xOutput;
#else
	Asura_AVector4	axRows[4];

	// Extract the data...
	axRows[0] = GetRow0();
	axRows[1] = GetRow1();
	axRows[2] = GetRow2();
	axRows[3] = GetRow3();

	// Set each element of the result in turn...
	return Asura_AVector4((axRows[0] * xInput), (axRows[1] * xInput), (axRows[2] * xInput), (axRows[3] * xInput));
#endif
}

inline void Asura_AMatrix4x4::ApplyToArray(const Asura_AVector4* pxInput, const u_int uNumElements, Asura_AVector4* pxOutput) const
{
	Asura_AVector4	axRows[4];

	// Extract the data...
	axRows[0] = GetRow0();
	axRows[1] = GetRow1();
	axRows[2] = GetRow2();
	axRows[3] = GetRow3();

	for(u_int u = 0; u < uNumElements; u++, pxInput++, pxOutput++)
	{
		// Set each element of the result in turn...
		pxOutput->SetElements((axRows[0] * *pxInput), (axRows[1] * *pxInput), (axRows[2] * *pxInput), (axRows[3] * *pxInput));
	}
}

inline void Asura_AMatrix4x4::Apply(Asura_AMatrix4x4_InParam xInput, Asura_AMatrix4x4_OutParam xOutput) const
{
#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
	// RJT TODO: If we had more allocated registers we could use the vmmul.q instruction
	SNVectorType xA, xB, xC, xD;
	__asm
	{
		vtfm4.q		xA, m_xMat( exx ), xInput.m_xMat( cx )
		vtfm4.q		xB, m_xMat( exx ), xInput.m_xMat( cy )
		vtfm4.q		xC, m_xMat( exx ), xInput.m_xMat( cz )
		vtfm4.q		xD, m_xMat( exx ), xInput.m_xMat( cw )
	}
	xOutput.m_axColumns[ 0 ].m_xVec = xA;
	xOutput.m_axColumns[ 1 ].m_xVec = xB;
	xOutput.m_axColumns[ 2 ].m_xVec = xC;
	xOutput.m_axColumns[ 3 ].m_xVec = xD;
#elif ASURA_PSP_GCC_BUILTINS
	reinterpret_cast< vfpu_m4x4& >( xOutput ) = vfpu_vmmul_q( reinterpret_cast< const vfpu_m4x4& >( *this ), reinterpret_cast< const vfpu_m4x4& >( xInput ), 0 );
#else
	Asura_AVector4	axRows[4];
	Asura_AVector4	axColumns[4];

	// Extract the data...
	axRows[0] = GetRow0();
	axRows[1] = GetRow1();
	axRows[2] = GetRow2();
	axRows[3] = GetRow3();
	axColumns[0] = xInput.GetColumn0();
	axColumns[1] = xInput.GetColumn1();
	axColumns[2] = xInput.GetColumn2();
	axColumns[3] = xInput.GetColumn3();

	// Set each element of the result in turn...
	xOutput.SetColumn0(Asura_AVector4(axColumns[0] * axRows[0], axColumns[0] * axRows[1], axColumns[0] * axRows[2], axColumns[0] * axRows[3]));
	xOutput.SetColumn1(Asura_AVector4(axColumns[1] * axRows[0], axColumns[1] * axRows[1], axColumns[1] * axRows[2], axColumns[1] * axRows[3]));
	xOutput.SetColumn2(Asura_AVector4(axColumns[2] * axRows[0], axColumns[2] * axRows[1], axColumns[2] * axRows[2], axColumns[2] * axRows[3]));
	xOutput.SetColumn3(Asura_AVector4(axColumns[3] * axRows[0], axColumns[3] * axRows[1], axColumns[3] * axRows[2], axColumns[3] * axRows[3]));
#endif
}

inline void Asura_AMatrix4x4::ApplyToArray(const Asura_AMatrix4x4* pxInput, const u_int uNumElements, Asura_AMatrix4x4* pxOutput) const
{
	Asura_AVector4	axRows[4];

	// Extract the row-data...
	axRows[0] = GetRow0();
	axRows[1] = GetRow1();
	axRows[2] = GetRow2();
	axRows[3] = GetRow3();

	for(u_int u = 0; u < uNumElements; u++, pxInput++, pxOutput++)
	{
		Asura_AVector4	axColumns[4];

		// Extract the column-data...
		axColumns[0] = pxInput->GetColumn0();
		axColumns[1] = pxInput->GetColumn1();
		axColumns[2] = pxInput->GetColumn2();
		axColumns[3] = pxInput->GetColumn3();

		// Set each element of the result in turn...
		pxOutput->SetColumn0(Asura_AVector4(axColumns[0] * axRows[0], axColumns[0] * axRows[1], axColumns[0] * axRows[2], axColumns[0] * axRows[3]));
		pxOutput->SetColumn1(Asura_AVector4(axColumns[1] * axRows[0], axColumns[1] * axRows[1], axColumns[1] * axRows[2], axColumns[1] * axRows[3]));
		pxOutput->SetColumn2(Asura_AVector4(axColumns[2] * axRows[0], axColumns[2] * axRows[1], axColumns[2] * axRows[2], axColumns[2] * axRows[3]));
		pxOutput->SetColumn3(Asura_AVector4(axColumns[3] * axRows[0], axColumns[3] * axRows[1], axColumns[3] * axRows[2], axColumns[3] * axRows[3]));
	}
}

inline float Asura_AMatrix4x4::Cofactor(const u_int uRow, const u_int uCol) const
{
	static const u_int	aauIndices[4][3]	= { {1, 2, 3}, {0, 2, 3}, {0, 1, 3}, {0, 1, 2} };
	const u_int*		puRowsToUse			= aauIndices[uRow];
	const u_int*		puColsToUse			= aauIndices[uCol];

	// Calculate the determinant of the sub-matrix produced by ignoring row uRow and column uCol
	const float	fA	= GetElement(puRowsToUse[0], puColsToUse[0]);
	const float	fB	= GetElement(puRowsToUse[0], puColsToUse[1]);
	const float	fC	= GetElement(puRowsToUse[0], puColsToUse[2]);

	const float	fD	= GetElement(puRowsToUse[1], puColsToUse[0]);
	const float	fE	= GetElement(puRowsToUse[1], puColsToUse[1]);
	const float	fF	= GetElement(puRowsToUse[1], puColsToUse[2]);

	const float	fG	= GetElement(puRowsToUse[2], puColsToUse[0]);
	const float	fH	= GetElement(puRowsToUse[2], puColsToUse[1]);
	const float	fI	= GetElement(puRowsToUse[2], puColsToUse[2]);

	return( ((fA * fE * fI) + (fD * fH * fC) + (fG * fB * fF)) -
		((fA * fH * fF) + (fD * fB * fI) + (fG * fE * fC)) );
}

inline float Asura_AMatrix4x4::Determinant(void) const
{
	const float	fElement00	= GetElement(0, 0);
	const float	fElement01	= GetElement(0, 1);
	const float	fElement02	= GetElement(0, 2);
	const float	fElement03	= GetElement(0, 3);

	const float fCofactor00	= Cofactor(0, 0);
	const float fCofactor01	= Cofactor(0, 1);
	const float fCofactor02	= Cofactor(0, 2);
	const float fCofactor03	= Cofactor(0, 3);

	return((fElement00 * fCofactor00) - (fElement01 * fCofactor01) + (fElement02 * fCofactor02) - (fElement03 * fCofactor03));
}

inline void Asura_AMatrix4x4::Invert(Asura_AMatrix4x4_OutParam xOther) const
{
	const float			fDeterminant		= Determinant();
	const float			fOneOverDeterminant	= 1.0f / fDeterminant;
	Asura_AMatrix4x4	xResult;

	// Calculate using the adjoint matrix approach...
	Asura_Assert(fDeterminant != 0.f, "Trying to calculate the inverse of a non-invertable matrix");
	for(int iRow = 0; iRow < 4; ++iRow)
	{
		for(int iCol = 0; iCol < 4; ++iCol)
		{
			const float	fSign		= (((iRow + iCol) & 1) == 0) ? 1.0f : -1.0f;
			const float	fCofactor	= Cofactor(iRow, iCol);	// note flipped uCol/uRow

			xOther.SetElement(iCol, iRow, fSign * fCofactor * fOneOverDeterminant);
		}
	}
}

inline void Asura_AMatrix4x4::Invert(void)
{
	const Asura_AMatrix4x4	xTemp(*this);

	xTemp.Invert(*this);
}

inline void Asura_AMatrix4x4::InvertOrthogonal(Asura_AMatrix4x4_OutParam xOther) const
{
	// Create the transposed orientation matrix...
	xOther.m_axColumns[0].SetElements(m_axColumns[0].GetX(), m_axColumns[1].GetX(), m_axColumns[2].GetX(), 0.0f);
	xOther.m_axColumns[1].SetElements(m_axColumns[0].GetY(), m_axColumns[1].GetY(), m_axColumns[2].GetY(), 0.0f);
	xOther.m_axColumns[2].SetElements(m_axColumns[0].GetZ(), m_axColumns[1].GetZ(), m_axColumns[2].GetZ(), 0.0f);
	xOther.m_axColumns[3].ZeroW1();

	// ...apply the transpose to the position and subtract this from the final translation...
	xOther.m_axColumns[3]	-= m_axColumns[3] * xOther;
	xOther.m_axColumns[3].SetW(1.0f);
}

inline void Asura_AMatrix4x4::InvertOrthogonal(void)
{
	const Asura_AMatrix4x4	xTemp(*this);

	xTemp.InvertOrthogonal(*this);
}

inline void Asura_AMatrix4x4::Transpose(Asura_AMatrix4x4_OutParam xOther) const
{
	xOther.SetRows(m_axColumns[0], m_axColumns[1], m_axColumns[2], m_axColumns[3]);
}

inline void Asura_AMatrix4x4::Transpose(void)
{
	const Asura_AMatrix4x4	xTemp(*this);

	xTemp.Transpose(*this);
}

inline Asura_AMatrix4x4 Asura_AMatrix4x4::operator*(const float fScale) const
{
	Asura_AMatrix4x4	xTemp(*this);

	xTemp.m_axColumns[0]	*= fScale;
	xTemp.m_axColumns[1]	*= fScale;
	xTemp.m_axColumns[2]	*= fScale;
	xTemp.m_axColumns[3]	*= fScale;
	return(xTemp);
}

inline Asura_AMatrix4x4 Asura_AMatrix4x4::operator/(const float fScale) const
{
	const float			fOneOverScale	= 1.0f / fScale;
	Asura_AMatrix4x4	xTemp(*this);

	xTemp.m_axColumns[0]	*= fOneOverScale;
	xTemp.m_axColumns[1]	*= fOneOverScale;
	xTemp.m_axColumns[2]	*= fOneOverScale;
	xTemp.m_axColumns[3]	*= fOneOverScale;
	return(xTemp);
}

inline void Asura_AMatrix4x4::operator=(Asura_AMatrix4x4_InParam xM)
{
	m_axColumns[0]	= xM.m_axColumns[0];
	m_axColumns[1]	= xM.m_axColumns[1];
	m_axColumns[2]	= xM.m_axColumns[2];
	m_axColumns[3]	= xM.m_axColumns[3];
}

inline void Asura_AMatrix4x4::operator*=(const float fScale)
{
	m_axColumns[0]	*= fScale;
	m_axColumns[1]	*= fScale;
	m_axColumns[2]	*= fScale;
	m_axColumns[3]	*= fScale;
}

inline void Asura_AMatrix4x4::operator/=(const float fScale)
{
	const float	fOneOverScale	= 1.0f / fScale;

	m_axColumns[0]	*= fOneOverScale;
	m_axColumns[1]	*= fOneOverScale;
	m_axColumns[2]	*= fOneOverScale;
	m_axColumns[3]	*= fOneOverScale;
}

inline bool Asura_AMatrix4x4::operator==(Asura_AMatrix4x4_InParam xOther) const
{
	return (m_axColumns[0] == xOther.m_axColumns[0])
		&& (m_axColumns[1] == xOther.m_axColumns[1])
		&& (m_axColumns[2] == xOther.m_axColumns[2])
		&& (m_axColumns[3] == xOther.m_axColumns[3]);
}

inline bool Asura_AMatrix4x4::ApproximatelyEquals(Asura_AMatrix4x4_InParam xOther, const float fEpsilon) const
{
	return (m_axColumns[0].ApproximatelyEquals(xOther.m_axColumns[0], fEpsilon))
		&& (m_axColumns[1].ApproximatelyEquals(xOther.m_axColumns[1], fEpsilon))
		&& (m_axColumns[2].ApproximatelyEquals(xOther.m_axColumns[2], fEpsilon))
		&& (m_axColumns[3].ApproximatelyEquals(xOther.m_axColumns[3], fEpsilon));
}

inline bool Asura_AMatrix4x4::operator!=(Asura_AMatrix4x4_InParam xOther) const
{
	return (m_axColumns[0] != xOther.m_axColumns[0])
		|| (m_axColumns[1] != xOther.m_axColumns[1])
		|| (m_axColumns[2] != xOther.m_axColumns[2])
		|| (m_axColumns[3] != xOther.m_axColumns[3]);
}

inline float Asura_AMatrix4x4::GetElement(const u_int uRow, const u_int uCol) const
{
	Asura_Assert(uRow <= 3, "Invalid matrix row index passed to Asura_AMatrix4x4::GetElement");
	Asura_Assert(uCol <= 3, "Invalid matrix column index passed to Asura_AMatrix4x4::GetElement");
	return(m_axColumns[uCol].GetElement(uRow));
}

inline void Asura_AMatrix4x4::SetElement(const u_int uRow, const u_int uCol, const float fValue)
{ 
	Asura_Assert(uRow <= 3, "Invalid matrix row index passed to Asura_AMatrix4x4::SetElement");
	Asura_Assert(uCol <= 3, "Invalid matrix column index passed to Asura_AMatrix4x4::SetElement");
	m_axColumns[uCol].SetElement(uRow, fValue);
}

inline Asura_AVector4 Asura_AMatrix4x4::GetColumn(const u_int uColumnIndex) const
{
	Asura_Assert(uColumnIndex <= 3, "Invalid matrix column index passed to Asura_AMatrix4x4::GetColumn");
	return m_axColumns[uColumnIndex];
}

inline Asura_AVector4 Asura_AMatrix4x4::GetColumn0() const
{
	return m_axColumns[0];
}

inline Asura_AVector4 Asura_AMatrix4x4::GetColumn1() const
{
	return m_axColumns[1];
}

inline Asura_AVector4 Asura_AMatrix4x4::GetColumn2() const
{
	return m_axColumns[2];
}

inline Asura_AVector4 Asura_AMatrix4x4::GetColumn3() const
{
	return m_axColumns[3];
}

inline void Asura_AMatrix4x4::SetColumn(const u_int uColumnIndex, Asura_AVector4_InParam xColumn)
{
	Asura_Assert(uColumnIndex <= 3, "Invalid matrix column index passed to Asura_AMatrix4x4::SetColumn");
	m_axColumns[uColumnIndex] = xColumn;
}

inline void Asura_AMatrix4x4::SetColumn0(Asura_AVector4_InParam xCol0)
{
	m_axColumns[0] = xCol0;
}

inline void Asura_AMatrix4x4::SetColumn1(Asura_AVector4_InParam xCol1)
{
	m_axColumns[1] = xCol1;
}

inline void Asura_AMatrix4x4::SetColumn2(Asura_AVector4_InParam xCol2)
{
	m_axColumns[2] = xCol2;
}

inline void Asura_AMatrix4x4::SetColumn3(Asura_AVector4_InParam xCol3)
{
	m_axColumns[3] = xCol3;
}

inline Asura_AVector4 Asura_AMatrix4x4::GetRow(const u_int uRowIndex) const
{
	Asura_Assert(uRowIndex <= 3, "Invalid matrix row index passed to Asura_AMatrix4x4::GetRow");
	return Asura_AVector4(m_axColumns[0].GetElement(uRowIndex), m_axColumns[1].GetElement(uRowIndex),
						  m_axColumns[2].GetElement(uRowIndex), m_axColumns[3].GetElement(uRowIndex));
}

inline Asura_AVector4 Asura_AMatrix4x4::GetRow0() const
{
	return Asura_AVector4(m_axColumns[0].GetElement(0), m_axColumns[1].GetElement(0),
						  m_axColumns[2].GetElement(0), m_axColumns[3].GetElement(0));
}

inline Asura_AVector4 Asura_AMatrix4x4::GetRow1() const
{
	return Asura_AVector4(m_axColumns[0].GetElement(1), m_axColumns[1].GetElement(1),
						  m_axColumns[2].GetElement(1), m_axColumns[3].GetElement(1));
}

inline Asura_AVector4 Asura_AMatrix4x4::GetRow2() const
{
	return Asura_AVector4(m_axColumns[0].GetElement(2), m_axColumns[1].GetElement(2),
						  m_axColumns[2].GetElement(2), m_axColumns[3].GetElement(2));
}

inline Asura_AVector4 Asura_AMatrix4x4::GetRow3() const
{
	return Asura_AVector4(m_axColumns[0].GetElement(3), m_axColumns[1].GetElement(3),
						  m_axColumns[2].GetElement(3), m_axColumns[3].GetElement(3));
}

inline void Asura_AMatrix4x4::SetRow(const u_int uRowIndex, Asura_AVector4_InParam xRow)
{
	Asura_Assert(uRowIndex <= 3, "Invalid matrix row index passed to Asura_AMatrix4x4::SetRow");
	m_axColumns[0].SetElement(uRowIndex, xRow.GetElement(0));
	m_axColumns[1].SetElement(uRowIndex, xRow.GetElement(1));
	m_axColumns[2].SetElement(uRowIndex, xRow.GetElement(2));
	m_axColumns[3].SetElement(uRowIndex, xRow.GetElement(3));
}

inline void Asura_AMatrix4x4::SetRow0(Asura_AVector4_InParam xRow0)
{
	m_axColumns[0].SetElement(0, xRow0.GetElement(0));
	m_axColumns[1].SetElement(0, xRow0.GetElement(1));
	m_axColumns[2].SetElement(0, xRow0.GetElement(2));
	m_axColumns[3].SetElement(0, xRow0.GetElement(3));
}

inline void Asura_AMatrix4x4::SetRow1(Asura_AVector4_InParam xRow1)
{
	m_axColumns[0].SetElement(1, xRow1.GetElement(0));
	m_axColumns[1].SetElement(1, xRow1.GetElement(1));
	m_axColumns[2].SetElement(1, xRow1.GetElement(2));
	m_axColumns[3].SetElement(1, xRow1.GetElement(3));
}

inline void Asura_AMatrix4x4::SetRow2(Asura_AVector4_InParam xRow2)
{
	m_axColumns[0].SetElement(2, xRow2.GetElement(0));
	m_axColumns[1].SetElement(2, xRow2.GetElement(1));
	m_axColumns[2].SetElement(2, xRow2.GetElement(2));
	m_axColumns[3].SetElement(2, xRow2.GetElement(3));
}

inline void Asura_AMatrix4x4::SetRow3(Asura_AVector4_InParam xRow3)
{
	m_axColumns[0].SetElement(3, xRow3.GetElement(0));
	m_axColumns[1].SetElement(3, xRow3.GetElement(1));
	m_axColumns[2].SetElement(3, xRow3.GetElement(2));
	m_axColumns[3].SetElement(3, xRow3.GetElement(3));
}

inline void Asura_AMatrix4x4::LoadColsAligned(const float* pfFloats)
{
	m_axColumns[0].LoadAligned(&pfFloats[0]);
	m_axColumns[1].LoadAligned(&pfFloats[4]);
	m_axColumns[2].LoadAligned(&pfFloats[8]);
	m_axColumns[3].LoadAligned(&pfFloats[12]);
}

inline void Asura_AMatrix4x4::LoadRowsAligned(const float* pfFloats) 
{
	LoadColsAligned(pfFloats);
	Transpose();
}

inline void Asura_AMatrix4x4::LoadColsUnaligned(const float* pfFloats)
{
	m_axColumns[0].LoadUnaligned(&pfFloats[0]);
	m_axColumns[1].LoadUnaligned(&pfFloats[4]);
	m_axColumns[2].LoadUnaligned(&pfFloats[8]);
	m_axColumns[3].LoadUnaligned(&pfFloats[12]);
}

inline void Asura_AMatrix4x4::LoadRowsUnaligned(const float* pfFloats)
{
	LoadColsUnaligned(pfFloats);
	Transpose();
}

inline void Asura_AMatrix4x4::StoreColsAligned(float* pfFloats) const
{
	m_axColumns[0].StoreAligned(&pfFloats[0]);
	m_axColumns[1].StoreAligned(&pfFloats[4]);
	m_axColumns[2].StoreAligned(&pfFloats[8]);
	m_axColumns[3].StoreAligned(&pfFloats[12]);
}

inline void Asura_AMatrix4x4::StoreRowsAligned(float* pfFloats) const
{
	Asura_AMatrix4x4	xTemp;

	Transpose(xTemp);
	xTemp.StoreColsAligned(pfFloats);
}

inline void Asura_AMatrix4x4::StoreColsUnaligned(float* pfFloats) const
{
	m_axColumns[0].StoreUnaligned(&pfFloats[0]);
	m_axColumns[1].StoreUnaligned(&pfFloats[4]);
	m_axColumns[2].StoreUnaligned(&pfFloats[8]);
	m_axColumns[3].StoreUnaligned(&pfFloats[12]);
}

inline void Asura_AMatrix4x4::StoreRowsUnaligned(float* pfFloats) const
{
	Asura_AMatrix4x4	xTemp;

	Transpose(xTemp);
	xTemp.StoreColsUnaligned(pfFloats);
}

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

#endif


/// End of file. ///
