#define __ENZYME_MPFR_DOUBLE_BINOP(LLVM_OP_NAME, MPFR_FUNC_NAME, \
ROUNDING_MODE) \
__ENZYME_MPFR_BIN(binop, LLVM_OP_NAME, MPFR_FUNC_NAME, 64_52, double, d, \
double, d, double, d, ROUNDING_MODE)
#define __ENZYME_MPFR_DOUBLE_BINFUNCINTR(LLVM_OP_NAME, MPFR_FUNC_NAME, \
ROUNDING_MODE) \
__ENZYME_MPFR_BIN(intr, LLVM_OP_NAME, MPFR_FUNC_NAME, 64_52, double, d, \
double, d, double, d, ROUNDING_MODE) \
__ENZYME_MPFR_BIN(intr, llvm_##LLVM_OP_NAME##_f64, MPFR_FUNC_NAME, 64_52, \
double, d, double, d, double, d, ROUNDING_MODE) \
__ENZYME_MPFR_BIN(func, LLVM_OP_NAME, MPFR_FUNC_NAME, 64_52, double, d, \
double, d, double, d, ROUNDING_MODE) \
__ENZYME_MPFR_BIN(func, __##LLVM_OP_NAME##_finite, MPFR_FUNC_NAME, 64_52, \
double, d, double, d, double, d, ROUNDING_MODE)
#define __ENZYME_MPFR_DOUBLE_BINOP_DEFAULT_ROUNDING(LLVM_OP_NAME, \
MPFR_FUNC_NAME) \
__ENZYME_MPFR_DOUBLE_BINOP(LLVM_OP_NAME, MPFR_FUNC_NAME, \
__ENZYME_MPFR_DEFAULT_ROUNDING_MODE)
#define __ENZYME_MPFR_DOUBLE_BINFUNCINTR_DEFAULT_ROUNDING(LLVM_OP_NAME, \
MPFR_FUNC_NAME) \
__ENZYME_MPFR_DOUBLE_BINFUNCINTR(LLVM_OP_NAME, MPFR_FUNC_NAME, \
__ENZYME_MPFR_DEFAULT_ROUNDING_MODE)
#define __ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(LLVM_NAME, MPFR_NAME) \
__ENZYME_MPFR_SINGOP(intr, LLVM_NAME, MPFR_NAME, 64_52, double, d, double, \
d, __ENZYME_MPFR_DEFAULT_ROUNDING_MODE) \
__ENZYME_MPFR_SINGOP(intr, llvm_##LLVM_NAME##_f64, MPFR_NAME, 64_52, double, \
d, double, d, __ENZYME_MPFR_DEFAULT_ROUNDING_MODE) \
__ENZYME_MPFR_SINGOP(func, LLVM_NAME, MPFR_NAME, 64_52, double, d, double, \
d, __ENZYME_MPFR_DEFAULT_ROUNDING_MODE) \
__ENZYME_MPFR_SINGOP(func, __##LLVM_NAME##_finite, MPFR_NAME, 64_52, double, \
d, double, d, __ENZYME_MPFR_DEFAULT_ROUNDING_MODE) \
__ENZYME_MPFR_SINGOP(TYPE, LLVM_NAME, MPFR_NAME, 32_23, float, d, float, d, \
__ENZYME_MPFR_DEFAULT_ROUNDING_MODE) \
__ENZYME_MPFR_SINGOP(intr, LLVM_NAME, MPFR_NAME, 32_23, float, d, float, d, \
__ENZYME_MPFR_DEFAULT_ROUNDING_MODE) \
__ENZYME_MPFR_SINGOP(intr, llvm_##LLVM_NAME##_f32, MPFR_NAME, 32_23, float, \
d, float, d, __ENZYME_MPFR_DEFAULT_ROUNDING_MODE) \
__ENZYME_MPFR_SINGOP(func, LLVM_NAME, MPFR_NAME, 32_23, float, d, float, d, \
__ENZYME_MPFR_DEFAULT_ROUNDING_MODE)
#define __ENZYME_MPFR_FCMP(NAME, ORDERED, CMP) \
__ENZYME_MPFR_FCMP_IMPL(NAME, ORDERED, CMP, 64_52, double, d, \
__ENZYME_MPFR_DEFAULT_ROUNDING_MODE) \
__ENZYME_MPFR_FCMP_IMPL(NAME, ORDERED, CMP, 32_23, double, d, \
__ENZYME_MPFR_DEFAULT_ROUNDING_MODE)
// Binary operations
__ENZYME_MPFR_DOUBLE_BINOP_DEFAULT_ROUNDING(fmul, mul);
__ENZYME_MPFR_DOUBLE_BINOP_DEFAULT_ROUNDING(fadd, add);
__ENZYME_MPFR_DOUBLE_BINOP_DEFAULT_ROUNDING(fsub, sub);
__ENZYME_MPFR_DOUBLE_BINOP_DEFAULT_ROUNDING(fdiv, div);
__ENZYME_MPFR_DOUBLE_BINOP_DEFAULT_ROUNDING(frem, remainder);
__ENZYME_MPFR_DOUBLE_BINFUNCINTR_DEFAULT_ROUNDING(pow, pow);
__ENZYME_MPFR_DOUBLE_BINFUNCINTR_DEFAULT_ROUNDING(copysign, copysign);
__ENZYME_MPFR_DOUBLE_BINFUNCINTR_DEFAULT_ROUNDING(fdim, dim);
__ENZYME_MPFR_DOUBLE_BINFUNCINTR_DEFAULT_ROUNDING(remainder, remainder);
__ENZYME_MPFR_DOUBLE_BINFUNCINTR_DEFAULT_ROUNDING(atan2, atan2);
__ENZYME_MPFR_DOUBLE_BINFUNCINTR_DEFAULT_ROUNDING(hypot, hypot);
__ENZYME_MPFR_DOUBLE_BINFUNCINTR_DEFAULT_ROUNDING(fmod, fmod);
__ENZYME_MPFR_DOUBLE_BINFUNCINTR_DEFAULT_ROUNDING(maxnum, max);
__ENZYME_MPFR_DOUBLE_BINFUNCINTR_DEFAULT_ROUNDING(minnum, min);
__ENZYME_MPFR_BIN_INT(intr, llvm_powi_f64_i32, pow_si, 64_52, double, d, double,
d, int32_t, __ENZYME_MPFR_DEFAULT_ROUNDING_MODE);
// Unary operations
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(sqrt, sqrt);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(atanh, atanh);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(acosh, acosh);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(asinh, asinh);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(atan, atan);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(acos, acos);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(asin, asin);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(tanh, tanh);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(cosh, cosh);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(sinh, sinh);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(tan, tan);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(cos, cos);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(sin, sin);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(exp, exp);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(exp2, exp2);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(expm1, expm1);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(log, log);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(log2, log2);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(log10, log10);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(log1p, log1p);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(fabs, abs);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(trunc, abs);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(round, abs);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(floor, abs);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(ceil, abs);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(erf, erf);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(erfc, erfc);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(cbrt, cbrt);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(tgamma, gamma);
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(lgamma, lngamma);
// TODO This is not accurate (I think we cast int to double)
__ENZYME_MPFR_SINGOP_DOUBLE_FLOAT(nearbyint, rint);
// Ternary operation
__ENZYME_MPFR_FMULADD(llvm_fmuladd, 64_52, double, d, f64,
__ENZYME_MPFR_DEFAULT_ROUNDING_MODE);
__ENZYME_MPFR_FMULADD(llvm_fma, 64_52, double, d, f64,
__ENZYME_MPFR_DEFAULT_ROUNDING_MODE);
// Comparisons
__ENZYME_MPFR_FCMP(oeq, 1, == 0);
__ENZYME_MPFR_FCMP(ueq, 0, == 0);
__ENZYME_MPFR_FCMP(ogt, 1, > 0);
__ENZYME_MPFR_FCMP(ugt, 0, > 0);
__ENZYME_MPFR_FCMP(oge, 1, >= 0);
__ENZYME_MPFR_FCMP(uge, 0, >= 0);
__ENZYME_MPFR_FCMP(olt, 1, < 0);
__ENZYME_MPFR_FCMP(ult, 0, < 0);
__ENZYME_MPFR_FCMP(ole, 1, <= 0);
__ENZYME_MPFR_FCMP(ule, 0, <= 0);
__ENZYME_MPFR_FCMP(one, 1, != 0);
__ENZYME_MPFR_FCMP(une, 0, != 0);
// TODO special handling needed
// __ENZYME_MPFR_FCMP(fcmp_ord, );
// __ENZYME_MPFR_FCMP(fcmp_uno, );
// Reference:
// FCMP_OEQ = 1, ///< 0 0 0 1 True if ordered and equal
// FCMP_OGT = 2, ///< 0 0 1 0 True if ordered and greater than
// FCMP_OGE = 3, ///< 0 0 1 1 True if ordered and greater than or equal
// FCMP_OLT = 4, ///< 0 1 0 0 True if ordered and less than
// FCMP_OLE = 5, ///< 0 1 0 1 True if ordered and less than or equal
// FCMP_ONE = 6, ///< 0 1 1 0 True if ordered and operands are unequal
// FCMP_ORD = 7, ///< 0 1 1 1 True if ordered (no nans)
// FCMP_UNO = 8, ///< 1 0 0 0 True if unordered: isnan(X) | isnan(Y)
// FCMP_UEQ = 9, ///< 1 0 0 1 True if unordered or equal
// FCMP_UGT = 10, ///< 1 0 1 0 True if unordered or greater than
// FCMP_UGE = 11, ///< 1 0 1 1 True if unordered, greater than, or equal
// FCMP_ULT = 12, ///< 1 1 0 0 True if unordered or less than
// FCMP_ULE = 13, ///< 1 1 0 1 True if unordered, less than, or equal
// FCMP_UNE = 14, ///< 1 1 1 0 True if unordered or not equal