#include <math.h>

// FIXME: Provide our own definitions instead of relying on the compiler's builtins.

#define WRAP_AROUND_BUILTIN(name)                                                                                      \
    double name(double val)                                                                                            \
    {                                                                                                                  \
        return __builtin_##name(val);                                                                                  \
    }                                                                                                                  \
    float name##f(float val)                                                                                           \
    {                                                                                                                  \
        return __builtin_##name##f(val);                                                                               \
    }                                                                                                                  \
    long double name##l(long double val)                                                                               \
    {                                                                                                                  \
        return __builtin_##name##l(val);                                                                               \
    }

#define WRAP_AROUND_BUILTIN2(name)                                                                                     \
    double name(double val1, double val2)                                                                              \
    {                                                                                                                  \
        return __builtin_##name(val1, val2);                                                                           \
    }                                                                                                                  \
    float name##f(float val1, float val2)                                                                              \
    {                                                                                                                  \
        return __builtin_##name##f(val1, val2);                                                                        \
    }                                                                                                                  \
    long double name##l(long double val1, long double val2)                                                            \
    {                                                                                                                  \
        return __builtin_##name##l(val1, val2);                                                                        \
    }

#define WRAP_AROUND_BUILTIN_WITH_PARAMETER_TYPE(name, type)                                                            \
    double name(double val1, type val2)                                                                                \
    {                                                                                                                  \
        return __builtin_##name(val1, val2);                                                                           \
    }                                                                                                                  \
    float name##f(float val1, type val2)                                                                               \
    {                                                                                                                  \
        return __builtin_##name##f(val1, val2);                                                                        \
    }                                                                                                                  \
    long double name##l(long double val1, type val2)                                                                   \
    {                                                                                                                  \
        return __builtin_##name##l(val1, val2);                                                                        \
    }

#define WRAP_AROUND_BUILTIN_WITH_POINTER(name)                                                                         \
    double name(double val1, double* val2)                                                                             \
    {                                                                                                                  \
        return __builtin_##name(val1, val2);                                                                           \
    }                                                                                                                  \
    float name##f(float val1, float* val2)                                                                             \
    {                                                                                                                  \
        return __builtin_##name##f(val1, val2);                                                                        \
    }                                                                                                                  \
    long double name##l(long double val1, long double* val2)                                                           \
    {                                                                                                                  \
        return __builtin_##name##l(val1, val2);                                                                        \
    }

extern "C"
{
    WRAP_AROUND_BUILTIN(cos);

    WRAP_AROUND_BUILTIN(sin);

    WRAP_AROUND_BUILTIN(tan);

    WRAP_AROUND_BUILTIN(acos);

    WRAP_AROUND_BUILTIN(asin);

    WRAP_AROUND_BUILTIN(atan);

    WRAP_AROUND_BUILTIN(cosh);

    WRAP_AROUND_BUILTIN(sinh);

    WRAP_AROUND_BUILTIN(tanh);

    WRAP_AROUND_BUILTIN(log);

    WRAP_AROUND_BUILTIN(exp);

    WRAP_AROUND_BUILTIN(sqrt);

    WRAP_AROUND_BUILTIN(fabs);

    WRAP_AROUND_BUILTIN(floor);

    WRAP_AROUND_BUILTIN(ceil);

    WRAP_AROUND_BUILTIN(log10);

    WRAP_AROUND_BUILTIN2(fmod);

    WRAP_AROUND_BUILTIN2(pow);

    WRAP_AROUND_BUILTIN2(atan2);

    WRAP_AROUND_BUILTIN_WITH_PARAMETER_TYPE(frexp, int*);

    WRAP_AROUND_BUILTIN_WITH_PARAMETER_TYPE(ldexp, int);

    WRAP_AROUND_BUILTIN_WITH_POINTER(modf);
}