//
//  Test_quat_aos_neon.cpp
//  BulletTest
//
//  Copyright (c) 2011 Apple Inc., Inc.
//


#include "LinearMath/btScalar.h"
#if defined (BT_USE_SSE_IN_API) || defined (BT_USE_NEON)

#include "Test_quat_aos_neon.h"
#include "vector.h"
#include "Utils.h"
#include "main.h"

#include "../vectormath/vmInclude.h"


//typedef Vectormath::Aos::Vector3    vmVector3;
//typedef Vectormath::Aos::Quat       vmQuat;
//typedef Vectormath::Aos::Matrix3    vmMatrix3;
//typedef Vectormath::Aos::Transform3 vmTransform3;
//typedef Vectormath::Aos::Point3     vmPoint3;


typedef Vectormath::Aos::Vector4    vmVector4;

// reference code for testing purposes
ATTRIBUTE_ALIGNED16(class) Quat_ref
{
    float mX;
    float mY;
    float mZ;
    float mW;
    
public:
    // Default constructor; does no initialization
    // 
    inline Quat_ref( ) { };
    
    // Copy a quaternion
    // 
    inline Quat_ref( const Quat_ref & quat );
    
    // Construct a quaternion from x, y, z, and w elements
    // 
    inline Quat_ref( float x, float y, float z, float w );
    
    // Construct a quaternion from a 3-D vector and a scalar
    // 
    inline Quat_ref( const vmVector3 & xyz, float w );
    
    // Copy elements from a 4-D vector into a quaternion
    // 
    explicit inline Quat_ref( const vmVector4 & vec );
    
    // Convert a rotation matrix to a unit-length quaternion
    // 
    explicit inline Quat_ref( const vmMatrix3 & rotMat );
    
    // Set all elements of a quaternion to the same scalar value
    // 
    explicit inline Quat_ref( float scalar );
    
    // Assign one quaternion to another
    // 
    inline Quat_ref & operator =( const Quat_ref & quat );
    
    // Set the x, y, and z elements of a quaternion
    // NOTE: 
    // This function does not change the w element.
    // 
    inline Quat_ref & setXYZ( const vmVector3 & vec );
    
    // Get the x, y, and z elements of a quaternion
    // 
    inline const vmVector3 getXYZ( ) const;
    
    // Set the x element of a quaternion
    // 
    inline Quat_ref & setX( float x );
    
    // Set the y element of a quaternion
    // 
    inline Quat_ref & setY( float y );
    
    // Set the z element of a quaternion
    // 
    inline Quat_ref & setZ( float z );
    
    // Set the w element of a quaternion
    // 
    inline Quat_ref & setW( float w );
    
    // Get the x element of a quaternion
    // 
    inline float getX( ) const;
    
    // Get the y element of a quaternion
    // 
    inline float getY( ) const;
    
    // Get the z element of a quaternion
    // 
    inline float getZ( ) const;
    
    // Get the w element of a quaternion
    // 
    inline float getW( ) const;
    
    // Set an x, y, z, or w element of a quaternion by index
    // 
    inline Quat_ref & setElem( int idx, float value );
    
    // Get an x, y, z, or w element of a quaternion by index
    // 
    inline float getElem( int idx ) const;
    
    // Subscripting operator to set or get an element
    // 
    inline float & operator []( int idx );
    
    // Subscripting operator to get an element
    // 
    inline float operator []( int idx ) const;
    
    // Add two quaternions
    // 
    inline const Quat_ref operator +( const Quat_ref & quat ) const;
    
    // Subtract a quaternion from another quaternion
    // 
    inline const Quat_ref operator -( const Quat_ref & quat ) const;
    
    // Multiply two quaternions
    // 
    inline const Quat_ref operator *( const Quat_ref & quat ) const;
    
    // Multiply a quaternion by a scalar
    // 
    inline const Quat_ref operator *( float scalar ) const;
    
    // Divide a quaternion by a scalar
    // 
    inline const Quat_ref operator /( float scalar ) const;
    
    // Perform compound assignment and addition with a quaternion
    // 
    inline Quat_ref & operator +=( const Quat_ref & quat );
    
    // Perform compound assignment and subtraction by a quaternion
    // 
    inline Quat_ref & operator -=( const Quat_ref & quat );
    
    // Perform compound assignment and multiplication by a quaternion
    // 
    inline Quat_ref & operator *=( const Quat_ref & quat );
    
    // Perform compound assignment and multiplication by a scalar
    // 
    inline Quat_ref & operator *=( float scalar );
    
    // Perform compound assignment and division by a scalar
    // 
    inline Quat_ref & operator /=( float scalar );
    
    // Negate all elements of a quaternion
    // 
    inline const Quat_ref operator -( ) const;
    
    // Construct an identity quaternion
    // 
    static inline const Quat_ref identity( );
    
    // Construct a quaternion to rotate between two unit-length 3-D vectors
    // NOTE: 
    // The result is unpredictable if unitVec0 and unitVec1 point in opposite directions.
    // 
    static inline const Quat_ref rotation( const vmVector3 & unitVec0, const vmVector3 & unitVec1 );
    
    // Construct a quaternion to rotate around a unit-length 3-D vector
    // 
    static inline const Quat_ref rotation( float radians, const vmVector3 & unitVec );
    
    // Construct a quaternion to rotate around the x axis
    // 
    static inline const Quat_ref rotationX( float radians );
    
    // Construct a quaternion to rotate around the y axis
    // 
    static inline const Quat_ref rotationY( float radians );
    
    // Construct a quaternion to rotate around the z axis
    // 
    static inline const Quat_ref rotationZ( float radians );
    
};

