/*
	Trig Wars.
	Copyright (c) 2008, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef GAME_FLOAT_H
#define GAME_FLOAT_H

//=====================================================================================================================
//=====================================================================================================================

#include "Base\Assertion.h"
#include <cmath>

//=====================================================================================================================
//=====================================================================================================================

namespace Game
{
	//=================================================================================================================
	/// @brief		Very simple class containing utilities for floating point numbers. Can be used to ensure that 
	///				floating point numbers are valid.
	//=================================================================================================================

	class Float
	{	
		public:

			//=========================================================================================================
			// Functions
			//=========================================================================================================
				
				//=====================================================================================================
				/// @brief		Compares two floats for equality with a certain margin for error
				/// @param		v1 - 1st number
				/// @param		v2 - 2nd number
				/// @param		e - margin for error. 0 by default
				/// @returns	result of the comparison
				/// @pre		Given numbers must be valid floats
				/// @pre		Error margin must be at least zero
				//=====================================================================================================
				
				static __forceinline bool Equals( float v1 , float v2 , float e = 0.0f )
				{ 				
					// Debug assertions:
					
					ENSURE( IsValid(v1) , "Given numbers must be valid floats" );
					ENSURE( IsValid(v2) , "Given numbers must be valid floats" );
					ENSURE( e >= 0		, "Error margin must be at least zero" );
				
					// Get difference between the numbers
					
					float diff = v1 - v2;
					
					// Make it positive:
					
					diff = Abs(diff);
					
					// See if within range:
					
					if ( diff <= e ) return true; else return false;
				}				
				
				//=====================================================================================================
				/// @brief		Inline assembly function that computes the inverse square root of a given float.
				///				This uses the SSE instructions to do so, which are very fast and which use hardware
				///				LUTs to speed up the square root approximation.
				/// @remarks	This is not an exact square root, but close enough. It may suffice for uses that do 
				///				not require 100% accuracy.
				//=====================================================================================================
				
				static __forceinline float FastInvSqrt( float v )
				{
					// Store the result here:
					
					float result;
					
					// Use SSE to get the inverse or reciprocal square root: this is approximate
				
					__asm
					{
						rsqrtss xmm0	, v
						movss	result	, xmm0
					}	
					
					// Return the result
					
					return result;
				}				
				
				//=====================================================================================================
				/// @brief		Returns the square root of this number
				/// @param		v - number to get the square root of
				/// @returns	A new float
				/// @pre		Given number must not be less than zero
				/// @pre		Given number must not be a NaN or infinite number
				//=====================================================================================================
				
				static __forceinline float Sqrt( float v )
				{
					ENSURE( IsValid(v)	, "Given number must not be a NaN or infinite number"	);
					ENSURE( v >= 0		, "Given number must not be less than zero"				);
					
					// Store result here:
					
					float result;
					
					// Use SSE to get the square root:
					
					__asm
					{
						sqrtss xmm0   , v
						movss  result , xmm0
					}	
				
					return result;
				}
				
				//=====================================================================================================
				/// @brief		Returns the square of the given number
				/// @param		v - number to get the square of
				/// @returns	Square of the given number
				/// @pre		Given number must be valid
				//=====================================================================================================
				
				static __forceinline float Square( float v )
				{
					ENSURE( IsValid(v) , "Given number must be valid" );
				
					return v * v;
				}
				
				//=====================================================================================================
				/// @brief		Truncates this floating point number to the next whole number down
				/// @param		v - number to get the ceiling of
				/// @returns	A new float that is truncated
				/// @pre		Given number must be valid
				//=====================================================================================================
				
				static __forceinline float Floor( float v )
				{
					ENSURE( IsValid(v) , "Given number must be valid" );
				
					return float((int)(v));
				}
				
				//=====================================================================================================
				/// @brief		Truncates this floating point number to the next whole number up, or to the next 
				///				whole number down if the number is negative.
				/// @param		v - number to get the ceiling of
				/// @returns	A new float that is truncated
				/// @pre		Given number must be valid
				//=====================================================================================================
				
				static __forceinline float Ceil( float v )
				{
					ENSURE( IsValid(v) , "Given number must be valid" );
					
					if ( v < 0 )
					{				
						return float( (int)(v) - 1.0f );
					}
					else
					{
						return float( (int)(v) + 1.0f );
					}					
				}
				
				//=====================================================================================================
				/// @brief		Returns the fractional part of the given number.
				/// @param		v - number to get the fractional part of
				/// @returns	Fractional part of the number
				/// @pre		Given number must be valid
				/// @remarks	Given fraction is in the sign of the given number
				//=====================================================================================================
				
				static __forceinline float Frac( float v )
				{
					ENSURE( IsValid(v) , "Given number must be valid" );
					
					if ( v < 1 )
					{				
						return v;
					}
					else
					{
						return v - float((int)(v));
					}					
				}
				
				//=====================================================================================================
				/// @brief		Returns the modulus or remainder from division of the two given numbers.
				/// @param		v1 - 1st number - dividend
				/// @param		v2 - 2nd number - divisor
				/// @returns	Floating point remainder from division
				/// @pre		Given numbers must be valid
				/// @pre		Second number cannot be 0
				//=====================================================================================================
				
				// Ignore this warning: inaccurate on some pentium 1s for the fprem instruction. It's fine.. 
				
				#pragma warning(disable:4725)						
				
				static __forceinline float Mod( float v1 , float v2 )
				{
					ENSURE( IsValid(v1) , "Given numbers must be valid" );
					ENSURE( IsValid(v2) , "Given numbers must be valid" );
					ENSURE( v2 !=  0	, "Second number cannot be 0"	);
					
					// Store the result here
					
					float result;
					
					// Do the modulus
					
					__asm
					{							
						fld			v2
						fld			v1
						fprem	
						fstp		result
						ffree		st(0)
						fincstp															
					}
				
					// Return the result
					
					return result;
				}	
				
				// Done ignoring
				
				#pragma warning(default:4725)
				
				//=====================================================================================================
				/// @brief		Rounds this floating point number to the given number of decimal places
				/// @param		v - number to round
				/// @param		places - number of decimal places to round to
				/// @returns	A new float that is rounded
				/// @pre		The number must be valid and not a NaN or infinity
				/// @remarks	This function uses normal rounding rules in that it is rounded up or down to the nearest
				///				number to the number specified places.
				//=====================================================================================================
				
				static __forceinline float Round( float v , unsigned places )
				{
					#ifdef DEBUG
						
						ENSURE( IsValid(v) , "This number must be valid" );
					
					#endif
					
					// Calculate 10 to the power of the number of places:
					
					float p = Float::Pow(10,(float)(places));
					
					// Multiply number by given number of places
					
					v *= p;
					
					// If fractional part is >= 0.5 then add another unit
					
					if ( Frac(v) >= 0.5f ) v += 1.0f;
					
					// Truncate:
					
					v = float((int)(v));
					
					// Now bring back to normal range and return
					
					return v / p;
				}
				
				//=====================================================================================================
				/// @brief		Returns the absolute value of this float. The absolute value is always positive.
				/// @returns	A new float
				/// @pre		Number given must not be a NaN
				//=====================================================================================================
				
				static __forceinline float Abs( float v )
				{
					ENSURE( IsNaN(v) == false , "Number given must not be a NaN" );
					
					// Store result here
					
					float result;
					
					// Do the abs
					
					__asm
					{
						push	ecx	
						mov		ecx		, v
						and		ecx		, 7fffffffh
						mov		result	, ecx
						pop		ecx
					}
					
					// Return the result
					
					return result;
				}
				
				//=====================================================================================================
				/// @brief		Returns the inverse cosine of this float.
				/// @pre		Number given must be valid
				/// @pre		Number given must be in -1 to 1 range
				//=====================================================================================================
				
				static __forceinline float InvCos( float v )
				{
					ENSURE( IsValid(v)				, "Number given must be valid"				);
					ENSURE( v >= -1.0f && v <= 1.0f	, "Number given must be in -1 to 1 range"	);
				
					return acosf(v);
				}

				//=====================================================================================================
				/// @brief		Returns the cosine of this float.
				/// @pre		Number given must be valid
				//=====================================================================================================
				
				static __forceinline float Cos( float v )
				{
					ENSURE( IsValid(v) , "Number given must be valid" );
					
					// Store the result here:
					
					float result;
					
					// Do the cosine
					
					_asm
					{
						fld		v
						fcos	
						fstp	result
					}
					
					// Return the result
				
					return result;
				}				
				
				//=====================================================================================================
				/// @brief		Returns the sine of this float.
				/// @pre		Number given must be valid
				//=====================================================================================================
				
				static __forceinline float Sin( float v )
				{
					ENSURE( IsValid(v) , "Number given must be valid" );
				
					// Store the result here:
					
					float result;
					
					// Do the sine
					
					_asm
					{
						fld		v
						fsin	
						fstp	result
					}
					
					// Return the result
				
					return result;
				}			
				
				//=====================================================================================================
				/// @brief		Returns the tan of this float.
				/// @pre		Number given must be valid
				/// @post		Tangent must not result in an invalid number
				//=====================================================================================================
				
				static __forceinline float Tan( float v )
				{
					ENSURE( IsValid(v) , "Number given must be valid" );
					
					// Store the result here
					
					float result; 
					
					// Do the tangent:
					
					result = tanf(v);
					
					// Make sure ok:
					
					ENSURE( IsValid(result) , "Tangent must not result in an invalid number" );
					
					// Return the result
				
					return result;
				}
				
				//=====================================================================================================
				/// @brief		Returns the 1st number raised to the power of the second
				/// @param		v1 - 1st number
				/// @param		v2 - 2nd number
				/// @pre		Numbers given must be valid
				/// @pre		The first number must be non negative
				//=====================================================================================================
				
				static __forceinline float Pow( float v1 , float v2 )
				{
					ENSURE( IsValid(v1)	, "Numbers given must be valid"				);
					ENSURE( IsValid(v2) , "Numbers given must be valid"				);
					ENSURE( v1 >= 0		, "The first number must be non negative"	);
					
					return powf(v1,v2);
				}		
				
				//=====================================================================================================
				/// @brief		Clamps the given float in the range given
				/// @param		v - number to clamp
				/// @param		min - minimum value to clamp to
				/// @param		max - maximum value to clamp to
				/// @returns	The clamped value
				/// @pre		Numbers given must be valid
				/// @pre		Min must be less than or equal to max
				//=====================================================================================================				
				
				static __forceinline float Clamp( float v , float min , float max )
				{
					ENSURE( IsValid(v)		, "Numbers given must be valid"				);
					ENSURE( IsValid(max)	, "Numbers given must be valid"				);
					ENSURE( IsValid(min)	, "Numbers given must be valid"				);
					ENSURE( min <= min		, "Min must be less than or equal to max"	);
					
					// Store result here
					
					float result;
					
					// Clamp:
					
					__asm
					{
						movss xmm0		, v
						movss xmm1		, min
						maxss xmm0		, xmm1
						movss xmm1		, max
						minss xmm0		, xmm1
						movss result	, xmm0
					}
					
					// Return result:
					
					return result;			
				} 
				
				//=====================================================================================================
				/// @brief		Returns the maximum of the two given numbers
				/// @param		v1 - first number
				/// @param		v2 - second number
				/// @returns	Maximum of the two numbers
				/// @pre		Both numbers must be valid
				//=====================================================================================================
				
				static __forceinline float Max( float v1 , float v2 )
				{
					ENSURE( IsValid(v1) , "Both numbers must be valid" );
					ENSURE( IsValid(v2) , "Both numbers must be valid" );
					
					// Store result here
					
					float result;
					
					// Use SSE to do the max:
					
					__asm
					{
						movss xmm0		, v1
						movss xmm1		, v2
						maxss xmm0		, xmm1
						movss result	, xmm0
					}
					
					// Return result
					
					return result;
				}
				
				//=====================================================================================================
				/// @brief		Returns the minium of the two given numbers
				/// @param		v1 - first number
				/// @param		v2 - second number
				/// @returns	Minimum of the two numbers
				/// @pre		Both numbers must be valid
				//=====================================================================================================
				
				static __forceinline float Min( float v1 , float v2 )
				{
					ENSURE( IsValid(v1) , "Both numbers must be valid" );
					ENSURE( IsValid(v2) , "Both numbers must be valid" );
					
					// Store result here
					
					float result;
					
					// Use SSE to do the min:
					
					__asm
					{
						movss xmm0		, v1
						movss xmm1		, v2
						minss xmm0		, xmm1
						movss result	, xmm0
					}
					
					// Return result
					
					return result;
				}						
				
				//=====================================================================================================
				/// @brief		Returns the inverse tangent of this float.
				/// @pre		Number given must be valid
				/// @pre		Number given must be in -1 to 1 range
				//=====================================================================================================
				
				static __forceinline float InvTan( float v )
				{
					ENSURE( IsValid(v)  , "Number given must be valid"	);
				
					return atanf(v);
				}				
				
				//=====================================================================================================
				/// @brief		Tells if the float is a NaN
				/// @param		v - float to check
				/// @returns	True if a NaN
				//=====================================================================================================
				
				static __forceinline bool IsNaN( float v )
				{					
					return (((unsigned&)(v)) & 0x7fffffff ) > 0x7f800000;
				}
				
				//=====================================================================================================
				/// @brief		Tells if the float is either positive or negative infinity
				/// @param		v - float to check
				/// @returns	True if the float is either positive or negative infinity
				//=====================================================================================================
				
				static __forceinline bool IsInfinity( float v )
				{					
					return (((unsigned&)(v)) & 0x7fffffff ) == 0x7f800000;
				}
				
				//=====================================================================================================
				/// @brief		Tells if the float is invalid. I.E it is either positive or negative infinity or 
				///				a NaN or some other indeterminate.
				/// @param		v - float to check
				/// @returns	True if the float is invalid
				//=====================================================================================================
				
				__forceinline static bool IsInvalid( float v )
				{				
					return IsNaN(v) || IsInfinity(v);
				}
				
				//=====================================================================================================
				/// @brief		Tells if the float is valid. I.E it is not a NaN or infinity
				/// @param		v - float to check
				/// @returns	True if the float is invalid
				//=====================================================================================================
				
				static __forceinline bool IsValid( float v )
				{				
					return IsNaN(v) == false && IsInfinity(v) == false;
				}				
				
				//=====================================================================================================
				/// @brief		Tells if the float is a positive infinity.
				/// @param		v - float to check
				/// @returns	True if the float is positive infinity
				//=====================================================================================================
				
				static __forceinline bool IsPositiveInfinity( float v )
				{				
					return ((unsigned&)(v)) == 0x7f800000;
				}
				
				//=====================================================================================================
				/// @brief		Tells if the float is a negative infinity.
				/// @param		v - float to check
				/// @returns	True if the float is negative infinity
				//=====================================================================================================
				
				static __forceinline bool IsNegativeInfinity( float v )
				{				
					return ((unsigned&)(v)) == 0xff800000;
				}
				
				//=====================================================================================================
				/// @brief		Tells if the float is normalised
				/// @param		v - float to check
				/// @returns	True if the float is normalised
				//=====================================================================================================
				
				static __forceinline bool IsNormalized( float v )
				{				
					return ! (((((unsigned&)(v)) & 0x7f800000) == 0 ) && ((((unsigned&)(v)) & 0x007fffff) > 0 ));
				}
				
				//=====================================================================================================
				/// @brief		Returns the smallest positive value that can be represented by a float
				/// @returns	The smallest value that can be represented by a float
				//=====================================================================================================
				
				static __forceinline float SmallestPositiveValue()
				{
					return (const float&)(0x00000001);
				}
				
				//=====================================================================================================
				/// @brief		Returns the smallest negative value that can be represented by a float
				/// @returns	The negative value that can be represented by a float
				//=====================================================================================================
				
				static __forceinline float SmallestNegativeValue()
				{
					return (const float&)(0x80000001);
				}				

				//=====================================================================================================
				/// @brief		Returns the largest positive value that can be represented by a float
				/// @returns	The largest value that can be represented by a float
				//=====================================================================================================
				
				static __forceinline float LargestPositiveValue()
				{
					return (const float&)(0x7f7fffff);
				}
				
				//=====================================================================================================
				/// @brief		Returns the largest negative value that can be represented by a float
				/// @returns	The largest value that can be represented by a float
				//=====================================================================================================
				
				static __forceinline float LargestNegativeValue()
				{
					return (const float&)(0xff7fffff);
				}
				
				//=====================================================================================================
				/// @brief		Returns positive infinity
				/// @returns	Positive infinity
				//=====================================================================================================
				
				static __forceinline float PositiveInfinity()
				{
					return (const float&)(0x7f800000);
				}

				//=====================================================================================================
				/// @brief		Returns negative infinity
				/// @returns	Negative infinity
				//=====================================================================================================
				
				static __forceinline float NegativeInfinity()
				{
					return (const float&)(0xff800000);
				}
				
				//=====================================================================================================
				/// @brief		Returns NaN
				/// @returns	NaN
				/// @remarks	Do not compare directly to other NaNs as NaN comparison never returns true.
				///				Instead use IsNaN() on both numbers in comparison.
				//=====================================================================================================
				
				static __forceinline float NaN()
				{
					return (const float&)(0xffffffff);
				}
				
				//=====================================================================================================
				/// @brief		Returns the mathematical constant, PI
				/// @returns	PI
				//=====================================================================================================
				
				static __forceinline float PI()
				{
					// Store the result here
				
					float result;
					
					// Load PI off the processor and return
				
					__asm
					{
						fldpi 
						fstp  result
					}
					
					// Return the result
				
					return result;
				}

				//=====================================================================================================
				/// @brief		Returns the mathematical constant, PI * 2
				/// @returns	PI
				//=====================================================================================================
				
				static __forceinline float TwoPI()
				{
					return PI() * 2.0f;
				}
				
				//=====================================================================================================
				/// @brief		Returns the mathematical constant, E
				/// @returns	E
				//=====================================================================================================			
				
				static __forceinline float E() 
				{
					return 2.71828182845904523536f;
				}						
	};
};

#endif 
