//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// 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; either
// version 2.1 of the License, or (at your option) any later version.
//
// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * 数学ヘッダ
 * @author Junpee
 */

#ifndef MATH_H_
#define MATH_H_

#include <cmath>
#include <cfloat>

namespace Lamp{

//------------------------------------------------------------------------------
/**
 * 数学ヘッダ
 */
class Math{
public:
	//--------------------------------------------------------------------------
	// 定数
	//--------------------------------------------------------------------------
	/// パイ
	static const float PI;

	/// パイ * 4
	static const float quadruplePI;

	/// パイ * 2
	static const float doublePI;

	/// パイ / 2
	static const float halfPI;

	/// パイ / 4
	static const float quadrantPI;

	/// 自然対数の底
	static const float E;

	/// イプシロン
	static const float epsilon;

	//--------------------------------------------------------------------------
	// 単位変換
	//--------------------------------------------------------------------------
	/**
	 * 度からラジアンへの変換
	 * @param degree 度
	 * @return ラジアン
	 */
	static inline float toRadian(float degree){
		static const float scale = PI / 180.f;
		return degree * scale;
	}

	/**
	 * ラジアンから度への変換
	 * @param radian ラジアン
	 * @return 度
	 */
	static inline float toDegree(float radian){
		static const float scale = 180.f / PI;
		return radian * scale;
	}

	//--------------------------------------------------------------------------
	// Maximum, Minimum
	//--------------------------------------------------------------------------
	/**
	 * Maximum
	 * @param a 左辺値
	 * @param b 右辺値
	 * @return 大きいほうの値を返す
	 */
	static inline int maximum(int a, int b){
		if(a > b){ return a; }
		else{ return b; }
	}

	/**
	 * Maximum
	 * @param a 左辺値
	 * @param b 右辺値
	 * @return 大きいほうの値を返す
	 */
	static inline u_int maximum(u_int a, u_int b){
		if(a > b){ return a; }
		else{ return b; }
	}

	/**
	 * Maximum
	 * @param a 左辺値
	 * @param b 右辺値
	 * @return 大きいほうの値を返す
	 */
	static inline float maximum(float a, float b){
		if(a > b){ return a; }
		else{ return b; }
	}

	/**
	 * Minimum
	 * @param a 左辺値
	 * @param b 右辺値
	 * @return 小さいほうの値を返す
	 */
	static inline int minimum(int a, int b){
		if(a < b){ return a; }
		else{ return b; }
	}

	/**
	 * Minimum
	 * @param a 左辺値
	 * @param b 右辺値
	 * @return 小さいほうの値を返す
	 */
	static inline int minimum(u_int a, u_int b){
		if(a < b){ return a; }
		else{ return b; }
	}

	/**
	 * Minimum
	 * @param a 左辺値
	 * @param b 右辺値
	 * @return 小さいほうの値を返す
	 */
	static inline float minimum(float a, float b){
		if(a < b){ return a; }
		else{ return b; }
	}

	//--------------------------------------------------------------------------
	// スワップ
	//--------------------------------------------------------------------------
	/**
	 * スワップ
	 * @param a 左辺値
	 * @param b 右辺値
	 */
	static inline void swap(int& a, int& b){
		int tmp = a;
		a = b;
		b = tmp;
	}

	/**
	 * スワップ
	 * @param a 左辺値
	 * @param b 右辺値
	 */
	static inline void swap(u_int& a, u_int& b){
		u_int tmp = a;
		a = b;
		b = tmp;
	}

	/**
	 * スワップ
	 * @param a 左辺値
	 * @param b 右辺値
	 */
	static inline void swap(float& a, float& b){
		float tmp = a;
		a = b;
		b = tmp;
	}

	//--------------------------------------------------------------------------
	// 絶対値
	//--------------------------------------------------------------------------
	/**
	 * 絶対値
	 * @param x 絶対値にする数字
	 * @return 絶対値
	 */
	static inline int abs(int x){ return ::abs(x); }

	/**
	 * 絶対値
	 * @param x 絶対値にする数字
	 * @return 絶対値
	 */
	static inline float abs(float x){ return ::fabsf(x); }

	//--------------------------------------------------------------------------
	// 切り捨て、切り上げ
	//--------------------------------------------------------------------------
	/**
	 * 小数部の切り捨て
	 * @param x 小数部を切り捨てる値
	 * @return 小数部が切り捨てられた値
	 */
	static inline float floor(float x){ return ::floorf(x); }

	/**
	 * 小数部の切り上げ
	 * @param x 小数部を切り上げる値
	 * @return 小数部が切り上げられた値
	 */
	static inline float ceil(float x){ return ::ceilf(x); }