inline Quat_ref::Quat_ref( const Quat_ref & quat )
{
    mX = quat.mX;
    mY = quat.mY;
    mZ = quat.mZ;
    mW = quat.mW;
}

inline Quat_ref::Quat_ref( float _x, float _y, float _z, float _w )
{
    mX = _x;
    mY = _y;
    mZ = _z;
    mW = _w;
}

inline Quat_ref::Quat_ref( const vmVector3 & xyz, float _w )
{
    this->setXYZ( xyz );
    this->setW( _w );
}

inline Quat_ref::Quat_ref( const vmVector4 & vec )
{
    mX = vec.getX();
    mY = vec.getY();
    mZ = vec.getZ();
    mW = vec.getW();
}

inline Quat_ref::Quat_ref( float scalar )
{
    mX = scalar;
    mY = scalar;
    mZ = scalar;
    mW = scalar;
}

inline const Quat_ref Quat_ref::identity( )
{
    return Quat_ref( 0.0f, 0.0f, 0.0f, 1.0f );
}


inline void loadXYZW_ref( Quat_ref & quat, const float * fptr )
{
    quat = Quat_ref( fptr[0], fptr[1], fptr[2], fptr[3] );
}

inline void storeXYZW_ref( const Quat_ref & quat, float * fptr )
{
    fptr[0] = quat.getX();
    fptr[1] = quat.getY();
    fptr[2] = quat.getZ();
    fptr[3] = quat.getW();
}

inline Quat_ref & Quat_ref::operator =( const Quat_ref & quat )
{
    mX = quat.mX;
    mY = quat.mY;
    mZ = quat.mZ;
    mW = quat.mW;
    return *this;
}

inline Quat_ref & Quat_ref::setXYZ( const vmVector3 & vec )
{
    mX = vec.getX();
    mY = vec.getY();
    mZ = vec.getZ();
    return *this;
}

inline const vmVector3 Quat_ref::getXYZ( ) const
{
    return vmVector3( mX, mY, mZ );
}

inline Quat_ref & Quat_ref::setX( float _x )
{
    mX = _x;
    return *this;
}

inline float Quat_ref::getX( ) const
{
    return mX;
}

inline Quat_ref & Quat_ref::setY( float _y )
{
    mY = _y;
    return *this;
}

inline float Quat_ref::getY( ) const
{
    return mY;
}

inline Quat_ref & Quat_ref::setZ( float _z )
{
    mZ = _z;
    return *this;
}

inline float Quat_ref::getZ( ) const
{
    return mZ;
}

inline Quat_ref & Quat_ref::setW( float _w )
{
    mW = _w;
    return *this;
}

inline float Quat_ref::getW( ) const
{
    return mW;
}

inline Quat_ref & Quat_ref::setElem( int idx, float value )
{
    *(&mX + idx) = value;
    return *this;
}

inline float Quat_ref::getElem( int idx ) const
{
    return *(&mX + idx);
}

inline float & Quat_ref::operator []( int idx )
{
    return *(&mX + idx);
}

inline float Quat_ref::operator []( int idx ) const
{
    return *(&mX + idx);
}

inline const Quat_ref Quat_ref::operator +( const Quat_ref & quat ) const
{
    return Quat_ref(
                ( mX + quat.mX ),
                ( mY + quat.mY ),
                ( mZ + quat.mZ ),
                ( mW + quat.mW )
                );
}

inline const Quat_ref Quat_ref::operator -( const Quat_ref & quat ) const
{
    return Quat_ref(
                ( mX - quat.mX ),
                ( mY - quat.mY ),
                ( mZ - quat.mZ ),
                ( mW - quat.mW )
                );
}

