#pragma once

#include <cassert>


namespace lm
{


// 複数引数対応のmin,max.
template<typename T> inline T (min)( const T& a , const T& b )
{
	return a < b ? a : b ;
}
template<typename T> inline T (min)( const T& a , const T& b , const T& c )
{
	return (lm::min)( (lm::min)( a , b ) , c ) ;
}
template<typename T> inline T (min)( const T& a , const T& b , const T& c , const T& d )
{
	return (lm::min)( (lm::min)( a , b ) , (lm::min)( c , d ) ) ;
}
template<typename T> inline T (max)( const T& a , const T& b )
{
	return a > b ? a : b ;
}
template<typename T> inline T (max)( const T& a , const T& b , const T& c )
{
	return (lm::max)( (lm::max)( a , b ) , c ) ;
}
template<typename T> inline T (max)( const T& a , const T& b , const T& c , const T& d )
{
	return (lm::max)( (lm::max)( a , b ) , (lm::max)( c , d ) ) ;
}


// 値の範囲を制限する
template<typename T> inline T clamp( const T& i_min , const T& i_value , const T& i_max ) 
{
	assert( i_min <= i_max );

	return (lm::min)( (lm::max)( i_min , i_value ) , i_max );
}

template<typename T> inline T clampto(T& val, const T& i_min, const T& i_max)
{
	assert( i_min <= i_max );

	val = (lm::min)( (lm::max)( i_min , val ) , i_max );
}

template<typename T> inline T clamp_abs( const T& i_value , const T& i_range )
{
	assert( i_range >= T(0) );

	return clamp( -i_range , i_value , i_range );
}

template<typename T> inline T clamp_01( const T& i_value )
{
	return clamp( T(0) , i_value , T(1) );
}

template<typename T> inline T& get_pair( const T& value , const T& pair_a , const T& pair_b )
{
	assert( value == pair_a || value == pair_b );

	return value == pair_a ? pair_b : pair_a;
}


template<typename T> inline T pow2( const T& v ) { return v * v ;             }
template<typename T> inline T pow3( const T& v ) { return v * v * v ;         }
template<typename T> inline T pow4( const T& v ) { return pow2( v * v ) ;     }
template<typename T> inline T pow5( const T& v ) { return pow2( v * v ) * v ; }
template<typename T> inline T pow6( const T& v ) { return pow3( v * v ) ;     }
template<typename T> inline T pow7( const T& v ) { return pow3( v * v ) * v ; }
template<typename T> inline T pow8( const T& v ) { return pow4( v * v ) ;     }


// 最小要素取得
template<typename T>
inline int min_elem_idx(const T& e0, const T& e1)
{
	return (e0 < e1) ? 0 : 1;
}
template<typename T>
inline int min_elem_idx(const T& e0, const T& e1, const T& e2)
{
	if (e2 < e0 && e2 < e1)
		return 2;
	return min_elem_idx(e0, e1);
}

// 最大要素取得
template<typename T>
inline int max_elem_idx(const T& e0, const T& e1)
{
	return (e0 > e1) ? 0 : 1;
}
template<typename T>
inline int max_elem_idx(const T& e0, const T& e1, const T& e2)
{
	if (e2 > e0 && e2 > e1)
		return 2;
	return max_elem_idx(e0, e1);
}


template<typename T> inline T gcd( T a , T b )
{
	if( b > a ) (std::swap)( a , b );
	while( ( a = a % b ) != 0 ) (std::swap)( b , a );
	return b;
}

template<typename T> inline T lcm( const T& a , const T& b )
{
	return a * b / gcd( a , b );
}


}