	//--------------------------------------------------------------------------
	// 浮動小数値演算
	//--------------------------------------------------------------------------
	/**
	 * 余り
	 * @param x 分母
	 * @param y 分子
	 * @return xをyで割った際の余りを返します
	 */
	static inline float fmod(float x, float y){ return ::fmodf(x, y); }

	/**
	 * 整数部と小数部への分離
	 * @param x 対象となる浮動小数値
	 * @param integer [out] xの整数部を格納するポインタ
	 * @return xの小数部分
	 */
	static inline float modf(float x, float* integer){
		return ::modff(x, integer);
	}

	/**
	 * 平方根
	 * @param x 平方根をとる正の値
	 * @return 平方根
	 */
	static inline float sqrt(float x){
		Assert(x >= 0.f);
		return ::sqrtf(x);
	}

	/**
	 * 浮動小数点チェック
	 * @param x チェックする値
	 * @return 正常な値ならtrue
	 */
	static inline bool classCheck(float x){
		int result = ::_fpclass(x);
		if((result == _FPCLASS_SNAN) ||		// シグナル型非数
			(result == _FPCLASS_QNAN) ||	// クワイエット型非数
			(result == _FPCLASS_PINF) ||	// 正の無限大
			(result == _FPCLASS_NINF)){		// 負の無限大
			return false;
		}
		return true;
	}

	//--------------------------------------------------------------------------
	// 指数、対数
	//--------------------------------------------------------------------------
	/**
	 * べき乗
	 * @param x 底
	 * @param y 指数
	 * @return xのy乗を返します
	 */
	static inline float pow(float x, float y){ return ::powf(x, y); }

	/**
	 * 指数
	 * @param x 乗数
	 * @return 自然対数の底 Math::E のx乗の値を返します
	 */
	static inline float exp(float x){ return ::expf(x); }

	/**
	 * 自然対数
	 * @param x 自然対数をとる対象値
	 * @return xの自然対数を返します
	 */
	static inline float log(float x){ return ::logf(x); }

	/**
	 * 常用対数
	 * @param x 常用対数をとる対象値
	 * @return xの常用対数を返します
	 */
	static inline float log10(float x){ return ::log10f(x); }

	/**
	 * 2の累乗かどうか
	 * @return 2の累乗ならtrue
	 */
	static inline bool checkPow2(int value){
		while(true){
			if(value == 1){ return true; }
			if((value % 2) == 1){ return false; }
			value /= 2;
		}
	}

	//--------------------------------------------------------------------------
	// 三角関数
	//--------------------------------------------------------------------------
	/**
	 * 正弦
	 * @param x 正弦を計算する値
	 * @return xの正弦
	 */
	static inline float sin(float x){ return ::sinf(x); }

	/**
	 * 余弦
	 * @param x 余弦を計算する値
	 * @return xの余弦
	 */
	static inline float cos(float x){ return ::cosf(x); }

	/**
	 * 正接
	 * @param x 正接を計算する値
	 * @return xの正接
	 */
	static inline float tan(float x){ return ::tanf(x); }

	/**
	 * 双曲正弦
	 * @param x 双曲正弦を計算する値
	 * @return xの双曲正弦
	 */
	static inline float sinh(float x){ return ::sinhf(x); }

	/**
	 * 双曲余弦
	 * @param x 双曲余弦を計算する値
	 * @return xの双曲余弦
	 */
	static inline float cosh(float x){ return ::coshf(x); }

	/**
	 * 双曲正接
	 * @param x 双曲正接を計算する値
	 * @return xの双曲正接
	 */
	static inline float tanh(float x){ return ::tanhf(x); }

	/**
	 * 逆正弦
	 * @param x 逆正弦を計算する値。-1 〜 1
	 * @return xの逆正弦。-π/2 〜 π/2
	 */
	static inline float asin(float x){ return ::asinf(x); }

	/**
	 * 逆余弦
	 * @param x 逆余弦を計算する値。-1 〜 1
	 * @return xの逆余弦。-π/2 〜 π/2
	 */
	static inline float acos(float x){ return ::acosf(x); }

	/**
	 * 逆正接
	 * @param x 逆正接を計算する値
	 * @return xの逆正接。-π/2 〜 π/2
	 */
	static inline float atan(float x){ return ::atanf(x); }

	/**
	 * 逆正接
	 * @param y 逆正接を計算するy値
	 * @param x 逆正接を計算するx値
	 * @return (x, y)の逆正接。-π 〜 π
	 */
	static inline float atan2(float y, float x){ return ::atan2f(y, x); }


private:
	// コンストラクタの隠蔽
	Math();

};
//------------------------------------------------------------------------------
} // End of namespace Lamp
#endif // End of MATH_H_
//------------------------------------------------------------------------------
