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

#define WANT_ROUNDING 1

#if WANT_SNAN
    #error SNaN is unsupported
#else
    #define issignalingf_inline(x) 0
    #define issignaling_inline(x) 0
#endif

/* Helps static branch prediction so hot path can be better optimized.  */
#ifdef __GNUC__
    #define predict_true(x) __builtin_expect(!!(x), 1)
    #define predict_false(x) __builtin_expect(x, 0)
#else
    #define predict_true(x) (x)
    #define predict_false(x) (x)
#endif

static inline float eval_as_float(float x)
{
    float y = x;
    return y;
}

static inline double eval_as_double(double x)
{
    double y = x;
    return y;
}

/* fp_force_eval ensures that the input value is computed when that's
   otherwise unused.  To prevent the constant folding of the input
   expression, an additional fp_barrier may be needed or a compilation
   mode that does so (e.g. -frounding-math in gcc). Then it can be
   used to evaluate an expression for its fenv side-effects only.   */

#ifndef fp_force_evalf
    #define fp_force_evalf fp_force_evalf

static inline void fp_force_evalf(float x)
{
    volatile float y;
    y = x;
}
#endif

#ifndef fp_force_eval
    #define fp_force_eval fp_force_eval

static inline void fp_force_eval(double x)
{
    volatile double y;
    y = x;
}
#endif

#ifndef fp_force_evall
    #define fp_force_evall fp_force_evall

static inline void fp_force_evall(long double x)
{
    volatile long double y;
    y = x;
}
#endif

#define FORCE_EVAL(x)                        \
    do                                       \
    {                                        \
        if(sizeof(x) == sizeof(float))       \
        {                                    \
            fp_force_evalf(x);               \
        }                                    \
        else if(sizeof(x) == sizeof(double)) \
        {                                    \
            fp_force_eval(x);                \
        }                                    \
        else                                 \
        {                                    \
            fp_force_evall(x);               \
        }                                    \
    } while(0)

#define asuint(f) ((union {float _f; uint32_t _i; }){ f })._i
#define asfloat(i) ((union {uint32_t _i; float _f; }){ i })._f
#define asuint64(f) ((union {double _f; uint64_t _i; }){ f })._i
#define asdouble(i) ((union {uint64_t _i; double _f; }){ i })._f

#define EXTRACT_WORDS(hi, lo, d)    \
    do                              \
    {                               \
        uint64_t __u = asuint64(d); \
        (hi) = __u >> 32;           \
        (lo) = (uint32_t)__u;       \
    } while(0)

#define GET_HIGH_WORD(hi, d)      \
    do                            \
    {                             \
        (hi) = asuint64(d) >> 32; \
    } while(0)

#define GET_LOW_WORD(lo, d)           \
    do                                \
    {                                 \
        (lo) = (uint32_t)asuint64(d); \
    } while(0)

#define INSERT_WORDS(d, hi, lo)                                  \
    do                                                           \
    {                                                            \
        (d) = asdouble(((uint64_t)(hi) << 32) | (uint32_t)(lo)); \
    } while(0)

#define SET_HIGH_WORD(d, hi) \
    INSERT_WORDS(d, hi, (uint32_t)asuint64(d))

#define SET_LOW_WORD(d, lo) \
    INSERT_WORDS(d, asuint64(d) >> 32, lo)

#define GET_FLOAT_WORD(w, d) \
    do                       \
    {                        \
        (w) = asuint(d);     \
    } while(0)

#define SET_FLOAT_WORD(d, w) \
    do                       \
    {                        \
        (d) = asfloat(w);    \
    } while(0)

int __rem_pio2_large(double*, double*, int, int, int);

int __rem_pio2(double, double*);
double __sin(double, double, int);
double __cos(double, double);

int __rem_pio2f(float, double*);
float __sindf(double);
float __cosdf(double);

float __math_invalidf(float);
double __math_xflow(uint32_t, double);
double __math_uflow(uint32_t);
double __math_oflow(uint32_t);
double __math_invalid(double);
