/** 
 * @file llvector4a.h
 * @brief LLVector4a class header file - memory aligned and vectorized 4 component vector
 *
 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef	LL_LLVECTOR4A_H
#define	LL_LLVECTOR4A_H


class LLRotation;

#include <assert.h>
#include "llpreprocessor.h"
#include "llmemory.h"

///////////////////////////////////
// FIRST TIME USERS PLEASE READ
//////////////////////////////////
// This is just the beginning of LLVector4a. There are many more useful functions
// yet to be implemented. For example, setNeg to negate a vector, rotate() to apply
// a matrix rotation, various functions to manipulate only the X, Y, and Z elements
// and many others (including a whole variety of accessors). So if you don't see a 
// function here that you need, please contact Falcon or someone else with SSE 
// experience (Richard, I think, has some and davep has a little as of the time 
// of this writing, July 08, 2010) about getting it implemented before you resort to
// LLVector3/LLVector4. 
/////////////////////////////////

class alignas(16) LLVector4a
{
    LL_ALIGN_NEW
public:

	///////////////////////////////////
	// STATIC METHODS
	///////////////////////////////////
	
	// Call initClass() at startup to avoid 15,000+ cycle penalties from denormalized numbers
	static void initClass()
	{
		_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
		_MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST);
	}

	// Return a vector of all zeros
	static inline const LLVector4a& getZero()
	{
		extern const LLVector4a LL_V4A_ZERO;
		return LL_V4A_ZERO;
	}
	
	// Return a vector of all epsilon, where epsilon is a small float suitable for approximate equality checks
	static inline const LLVector4a& getEpsilon()
	{
		extern const LLVector4a LL_V4A_EPSILON;
		return LL_V4A_EPSILON;
	}

	// Copy 16 bytes from src to dst. Source and destination must be 16-byte aligned
	static inline void copy4a(F32* dst, const F32* src)
	{
		_mm_store_ps(dst, _mm_load_ps(src));
	}

	// Copy words 16-byte blocks from src to dst. Source and destination must not overlap. 
	// Source and dest must be 16-byte aligned and size must be multiple of 16.
	static void memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes);

	////////////////////////////////////
	// CONSTRUCTORS 
	////////////////////////////////////
	
#ifdef SHOW_ASSERT
	//LLVector4a is plain data which should never have a default constructor or destructor(malloc&free won't trigger it)
	LLVector4a()
	{ //DO NOT INITIALIZE -- The overhead is completely unnecessary
		ll_assert_aligned(this,16);
	}
#else
	LLVector4a() = default;
#endif
	
	LLVector4a(F32 x, F32 y, F32 z, F32 w = 0.f)
	{
		set(x,y,z,w);
	}
	
	LLVector4a(F32 x)
	{
		splat(x);
	}
	
	LLVector4a(const LLSimdScalar& x)
	{
		splat(x);
	}

	LLVector4a(LLQuad q)
	{
		mQ = q;
	}

	////////////////////////////////////
	// LOAD/STORE
	////////////////////////////////////
	
	// Load from 16-byte aligned src array (preferred method of loading)
	inline void load4a(const F32* src);
	
	// Load from unaligned src array (NB: Significantly slower than load4a)
	inline void loadua(const F32* src);
	
	// Load only three floats beginning at address 'src'. Slowest method.
	inline void load3(const F32* src);
	
	// Store to a 16-byte aligned memory address
	inline void store4a(F32* dst) const;
	
	////////////////////////////////////
	// BASIC GET/SET 
	////////////////////////////////////
	
	// Return a "this" as an F32 pointer.
	inline F32* getF32ptr();
	
	// Return a "this" as a const F32 pointer.
	inline const F32* const getF32ptr() const;
	
	// Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates
	// the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead
	inline F32 operator[](const S32 idx) const;

	// Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time.
	inline LLSimdScalar getScalarAt(const S32 idx) const;

	// Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time.
	template <int N> LL_FORCE_INLINE LLSimdScalar getScalarAt() const;

	// Set to an x, y, z and optional w provided
	inline void set(F32 x, F32 y, F32 z, F32 w = 0.f);
	
	// Set to all zeros. This is preferred to using ::getZero()
	inline void clear();
	
	// Set all elements to 'x'
	inline void splat(const F32 x);

	// Set all elements to 'x'
	inline void splat(const LLSimdScalar& x);
	
	// Set all 4 elements to element N of src, with N known at compile time
	template <int N> void splat(const LLVector4a& src);
	
	// Set all 4 elements to element i of v, with i NOT known at compile time
	inline void splat(const LLVector4a& v, U32 i);
	
	// Select bits from sourceIfTrue and sourceIfFalse according to bits in mask
	inline void setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse );
	
	////////////////////////////////////
	// ALGEBRAIC
	////////////////////////////////////
	
	// Set this to the element-wise (a + b)
	inline void setAdd(const LLVector4a& a, const LLVector4a& b);
	
	// Set this to element-wise (a - b)
	inline void setSub(const LLVector4a& a, const LLVector4a& b);
	
	// Set this to element-wise multiply (a * b)
	inline void setMul(const LLVector4a& a, const LLVector4a& b);
	
	// Set this to element-wise quotient (a / b)
	inline void setDiv(const LLVector4a& a, const LLVector4a& b);
	
	// Set this to the element-wise absolute value of src
	inline void setAbs(const LLVector4a& src);
	
	// Add to each component in this vector the corresponding component in rhs
	inline void add(const LLVector4a& rhs);
	
	// Subtract from each component in this vector the corresponding component in rhs
	inline void sub(const LLVector4a& rhs);
	
	// Multiply each component in this vector by the corresponding component in rhs
	inline void mul(const LLVector4a& rhs);
	
	// Divide each component in this vector by the corresponding component in rhs
	inline void div(const LLVector4a& rhs);
	
	// Multiply this vector by x in a scalar fashion
	inline void mul(const F32 x);

	// Set this to (a x b) (geometric cross-product)
	inline void setCross3(const LLVector4a& a, const LLVector4a& b);
	
	// Set all elements to the dot product of the x, y, and z elements in a and b
	inline void setAllDot3(const LLVector4a& a, const LLVector4a& b);

	// Set all elements to the dot product of the x, y, z, and w elements in a and b
	inline void setAllDot4(const LLVector4a& a, const LLVector4a& b);

	// Return the 3D dot product of this vector and b
	inline LLSimdScalar dot3(const LLVector4a& b) const;

	// Return the 4D dot product of this vector and b
	inline LLSimdScalar dot4(const LLVector4a& b) const;

	// Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed
	// Note that this does not consider zero length vectors!
	inline void normalize3();

	// Same as normalize3() but with respect to all 4 components
	inline void normalize4();

	// Same as normalize3(), but returns length as a SIMD scalar
	inline LLSimdScalar normalize3withLength();

	// Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed
	// Note that this does not consider zero length vectors!
	inline void normalize3fast();

	// Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed
	// Same as above except substitutes default vector contents if the vector is non-finite or degenerate due to zero length.
	//
	inline void normalize3fast_checked(LLVector4a* d = 0);

	// Return true if this vector is normalized with respect to x,y,z up to tolerance
	inline LLBool32 isNormalized3( F32 tolerance = 1e-3 ) const;

	// Return true if this vector is normalized with respect to all components up to tolerance
	inline LLBool32 isNormalized4( F32 tolerance = 1e-3 ) const;

	// Set all elements to the length of vector 'v' 
	inline void setAllLength3( const LLVector4a& v );

	// Get this vector's length
	inline LLSimdScalar getLength3() const;
	
	// Set the components of this vector to the minimum of the corresponding components of lhs and rhs
	inline void setMin(const LLVector4a& lhs, const LLVector4a& rhs);
	
	// Set the components of this vector to the maximum of the corresponding components of lhs and rhs
	inline void setMax(const LLVector4a& lhs, const LLVector4a& rhs);
	
	// Clamps this vector to be within the component-wise range low to high (inclusive)
	inline void clamp( const LLVector4a& low, const LLVector4a& high );

	// Set this to  (c * lhs) + rhs * ( 1 - c)
	inline void setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c);
	
	// Return true (nonzero) if x, y, z (and w for Finite4) are all finite floats
	inline LLBool32 isFinite3() const;	
	inline LLBool32 isFinite4() const;

	// Set this vector to 'vec' rotated by the LLRotation or LLQuaternion2 provided
	void setRotated( const LLRotation& rot, const LLVector4a& vec );
	void setRotated( const class LLQuaternion2& quat, const LLVector4a& vec );

	// Set this vector to 'vec' rotated by the INVERSE of the LLRotation or LLQuaternion2 provided
	inline void setRotatedInv( const LLRotation& rot, const LLVector4a& vec );
	inline void setRotatedInv( const class LLQuaternion2& quat, const LLVector4a& vec );

	// Quantize this vector to 8 or 16 bit precision
	void quantize8( const LLVector4a& low, const LLVector4a& high );
	void quantize16( const LLVector4a& low, const LLVector4a& high );

	////////////////////////////////////
	// LOGICAL
	////////////////////////////////////	
	// The functions in this section will compare the elements in this vector
	// to those in rhs and return an LLVector4Logical with all bits set in elements
	// where the comparison was true and all bits unset in elements where the comparison
	// was false. See llvector4logica.h
	////////////////////////////////////
	// WARNING: Other than equals3 and equals4, these functions do NOT account
	// for floating point tolerance. You should include the appropriate tolerance
	// in the inputs.
	////////////////////////////////////
	
	inline LLVector4Logical greaterThan(const LLVector4a& rhs) const;

	inline LLVector4Logical lessThan(const LLVector4a& rhs) const;
	
	inline LLVector4Logical greaterEqual(const LLVector4a& rhs) const;

	inline LLVector4Logical lessEqual(const LLVector4a& rhs) const;
	
	inline LLVector4Logical equal(const LLVector4a& rhs) const;

	// Returns true if this and rhs are componentwise equal up to the specified absolute tolerance
	inline bool equals4(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const;

	inline bool equals3(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const;

	////////////////////////////////////
	// OPERATORS
	////////////////////////////////////	
	
	// Do NOT add aditional operators without consulting someone with SSE experience
	inline const LLVector4a& operator= ( const LLQuad& rhs );

	inline operator LLQuad() const;	
    
private:
	LLQuad mQ;
};

#ifndef SHOW_ASSERT
static_assert(std::is_trivial<LLVector4a>::value, "LLVector4a must be a trivial type");
static_assert(std::is_standard_layout<LLVector4a>::value, "LLVector4a must be a standard layout type");
#endif

inline void update_min_max(LLVector4a& min, LLVector4a& max, const LLVector4a& p)
{
	min.setMin(min, p);
	max.setMax(max, p);
}

inline std::ostream& operator<<(std::ostream& s, const LLVector4a& v)
{
    s << "(" << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << ")";
    return s;
}
#endif