inline const Quat_ref Quat_ref::operator *( float scalar ) const
{
    return Quat_ref(
                ( mX * scalar ),
                ( mY * scalar ),
                ( mZ * scalar ),
                ( mW * scalar )
                );
}

inline Quat_ref & Quat_ref::operator +=( const Quat_ref & quat )
{
    *this = *this + quat;
    return *this;
}

inline Quat_ref & Quat_ref::operator -=( const Quat_ref & quat )
{
    *this = *this - quat;
    return *this;
}

inline Quat_ref & Quat_ref::operator *=( float scalar )
{
    *this = *this * scalar;
    return *this;
}

inline const Quat_ref Quat_ref::operator /( float scalar ) const
{
    return Quat_ref(
                ( mX / scalar ),
                ( mY / scalar ),
                ( mZ / scalar ),
                ( mW / scalar )
                );
}

inline Quat_ref & Quat_ref::operator /=( float scalar )
{
    *this = *this / scalar;
    return *this;
}

inline const Quat_ref Quat_ref::operator -( ) const
{
    return Quat_ref(
                -mX,
                -mY,
                -mZ,
                -mW
                );
}

inline const Quat_ref operator *( float scalar, const Quat_ref & quat )
{
    return quat * scalar;
}

inline float dot( const Quat_ref & quat0, const Quat_ref & quat1 )
{
    float result;
    result = ( quat0.getX() * quat1.getX() );
    result = ( result + ( quat0.getY() * quat1.getY() ) );
    result = ( result + ( quat0.getZ() * quat1.getZ() ) );
    result = ( result + ( quat0.getW() * quat1.getW() ) );
    return result;
}

inline const Quat_ref lerp( float t, const Quat_ref & quat0, const Quat_ref & quat1 )
{
    return ( quat0 + ( ( quat1 - quat0 ) * t ) );
}

inline const Quat_ref slerp( float t, const Quat_ref & unitQuat0, const Quat_ref & unitQuat1 )
{
    Quat_ref start;
    float recipSinAngle, scale0, scale1, cosAngle, angle;
    cosAngle = dot( unitQuat0, unitQuat1 );
    if ( cosAngle < 0.0f ) {
        cosAngle = -cosAngle;
        start = ( -unitQuat0 );
    } else {
        start = unitQuat0;
    }
    if ( cosAngle < _VECTORMATH_SLERP_TOL ) {
        angle = acosf( cosAngle );
        recipSinAngle = ( 1.0f / sinf( angle ) );
        scale0 = ( sinf( ( ( 1.0f - t ) * angle ) ) * recipSinAngle );
        scale1 = ( sinf( ( t * angle ) ) * recipSinAngle );
    } else {
        scale0 = ( 1.0f - t );
        scale1 = t;
    }
    return ( ( start * scale0 ) + ( unitQuat1 * scale1 ) );
}

inline const Quat_ref squad( float t, const Quat_ref & unitQuat0, const Quat_ref & unitQuat1, const Quat_ref & unitQuat2, const Quat_ref & unitQuat3 )
{
    Quat_ref tmp0, tmp1;
    tmp0 = slerp( t, unitQuat0, unitQuat3 );
    tmp1 = slerp( t, unitQuat1, unitQuat2 );
    return slerp( ( ( 2.0f * t ) * ( 1.0f - t ) ), tmp0, tmp1 );
}

inline float norm( const Quat_ref & quat )
{
    float result;
    result = ( quat.getX() * quat.getX() );
    result = ( result + ( quat.getY() * quat.getY() ) );
    result = ( result + ( quat.getZ() * quat.getZ() ) );
    result = ( result + ( quat.getW() * quat.getW() ) );
    return result;
}

inline float length( const Quat_ref & quat )
{
    return ::sqrtf( norm( quat ) );
}

inline const Quat_ref normalize( const Quat_ref & quat )
{
    float lenSqr, lenInv;
    lenSqr = norm( quat );
    lenInv = ( 1.0f / sqrtf( lenSqr ) );
    return Quat_ref(
                ( quat.getX() * lenInv ),
                ( quat.getY() * lenInv ),
                ( quat.getZ() * lenInv ),
                ( quat.getW() * lenInv )
                );
}

