#ifndef GC_MATH_H_
#define GC_MATH_H_

#include <math.h>
#include <float.h>

#ifndef M_PI  /*quasi-standard const, not present in all compilers, but available under GCC*/
#define M_PI		3.14159265358979323846
#define M_PI_2		1.57079632679489661923
#define M_PI_4		0.78539816339744830962
#define M_1_PI		0.31830988618379067154
#define M_2_PI		0.63661977236758134308
#endif

#define GC_K_COMPOSE2(X_, Y_)  (X_ ## Y_)

#define GC_K_PIx(x_) ((x_)*GC_K_PI)
#define GC_K_PId(x_) ((x_)/GC_K_PI)
#define GC_K_PIxd(x_,d_) ((GC_K_PI*(x_))/(d_))

#define GC_K_PI static_cast<float>(M_PI) //180deg
#define GC_K_PI_2 static_cast<float>(M_PI_2) //90deg
#define GC_K_PI_3 GC_K_PId(3.0f)  //60deg
#define GC_K_2_PI_3 GC_K_PIxd(2.0f,3.0f)  //120deg
#define GC_K_2_PI static_cast<float>(GC_K_PIx(2)) //360deg
#define GC_K_4_PI static_cast<float>(GC_K_PIx(4)) //720deg

#define GC_K_180DEG (180.0f)
#define GC_K_360DEG (360.0f)
#define GC_K_90DEG  (90.0f)

#define GC_K_RAD_TO_DEG GC_K_PId(180.0f)
#define GC_K_DEG_TO_RAD (1.0f/GC_K_RAD_TO_DEG)

#define GC_K_NM_TO_METERS (1852.0f)
#define GC_K_METERS_TO_NM (1.0f/GC_K_NM_TO_METERS)
#define GC_K_METERS_TO_FEETS (3.280839895f)
#define GC_K_FEETS_TO_METERS (1.0f/GC_K_METERS_TO_FEETS)

#define GC_K_MS_TO_KNOTS (3600.0f/GC_K_NM_TO_METERS)
#define GC_K_KNOTS_TO_MS (1.0f/GC_K_MS_TO_KNOTS)

namespace gc_math
{

static inline bool float_equal(float a, float b)
{
	float delta=a-b;
	bool result= (delta>=-FLT_EPSILON) || (delta<=FLT_EPSILON);
	return result;
}

static inline bool float_exact_equal(float a, float b)	/*DEPRECATED*/
{
	return a==b; /* parasoft-suppress SELEXESC++-RC++12 "float exact compare, to use only in test code where exact compare against integer value" */
}

static inline bool float_is_zero(float v)
{
  return float_equal(v, 0.0f);
}

extern const float k_epsilon;

extern const float k_180deg;
extern const float k_360deg;
extern const float k_90deg;

extern const float k_pi;
extern const float k_2_pi;
extern const float k_4_pi;
extern const float k_half_pi;
extern const float k_pi_over_3;   //60deg
extern const float k_2_pi_over_3; //120deg

extern const float k_rad_to_deg;
extern const float k_deg_to_rad;

extern const float k_nm_to_meters;
extern const float k_meters_to_nm;

extern const float k_meters_to_feets;
extern const float k_feets_to_meters;

extern const float k_ms_to_knots;
extern const float k_knots_to_ms;

inline float meters_to_feet(float m) { return m*gc_math::k_meters_to_feets;}
inline float feet_to_meters(float ft) { return ft*gc_math::k_feets_to_meters;}

inline float nm_to_meters(float nm) { return nm*gc_math::k_nm_to_meters;}
inline float meters_to_nm(float m) { return m*gc_math::k_meters_to_nm;}

inline float ms_to_knots(float ms) { return ms*gc_math::k_ms_to_knots;}
inline float knots_to_ms(float kts) { return kts*gc_math::k_knots_to_ms;}

inline float deg_to_rad(float deg) { return deg*gc_math::k_deg_to_rad;}
inline float rad_to_deg(float rad) { return rad*gc_math::k_rad_to_deg;}

float angle_bound_pi(float x);
float angle_bound_2pi(float x);

bool angle_check_pi(float x);
bool angle_check_pi_pos(float x);
bool angle_check_pi2(float x);
bool angle_check_2pi_pos(float x);
bool angle_check_2pi3_pos(float x);

float angle_sum_pi(float a, float b);
float angle_sum_2pi(float a, float b);

inline int nint(float x) { return roundf(x); }
inline int ntrunc(float x) { return truncf(x); }

float angle_bound_180(float x);
float angle_bound_360(float x);
int angle_bound_int_359(int x);
float angle_sum_180(float a, float b);
float angle_sum_360(float a, float b);

}

#endif /*GC_MATH_H_*/