inline const Quat_ref Quat_ref::rotation( const vmVector3 & unitVec0, const vmVector3 & unitVec1 )
{
    float cosHalfAngleX2, recipCosHalfAngleX2;
    cosHalfAngleX2 = sqrtf( ( 2.0f * ( 1.0f + dot( unitVec0, unitVec1 ) ) ) );
    recipCosHalfAngleX2 = ( 1.0f / cosHalfAngleX2 );
    return Quat_ref( ( cross( unitVec0, unitVec1 ) * recipCosHalfAngleX2 ), ( cosHalfAngleX2 * 0.5f ) );
}

inline const Quat_ref Quat_ref::rotation( float radians, const vmVector3 & unitVec )
{
    float s, c, angle;
    angle = ( radians * 0.5f );
    s = sinf( angle );
    c = cosf( angle );
    return Quat_ref( ( unitVec * s ), c );
}

inline const Quat_ref Quat_ref::rotationX( float radians )
{
    float s, c, angle;
    angle = ( radians * 0.5f );
    s = sinf( angle );
    c = cosf( angle );
    return Quat_ref( s, 0.0f, 0.0f, c );
}

inline const Quat_ref Quat_ref::rotationY( float radians )
{
    float s, c, angle;
    angle = ( radians * 0.5f );
    s = sinf( angle );
    c = cosf( angle );
    return Quat_ref( 0.0f, s, 0.0f, c );
}

inline const Quat_ref Quat_ref::rotationZ( float radians )
{
    float s, c, angle;
    angle = ( radians * 0.5f );
    s = sinf( angle );
    c = cosf( angle );
    return Quat_ref( 0.0f, 0.0f, s, c );
}

inline const Quat_ref Quat_ref::operator *( const Quat_ref & quat ) const
{
    return Quat_ref(
                ( ( ( ( mW * quat.mX ) + ( mX * quat.mW ) ) + ( mY * quat.mZ ) ) - ( mZ * quat.mY ) ),
                ( ( ( ( mW * quat.mY ) + ( mY * quat.mW ) ) + ( mZ * quat.mX ) ) - ( mX * quat.mZ ) ),
                ( ( ( ( mW * quat.mZ ) + ( mZ * quat.mW ) ) + ( mX * quat.mY ) ) - ( mY * quat.mX ) ),
                ( ( ( ( mW * quat.mW ) - ( mX * quat.mX ) ) - ( mY * quat.mY ) ) - ( mZ * quat.mZ ) )
                );
}

inline Quat_ref & Quat_ref::operator *=( const Quat_ref & quat )
{
    *this = *this * quat;
    return *this;
}

inline const vmVector3 rotate( const Quat_ref & quat, const vmVector3 & vec )
{
    float tmpX, tmpY, tmpZ, tmpW;
    tmpX = ( ( ( quat.getW() * vec.getX() ) + ( quat.getY() * vec.getZ() ) ) - ( quat.getZ() * vec.getY() ) );
    tmpY = ( ( ( quat.getW() * vec.getY() ) + ( quat.getZ() * vec.getX() ) ) - ( quat.getX() * vec.getZ() ) );
    tmpZ = ( ( ( quat.getW() * vec.getZ() ) + ( quat.getX() * vec.getY() ) ) - ( quat.getY() * vec.getX() ) );
    tmpW = ( ( ( quat.getX() * vec.getX() ) + ( quat.getY() * vec.getY() ) ) + ( quat.getZ() * vec.getZ() ) );
    return vmVector3(
                   ( ( ( ( tmpW * quat.getX() ) + ( tmpX * quat.getW() ) ) - ( tmpY * quat.getZ() ) ) + ( tmpZ * quat.getY() ) ),
                   ( ( ( ( tmpW * quat.getY() ) + ( tmpY * quat.getW() ) ) - ( tmpZ * quat.getX() ) ) + ( tmpX * quat.getZ() ) ),
                   ( ( ( ( tmpW * quat.getZ() ) + ( tmpZ * quat.getW() ) ) - ( tmpX * quat.getY() ) ) + ( tmpY * quat.getX() ) )
                   );
}

inline const Quat_ref conj( const Quat_ref & quat )
{
    return Quat_ref( -quat.getX(), -quat.getY(), -quat.getZ(), quat.getW() );
}

inline const Quat_ref select( const Quat_ref & quat0, const Quat_ref & quat1, bool select1 )
{
    return Quat_ref(
                ( select1 )? quat1.getX() : quat0.getX(),
                ( select1 )? quat1.getY() : quat0.getY(),
                ( select1 )? quat1.getZ() : quat0.getZ(),
                ( select1 )? quat1.getW() : quat0.getW()
                );
}



#define LOOPCOUNT 1000
#define NUM_CYCLES 10000
#define DATA_SIZE 1024

int Test_quat_aos_neon(void)
{
       
    return 0;
}

#endif

