/* lmpfrlib.c                                                            */
/*                                                                       */
/* Lua API for the GNU MPFR Library                                      */
/*                                                                       */
/* Written 2016-01-23, Version 2018-04-24, 2023-05-29                    */
/*                                                                       */
/* Copyright (C) 2016--2023 Hartmut Henkel                               */
/* E-Mail: hartmut_henkel@gmx.de                                         */
/* Homepage: http://www.hhenkel.de/lmpfrlib/                             */
/*                                                                       */
/* This program is free software: you can redistribute it and/or modify  */
/* it under the terms of the GNU Lesser Public License as published by   */
/* the Free Software Foundation, either version 3 of the License, or     */
/* (at your option) any later version.                                   */
/*                                                                       */
/* This program is distributed in the hope that it will be useful,       */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of        */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         */
/* GNU Lesser Public License for more details.                           */
/*                                                                       */
/* You should have received a copy of the GNU Lesser Public License      */
/* along with this program.  If not, see <http://www.gnu.org/licenses/>. */
/*************************************************************************/

#include<stdio.h>
#include<string.h>
#include<mpfr.h>
#include<lua.h>
#include<lauxlib.h>
#include<assert.h>

#define M_mpfr_t "mpfr_t"
#define M_mpfr_exp_t "mpfr_exp_t"
#define M_mpfr_prec_t "mpfr_prec_t"
#define M_mpfr_rnd_t "mpfr_rnd_t"

/**********************************************************************/

#define NOT_IMPLEMENTED(NAME)                      \
int l_##NAME(lua_State * L)                        \
{                                                  \
    luaL_error(L, #NAME "() not implemented yet"); \
    return 0;                                      \
}

#define USE_INSTEAD(NAME1, NAME2)                  \
int l_##NAME1(lua_State * L)                       \
{                                                  \
    luaL_error(L, #NAME1 "() not implemented yet," \
        " use " #NAME2 "() instead");              \
    return 0;                                      \
}

#define DEPRECATED_NEW(NAME1, NAME2)               \
int l_##NAME1(lua_State * L)                       \
{                                                  \
    luaL_error(L, #NAME1 "() deprecated,"          \
        " use " #NAME2 "() instead");              \
    return 0;                                      \
}

#define l_mpfr_specfun(NAME)                             \
int l_##NAME(lua_State * L)                              \
{                                                        \
    mpfr_t *rop, *op;                                    \
    mpfr_rnd_t rnd;                                      \
    int top, ret;                                        \
    top = lua_gettop(L);                                 \
    if (top == 1) {                                      \
        op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t); \
        rop = new_mpfr_t(L);                             \
        mpfr_init(*rop);                                 \
        rnd = mpfr_get_default_rounding_mode();          \
        (void)  NAME(*rop, *op, rnd);                    \
    } else {                                             \
        rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);\
        op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t); \
        rnd = get_rounding_mode(L, 3);                   \
        ret =  NAME(*rop, *op, rnd);                     \
        lua_pushinteger(L, ret);                         \
    }                                                    \
    return 1;                                            \
}

#define l_mpfr_specfun2(NAME)                             \
int l_##NAME(lua_State * L)                               \
{                                                         \
    mpfr_t *rop, *op1, *op2;                              \
    mpfr_rnd_t rnd;                                       \
    int top, ret;                                         \
    top = lua_gettop(L);                                  \
    if (top == 2) {                                       \
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t); \
        op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t); \
        rop = new_mpfr_t(L);                              \
        mpfr_init(*rop);                                  \
        rnd = mpfr_get_default_rounding_mode();           \
        (void)  NAME(*rop, *op1, *op2, rnd);              \
    } else {                                              \
        rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t); \
        op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t); \
        op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t); \
        rnd = get_rounding_mode(L, 3);                    \
        ret =  NAME(*rop, *op1, *op2, rnd);               \
        lua_pushinteger(L, ret);                          \
    }                                                     \
    return 1;                                             \
}

#define l_mpfr_const(NAME)                               \
int l_##NAME(lua_State * L)                              \
{                                                        \
    mpfr_t *rop;                                         \
    mpfr_rnd_t rnd;                                      \
    int top, ret;                                        \
    top = lua_gettop(L);                                 \
    if (top == 0) {                                      \
        rop = new_mpfr_t(L);                             \
        mpfr_init(*rop);                                 \
        rnd = mpfr_get_default_rounding_mode();          \
        (void) NAME(*rop, rnd);                          \
    } else {                                             \
        rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);\
        rnd = get_rounding_mode(L, 2);                   \
        ret = NAME(*rop, rnd);                           \
        lua_pushinteger(L, ret);                         \
    }                                                    \
    return 1;                                            \
}

#define l_mpfr_fits(NAME)                           \
int l_##NAME(lua_State * L)                         \
{                                                   \
    mpfr_t *op;                                     \
    mpfr_rnd_t rnd;                                 \
    int ret;                                        \
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);\
    rnd = get_rounding_mode(L, 2);                  \
    ret = NAME(*op, rnd);                           \
    lua_pushinteger(L, ret);                        \
    return 1;                                       \
}

/**********************************************************************/

mpfr_rnd_t get_rounding_mode(lua_State * L, int spos)
{
    mpfr_rnd_t rnd;
    const char *s;
    char c;
    size_t len;
    int pos, top;
    top = lua_gettop(L);
    if (top >= spos) {
        s = luaL_checklstring(L, spos, &len);
        switch (len) {
        case 1:
            pos = 0;
            break;
        case 4:
            pos = 3;
            if (strncmp("RND", s, pos) != 0)
                luaL_error(L,
                           "mpfr_set_default_rounding_mode(): wrong rounding mode (1)");
            break;
        case 9:
            pos = 8;
            if (strncmp("MPFR_RND", s, pos) != 0)
                luaL_error(L,
                           "mpfr_set_default_rounding_mode(): wrong rounding mode (2)");
            break;
        default:
            luaL_error(L,
                       "mpfr_set_default_rounding_mode(): wrong rounding mode (3)");
        }
        c = s[pos];
        switch (c) {
        case 'N':
            rnd = MPFR_RNDN;
            break;
        case 'D':
            rnd = MPFR_RNDD;
            break;
        case 'U':
            rnd = MPFR_RNDU;
            break;
        case 'Z':
            rnd = MPFR_RNDZ;
            break;
        case 'A':
            rnd = MPFR_RNDA;
            break;
        case 'F':
            rnd = MPFR_RNDF;
            break;
        default:
            luaL_error(L,
                       "mpfr_set_default_rounding_mode(): wrong rounding mode (4)");
        }
    } else
        rnd = mpfr_get_default_rounding_mode();
    return rnd;
}

mpfr_t *new_mpfr_t(lua_State * L)
{
    mpfr_t *a;
    a = (mpfr_t *) lua_newuserdata(L, sizeof(mpfr_t));  /* mpfr_t ... */
    luaL_getmetatable(L, M_mpfr_t);     /* m mpfr_t ... */
    lua_setmetatable(L, -2);    /* mpfr_t ... */
    return a;
}

/**********************************************************************/
/* 5.1 Initialization Functions */

int l_mpfr_num(lua_State * L)
{
    mpfr_t *rop, *op;
    int base, ltype, top;
    size_t l;
    const char *s;
    lua_Number n;
    lua_Integer i;
    mpfr_rnd_t rnd;
    top = lua_gettop(L);
    if (top == 0) {
        rop = new_mpfr_t(L);
        mpfr_init(*rop);
    } else {
        ltype = lua_type(L, 1);
        switch (ltype) {
        case LUA_TUSERDATA:
            op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
            rnd = get_rounding_mode(L, 2);
            rop = new_mpfr_t(L);
            (void) mpfr_init_set(*rop, *op, rnd);
            break;
        case LUA_TNUMBER:
            n = luaL_checknumber(L, 1);
            rnd = get_rounding_mode(L, 2);
            if (lua_isinteger(L, 1)) {
                (void) lua_numbertointeger(n, &i);
                rop = new_mpfr_t(L);
                (void) mpfr_init_set_si(*rop, i, rnd);
            } else {
                rop = new_mpfr_t(L);
                (void) mpfr_init_set_d(*rop, n, rnd);
            }
            break;
        case LUA_TSTRING:
            s = luaL_checklstring(L, 1, &l);
            if (top >= 2)
                base = (int) luaL_checkinteger(L, 2);
            else
                base = 0;       /* auto-detect */
            rnd = get_rounding_mode(L, 3);
            rop = new_mpfr_t(L);
            (void) mpfr_init_set_str(*rop, s, base, rnd);
            break;
        }
    }
    return 1;
}

int l_mpfr_init2(lua_State * L)
{
    mpfr_t *a;
    mpfr_prec_t prec;
    prec = (mpfr_prec_t) luaL_checkinteger(L, 1);
    a = new_mpfr_t(L);
    mpfr_init2(*a, prec);
    return 1;
}

NOT_IMPLEMENTED(mpfr_inits2);

int l_mpfr_clear(lua_State * L)
{
    mpfr_t *x;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    mpfr_clear(*x);
    return 0;
}

int l_mpfr_clears(lua_State * L)
{
    mpfr_t *x;
    int i, top;
    top = lua_gettop(L);
    for (i = 1; i <= top; i++) {
        x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        mpfr_clear(*x);
    }
    return 0;
}

int l_mpfr_init(lua_State * L)
{
    mpfr_t *a;
    a = new_mpfr_t(L);
    mpfr_init(*a);
    return 1;
}

NOT_IMPLEMENTED(mpfr_inits);

int l_mpfr_set_default_prec(lua_State * L)
{
    mpfr_prec_t prec;
    prec = (mpfr_prec_t) luaL_checkinteger(L, 1);
    mpfr_set_default_prec(prec);
    return 0;
}

int l_mpfr_get_default_prec(lua_State * L)
{
    mpfr_prec_t prec;
    prec = mpfr_get_default_prec();
    lua_pushinteger(L, prec);
    return 1;
}

int l_mpfr_set_prec(lua_State * L)
{
    mpfr_t *op;
    mpfr_prec_t prec;
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    prec = (mpfr_prec_t) luaL_checkinteger(L, 2);
    mpfr_set_prec(*op, prec);
    return 0;
}

int l_mpfr_get_prec(lua_State * L)
{
    mpfr_t *op;
    mpfr_prec_t prec;
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    prec = mpfr_get_prec(*op);
    lua_pushinteger(L, prec);
    return 1;
}

/**********************************************************************/
/* 5.2 Assignment Functions */

l_mpfr_specfun(mpfr_set);

USE_INSTEAD(mpfr_set_ui, mpfr_set_si);

int l_mpfr_set_si(lua_State * L)
{
    mpfr_t *rop;
    lua_Integer op;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = luaL_checkinteger(L, 2);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_set_si(*rop, (long int) op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_set_uj);
NOT_IMPLEMENTED(mpfr_set_sj);
NOT_IMPLEMENTED(mpfr_set_flt);

int l_mpfr_set_d(lua_State * L)
{
    mpfr_t *rop;
    lua_Number op;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = luaL_checknumber(L, 2);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_set_d(*rop, op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_set_ld);
NOT_IMPLEMENTED(mpfr_set_float128);
NOT_IMPLEMENTED(mpfr_set_decimal64);
NOT_IMPLEMENTED(mpfr_set_decimal128);
NOT_IMPLEMENTED(mpfr_set_z);
NOT_IMPLEMENTED(mpfr_set_q);
NOT_IMPLEMENTED(mpfr_set_f);
NOT_IMPLEMENTED(mpfr_set_ui_2exp);

int l_mpfr_set_si_2exp(lua_State * L)
{
    mpfr_t *rop;
    lua_Integer op;
    mpfr_exp_t e;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = luaL_checkinteger(L, 2);
    e = (mpfr_exp_t) luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_set_si_2exp(*rop, op, e, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_set_uj_2exp);
NOT_IMPLEMENTED(mpfr_set_sj_2exp);
NOT_IMPLEMENTED(mpfr_set_z_2exp);

int l_mpfr_set_str(lua_State * L)
{
    mpfr_t *rop;
    const char *s;
    mpfr_rnd_t rnd;
    int base, ret;
    size_t l;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    s = luaL_checklstring(L, 2, &l);
    base = (int) luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_set_str(*rop, s, base, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_strtofr);

int l_mpfr_set_nan(lua_State * L)
{
    mpfr_t *x;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    mpfr_set_nan(*x);
    return 0;
}

int l_mpfr_set_inf(lua_State * L)
{
    mpfr_t *x;
    int sign;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    sign = (int) luaL_checkinteger(L, 2);
    mpfr_set_inf(*x, sign);
    return 0;
}

int l_mpfr_set_zero(lua_State * L)
{
    mpfr_t *x;
    int sign;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    sign = (int) luaL_checkinteger(L, 2);
    mpfr_set_zero(*x, sign);
    return 0;
}

int l_mpfr_swap(lua_State * L)
{
    mpfr_t *x, *y;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    y = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    mpfr_swap(*x, *y);
    return 0;
}

/**********************************************************************/
/* 5.3 Combined Initialization and Assignment Functions */

int l_mpfr_init_set(lua_State * L)
{
    mpfr_t *rop, *op;
    mpfr_rnd_t rnd;
    int ret;
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    rnd = get_rounding_mode(L, 2);
    rop = new_mpfr_t(L);
    ret = mpfr_init_set(*rop, *op, rnd);
    lua_pushinteger(L, ret);
    return 2;
}

USE_INSTEAD(mpfr_init_set_ui, mpfr_init_set_si);

int l_mpfr_init_set_si(lua_State * L)
{
    mpfr_t *rop;
    lua_Integer op;
    mpfr_rnd_t rnd;
    int ret;
    op = luaL_checkinteger(L, 1);
    rnd = get_rounding_mode(L, 2);
    rop = new_mpfr_t(L);
    ret = mpfr_init_set_si(*rop, op, rnd);
    lua_pushinteger(L, ret);
    return 2;
}

int l_mpfr_init_set_d(lua_State * L)
{
    mpfr_t *rop;
    lua_Number op;
    mpfr_rnd_t rnd;
    int ret;
    op = luaL_checknumber(L, 1);
    rnd = get_rounding_mode(L, 2);
    rop = new_mpfr_t(L);
    ret = mpfr_init_set_d(*rop, op, rnd);
    lua_pushinteger(L, ret);
    return 2;
}

NOT_IMPLEMENTED(mpfr_init_set_ld);
NOT_IMPLEMENTED(mpfr_init_set_z);
NOT_IMPLEMENTED(mpfr_init_set_q);
NOT_IMPLEMENTED(mpfr_init_set_f);

int l_mpfr_init_set_str(lua_State * L)
{
    mpfr_t *x;
    const char *s;
    mpfr_rnd_t rnd;
    int base, top, ret;
    size_t l;
    s = luaL_checklstring(L, 1, &l);
    top = lua_gettop(L);
    if (top >= 2)
        base = (int) luaL_checkinteger(L, 2);
    else
        base = 0;               /* auto-detect */
    rnd = get_rounding_mode(L, 3);
    x = new_mpfr_t(L);
    ret = mpfr_init_set_str(*x, s, base, rnd);
    lua_pushinteger(L, ret);
    return 2;
}

/**********************************************************************/
/* 5.4 Conversion Functions */

NOT_IMPLEMENTED(mpfr_get_flt);

int l_mpfr_get_d(lua_State * L)
{
    mpfr_t *op;
    mpfr_rnd_t rnd;
    double x;
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    rnd = get_rounding_mode(L, 2);
    x = mpfr_get_d(*op, rnd);
    lua_pushnumber(L, x);
    return 1;
}

NOT_IMPLEMENTED(mpfr_get_ld);
NOT_IMPLEMENTED(mpfr_get_float128);
NOT_IMPLEMENTED(mpfr_get_decimal64);
NOT_IMPLEMENTED(mpfr_get_decimal128);

int l_mpfr_get_si(lua_State * L)
{
    mpfr_t *op;
    mpfr_rnd_t rnd;
    long x;
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    rnd = get_rounding_mode(L, 2);
    x = mpfr_get_si(*op, rnd);
    lua_pushinteger(L, x);
    return 1;
}

USE_INSTEAD(mpfr_get_ui, mpfr_get_si);
NOT_IMPLEMENTED(mpfr_get_sj);
NOT_IMPLEMENTED(mpfr_get_uj);

int l_mpfr_get_d_2exp(lua_State * L)
{
    mpfr_t *op;
    mpfr_rnd_t rnd;
    long exp;
    double d;
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    rnd = get_rounding_mode(L, 2);
    d = mpfr_get_d_2exp(&exp, *op, rnd);
    lua_pushnumber(L, d);
    lua_pushinteger(L, exp);
    return 2;
}

NOT_IMPLEMENTED(mpfr_get_ld_2exp);

int l_mpfr_frexp(lua_State * L)
{
    mpfr_t *x, *y;
    mpfr_rnd_t rnd;
    long exp;
    int ret;
    y = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    x = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_frexp(&exp, *y, *x, rnd);
    lua_pushinteger(L, ret);
    lua_pushinteger(L, exp);
    return 1;
}

NOT_IMPLEMENTED(mpfr_get_z_2exp);
NOT_IMPLEMENTED(mpfr_get_z);
NOT_IMPLEMENTED(mpfr_get_f);
NOT_IMPLEMENTED(mpfr_get_str);
NOT_IMPLEMENTED(mpfr_free_str);

l_mpfr_fits(mpfr_fits_ulong_p);
l_mpfr_fits(mpfr_fits_slong_p);
l_mpfr_fits(mpfr_fits_uint_p);
l_mpfr_fits(mpfr_fits_sint_p);
l_mpfr_fits(mpfr_fits_ushort_p);
l_mpfr_fits(mpfr_fits_sshort_p);
l_mpfr_fits(mpfr_fits_uintmax_p);
l_mpfr_fits(mpfr_fits_intmax_p);

/**********************************************************************/
/* 5.5 Arithmetic Functions */

int l_mpfr_add(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_add(*rop, *op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

USE_INSTEAD(mpfr_add_ui, mpfr_add_si);

int l_mpfr_add_si(lua_State * L)
{
    mpfr_t *rop, *op1;
    lua_Integer op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_add_si(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_add_d(lua_State * L)
{
    mpfr_t *rop, *op1;
    double op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checknumber(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_add_d(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_add_z);
NOT_IMPLEMENTED(mpfr_add_q);

int l_mpfr_sub(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_sub(*rop, *op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

USE_INSTEAD(mpfr_ui_sub, mpfr_si_sub);
USE_INSTEAD(mpfr_sub_ui, mpfr_sub_si);

int l_mpfr_si_sub(lua_State * L)
{
    mpfr_t *rop, *op2;
    lua_Integer op1;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = luaL_checkinteger(L, 2);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_si_sub(*rop, op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_sub_si(lua_State * L)
{
    mpfr_t *rop, *op1;
    lua_Integer op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_sub_si(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_d_sub(lua_State * L)
{
    mpfr_t *rop, *op2;
    double op1;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = luaL_checknumber(L, 2);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_d_sub(*rop, op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_sub_d(lua_State * L)
{
    mpfr_t *rop, *op1;
    double op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checknumber(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_sub_d(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_z_sub);
NOT_IMPLEMENTED(mpfr_sub_z);
NOT_IMPLEMENTED(mpfr_sub_q);

int l_mpfr_mul(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_mul(*rop, *op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

USE_INSTEAD(mpfr_mul_ui, mpfr_mul_si);

int l_mpfr_mul_si(lua_State * L)
{
    mpfr_t *rop, *op1;
    lua_Integer op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_mul_si(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_mul_d(lua_State * L)
{
    mpfr_t *rop, *op1;
    double op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checknumber(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_mul_d(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_mul_z);
NOT_IMPLEMENTED(mpfr_mul_q);

l_mpfr_specfun(mpfr_sqr);

int l_mpfr_div(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_div(*rop, *op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

USE_INSTEAD(mpfr_ui_div, mpfr_si_div);
USE_INSTEAD(mpfr_div_ui, mpfr_div_si);

int l_mpfr_si_div(lua_State * L)
{
    mpfr_t *rop, *op2;
    lua_Integer op1;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = luaL_checkinteger(L, 2);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_si_div(*rop, op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_div_si(lua_State * L)
{
    mpfr_t *rop, *op1;
    lua_Integer op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_div_si(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_d_div(lua_State * L)
{
    mpfr_t *rop, *op2;
    double op1;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = luaL_checknumber(L, 2);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_d_div(*rop, op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_div_d(lua_State * L)
{
    mpfr_t *rop, *op1;
    double op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checknumber(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_div_d(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_div_z);
NOT_IMPLEMENTED(mpfr_div_q);

l_mpfr_specfun(mpfr_sqrt);

USE_INSTEAD(mpfr_sqrt_ui, mpfr_sqrt_si);

l_mpfr_specfun(mpfr_rec_sqrt);
l_mpfr_specfun(mpfr_cbrt);

int l_mpfr_root(lua_State * L)
{
    mpfr_t *rop, *op;
    mpfr_rnd_t rnd;
    lua_Integer k;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    k = luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_rootn_ui(*rop, *op, k, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_pow(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_pow(*rop, *op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

USE_INSTEAD(mpfr_pow_ui, mpfr_pow_si);

int l_mpfr_pow_si(lua_State * L)
{
    mpfr_t *rop, *op1;
    lua_Integer op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_pow_si(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_pow_z);

int l_mpfr_ui_pow_ui(lua_State * L)
{
    mpfr_t *rop;
    lua_Integer op1, op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = luaL_checkinteger(L, 2);
    op2 = luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_ui_pow_ui(*rop, op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_ui_pow(lua_State * L)
{
    mpfr_t *rop, *op2;
    lua_Integer op1;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = luaL_checkinteger(L, 2);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_ui_pow(*rop, op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

l_mpfr_specfun(mpfr_neg);
l_mpfr_specfun(mpfr_abs);

int l_mpfr_dim(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_dim(*rop, *op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_mul_2ui);

int l_mpfr_mul_2si(lua_State * L)
{
    mpfr_t *rop, *op1;
    lua_Integer op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_mul_2si(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_div_2ui);

int l_mpfr_div_2si(lua_State * L)
{
    mpfr_t *rop, *op1;
    lua_Integer op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_div_2si(*rop, *op1, op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

/**********************************************************************/
/* 5.6 Comparison Functions */

int l_mpfr_cmp(lua_State * L)
{
    mpfr_t *op1, *op2;
    int ret;
    op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    ret = mpfr_cmp(*op1, *op2);
    lua_pushinteger(L, ret);
    return 1;
}

USE_INSTEAD(mpfr_cmp_ui, mpfr_cmp_si);

int l_mpfr_cmp_si(lua_State * L)
{
    mpfr_t *op1;
    lua_Integer op2;
    int ret;
    op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op2 = luaL_checkinteger(L, 2);
    ret = mpfr_cmp_si(*op1, op2);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_cmp_d(lua_State * L)
{
    mpfr_t *op1;
    double op2;
    int ret;
    op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op2 = luaL_checknumber(L, 2);
    ret = mpfr_cmp_d(*op1, op2);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_cmp_ld);
NOT_IMPLEMENTED(mpfr_cmp_z);
NOT_IMPLEMENTED(mpfr_cmp_q);
NOT_IMPLEMENTED(mpfr_cmp_f);
USE_INSTEAD(mpfr_cmp_ui_2exp, mpfr_cmp_si_2exp);

int l_mpfr_cmp_si_2exp(lua_State * L)
{
    mpfr_t *op1;
    lua_Integer op2;
    mpfr_exp_t e;
    int ret;
    op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op2 = luaL_checkinteger(L, 2);
    e = luaL_checkinteger(L, 3);
    ret = mpfr_cmp_si_2exp(*op1, op2, e);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_cmpabs(lua_State * L)
{
    mpfr_t *op1, *op2;
    int ret;
    op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    ret = mpfr_cmpabs(*op1, *op2);
    lua_pushinteger(L, ret);
    return 1;
}

#define l_mpfr_number_is(NAME)                      \
int l_mpfr_##NAME(lua_State * L)                    \
{                                                   \
    mpfr_t *op;                                     \
    int i;                                          \
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);\
    i = mpfr_##NAME(*op);                           \
    lua_pushinteger(L, i);                          \
    return 1;                                       \
}

l_mpfr_number_is(nan_p);
l_mpfr_number_is(inf_p);
l_mpfr_number_is(number_p);
l_mpfr_number_is(zero_p);
l_mpfr_number_is(regular_p);
l_mpfr_number_is(sgn);

#define l_mpfr_compare_numbers(NAME)                  \
int l_mpfr_##NAME(lua_State * L)                      \
{                                                     \
    mpfr_t *op1, *op2;                                \
    int i;                                            \
    op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t); \
    op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t); \
    i = mpfr_##NAME(*op1, *op2);                      \
    lua_pushinteger(L, i);                            \
    return 1;                                         \
}

l_mpfr_compare_numbers(greater_p);
l_mpfr_compare_numbers(greaterequal_p);
l_mpfr_compare_numbers(less_p);
l_mpfr_compare_numbers(lessequal_p);
l_mpfr_compare_numbers(equal_p);
l_mpfr_compare_numbers(lessgreater_p);
l_mpfr_compare_numbers(unordered_p);

/**********************************************************************/
/* 5.7 Transcendental Functions */

l_mpfr_specfun(mpfr_log);
l_mpfr_specfun(mpfr_log2);
l_mpfr_specfun(mpfr_log10);
l_mpfr_specfun(mpfr_exp);
l_mpfr_specfun(mpfr_exp2);
l_mpfr_specfun(mpfr_exp10);
l_mpfr_specfun(mpfr_cos);
l_mpfr_specfun(mpfr_sin);
l_mpfr_specfun(mpfr_tan);

int l_mpfr_sin_cos(lua_State * L)
{
    mpfr_t *sop, *cop, *op;
    mpfr_rnd_t rnd;
    int ret;
    sop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    cop = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_sin_cos(*sop, *cop, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

l_mpfr_specfun(mpfr_sec);
l_mpfr_specfun(mpfr_csc);
l_mpfr_specfun(mpfr_cot);
l_mpfr_specfun(mpfr_acos);
l_mpfr_specfun(mpfr_asin);
l_mpfr_specfun(mpfr_atan);

l_mpfr_specfun2(mpfr_atan2);

l_mpfr_specfun(mpfr_cosh);
l_mpfr_specfun(mpfr_sinh);
l_mpfr_specfun(mpfr_tanh);

int l_mpfr_sinh_cosh(lua_State * L)
{
    mpfr_t *sop, *cop, *op;
    mpfr_rnd_t rnd;
    int ret;
    sop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    cop = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_sinh_cosh(*sop, *cop, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

l_mpfr_specfun(mpfr_sech);
l_mpfr_specfun(mpfr_csch);
l_mpfr_specfun(mpfr_coth);
l_mpfr_specfun(mpfr_acosh);
l_mpfr_specfun(mpfr_asinh);
l_mpfr_specfun(mpfr_atanh);

int l_mpfr_fac_ui(lua_State * L)
{
    mpfr_t *rop;
    lua_Integer op;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = luaL_checkinteger(L, 2);
    assert(op >= 0);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_fac_ui(*rop, op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

l_mpfr_specfun(mpfr_log1p);
l_mpfr_specfun(mpfr_expm1);
l_mpfr_specfun(mpfr_eint);
l_mpfr_specfun(mpfr_li2);
l_mpfr_specfun(mpfr_gamma);
l_mpfr_specfun(mpfr_lngamma);

int l_mpfr_lgamma(lua_State * L)
{
    mpfr_t *rop, *op;
    mpfr_rnd_t rnd;
    int ret, sign;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_lgamma(*rop, &sign, *op, rnd);
    lua_pushinteger(L, ret);
    lua_pushinteger(L, sign);
    return 2;
}

l_mpfr_specfun(mpfr_digamma);
l_mpfr_specfun(mpfr_zeta);

NOT_IMPLEMENTED(mpfr_zeta_ui);

l_mpfr_specfun(mpfr_erf);
l_mpfr_specfun(mpfr_erfc);
l_mpfr_specfun(mpfr_j0);
l_mpfr_specfun(mpfr_j1);

int l_mpfr_jn(lua_State * L)
{
    mpfr_t *rop, *op;
    long n;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    n = luaL_checkinteger(L, 2);
    op = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_jn(*rop, n, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

l_mpfr_specfun(mpfr_y0);
l_mpfr_specfun(mpfr_y1);

int l_mpfr_yn(lua_State * L)
{
    mpfr_t *rop, *op;
    long n;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    n = luaL_checkinteger(L, 2);
    op = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_yn(*rop, n, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_fma(lua_State * L)
{
    mpfr_t *rop, *op1, *op2, *op3;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    op3 = (mpfr_t *) luaL_checkudata(L, 4, M_mpfr_t);
    rnd = get_rounding_mode(L, 5);
    ret = mpfr_fma(*rop, *op1, *op2, *op3, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_fms(lua_State * L)
{
    mpfr_t *rop, *op1, *op2, *op3;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    op3 = (mpfr_t *) luaL_checkudata(L, 4, M_mpfr_t);
    rnd = get_rounding_mode(L, 5);
    ret = mpfr_fms(*rop, *op1, *op2, *op3, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_agm(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_agm(*rop, *op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_hypot(lua_State * L)
{
    mpfr_t *rop, *x, *y;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    x = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    y = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_hypot(*rop, *x, *y, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

l_mpfr_specfun(mpfr_ai);

l_mpfr_const(mpfr_const_log2);
l_mpfr_const(mpfr_const_pi);
l_mpfr_const(mpfr_const_euler);
l_mpfr_const(mpfr_const_catalan);

int l_mpfr_free_cache(lua_State * L)
{
    mpfr_free_cache();
    return 0;
}

int l_mpfr_sum(lua_State * L)
{
    mpfr_t *rop;
    mpfr_rnd_t rnd;
    lua_Integer n;
    lua_Integer i, ret;
    mpfr_ptr *tab;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);   /* (rnd) t rop */
    luaL_checktype(L, 2, LUA_TTABLE);   /* (rnd) t rop */
    rnd = get_rounding_mode(L, 3);
    lua_len(L, 2);              /* n (rnd) t rop */
    n = lua_tointeger(L, -1);
    lua_pop(L, 1);              /* (rnd) t rop */
    tab = (mpfr_ptr *) lua_newuserdata(L, n * sizeof(mpfr_ptr));        /* tab (rnd) t rop */
    for (i = 1; i <= n; i++) {
        lua_geti(L, 2, i);      /* op tab (rnd) t rop */
        tab[i - 1] = (mpfr_ptr) luaL_checkudata(L, -1, M_mpfr_t);       /* op tab (rnd) t rop */
        lua_pop(L, 1);          /* tab (rnd) t rop */
    }
    ret = mpfr_sum(*rop, tab, n, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

/**********************************************************************/
/* 5.8 Input and Output Functions */

int l_mpfr_out_str(lua_State * L)
{
    mpfr_t *op;
    int base;
    size_t n;
    mpfr_rnd_t rnd;
    luaL_Stream *fh;
    size_t ret;
    fh = (luaL_Stream *) luaL_checkudata(L, 1, LUA_FILEHANDLE);
    base = (int) luaL_checkinteger(L, 2);
    n = luaL_checkinteger(L, 3);
    op = (mpfr_t *) luaL_checkudata(L, 4, M_mpfr_t);
    rnd = get_rounding_mode(L, 5);
    ret = mpfr_out_str(fh->f, base, n, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_inp_str);

/**********************************************************************/
/* 5.9 Formatted Output Functions */

NOT_IMPLEMENTED(mpfr_fprintf);
NOT_IMPLEMENTED(mpfr_vfprintf);

int l_mpfr_printf(lua_State * L)
{
    mpfr_t *rop, *op;
    const char *fmt;
    int ltype, ret = 0, top;
    size_t l;
    top = lua_gettop(L);
    if (top > 0) {
        ltype = lua_type(L, 1);
        if (ltype == LUA_TUSERDATA) {
            rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
            if (top >= 2) {
                fmt = luaL_checklstring(L, 2, &l);
                if (top >= 3) {
                    op = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
                    ret = mpfr_printf(fmt, op);
                } else {
                    ret = mpfr_printf(fmt, rop);
                }
            }
        } else {
            fmt = luaL_checklstring(L, 1, &l);
            if (top >= 2) {
                op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
                ret = mpfr_printf(fmt, op);
            }
        }
    }
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_vprintf);

int l_mpfr_sprintf(lua_State * L)
{
    mpfr_t *rop, *op;
    const char *fmt;
    char *p, pos;
    int ltype, ret = 0, top;
    size_t l, n;
    luaL_Buffer b;
    top = lua_gettop(L);
    if (top == 0) {
        lua_pushnil(L);
    } else {
        pos = 1;
        ltype = lua_type(L, pos);
        if (ltype == LUA_TUSERDATA) {
            rop = (mpfr_t *) luaL_checkudata(L, pos, M_mpfr_t);
            pos++;
        }
        if (top >= pos) {
            fmt = luaL_checklstring(L, pos, &l);
            pos++;
            if (top >= pos)
                op = (mpfr_t *) luaL_checkudata(L, pos, M_mpfr_t);
            else
                op = rop;
            n = mpfr_snprintf(NULL, 0, fmt, op);
            if (n >= 0) {
                p = luaL_buffinitsize(L, &b, n + 1);
                ret = mpfr_sprintf(p, fmt, op);
                assert(ret == n);
                luaL_pushresultsize(&b, n);
            } else
                lua_pushnil(L);
        } else
            lua_pushnil(L);
    }
    lua_pushinteger(L, ret);
    return 2;
}

NOT_IMPLEMENTED(mpfr_vsprintf);

int l_mpfr_snprintf(lua_State * L)
{
    mpfr_t *rop, *op;
    const char *fmt;
    char *p;
    int ltype, ret = 0, sz, top;
    size_t l, n;
    luaL_Buffer b;
    top = lua_gettop(L);
    if (top == 0) {
        lua_pushnil(L);
    } else {
        ltype = lua_type(L, 1);
        if (ltype == LUA_TUSERDATA) {
            rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
            if (top >= 2) {
                n = luaL_checkinteger(L, 2);
                if (top >= 3) {
                    fmt = luaL_checklstring(L, 3, &l);
                    p = luaL_buffinitsize(L, &b, n + 1);
                    if (top >= 4) {
                        op = (mpfr_t *) luaL_checkudata(L, 4, M_mpfr_t);
                        ret = mpfr_snprintf(p, n, fmt, op);
                        if (ret > (n - 1))
                            sz = n - 1;
                        else
                            sz = ret;
                        luaL_pushresultsize(&b, sz);
                    } else {
                        ret = mpfr_snprintf(p, n, fmt, rop);
                        if (ret > (n - 1))
                            sz = n - 1;
                        else
                            sz = ret;
                        luaL_pushresultsize(&b, sz);
                    }
                } else
                    lua_pushnil(L);
            } else
                lua_pushnil(L);
        } else {
            n = luaL_checkinteger(L, 1);
            if (top >= 2) {
                fmt = luaL_checklstring(L, 2, &l);
                if (top >= 3) {
                    op = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
                    p = luaL_buffinitsize(L, &b, n + 1);
                    ret = mpfr_snprintf(p, n, fmt, op);
                    if (ret > (n - 1))
                        sz = n - 1;
                    else
                        sz = ret;
                    luaL_pushresultsize(&b, sz);
                }
            } else
                lua_pushnil(L);
        }
    }
    lua_pushinteger(L, ret);
    return 2;
}

NOT_IMPLEMENTED(mpfr_vsnprintf);
NOT_IMPLEMENTED(mpfr_asprintf);
NOT_IMPLEMENTED(mpfr_vasprintf);

/**********************************************************************/
/* 5.10 Integer and Remainder Related Functions */

int l_mpfr_rint(lua_State * L)
{
    mpfr_t *rop, *op;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_rint(*rop, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_ceil(lua_State * L)
{
    mpfr_t *rop, *op;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    ret = mpfr_ceil(*rop, *op);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_floor(lua_State * L)
{
    mpfr_t *rop, *op;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    ret = mpfr_floor(*rop, *op);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_round(lua_State * L)
{
    mpfr_t *rop, *op;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    ret = mpfr_round(*rop, *op);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_trunc(lua_State * L)
{
    mpfr_t *rop, *op;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    ret = mpfr_trunc(*rop, *op);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_rint_ceil(lua_State * L)
{
    mpfr_t *rop, *op;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_rint_ceil(*rop, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_rint_floor(lua_State * L)
{
    mpfr_t *rop, *op;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_rint_floor(*rop, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_rint_round(lua_State * L)
{
    mpfr_t *rop, *op;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_rint_round(*rop, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_rint_trunc(lua_State * L)
{
    mpfr_t *rop, *op;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_rint_trunc(*rop, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_frac(lua_State * L)
{
    mpfr_t *rop, *op;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    rnd = get_rounding_mode(L, 3);
    ret = mpfr_frac(*rop, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_modf(lua_State * L)
{
    mpfr_t *iop, *fop, *op;
    mpfr_rnd_t rnd;
    int ret;
    iop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    fop = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_modf(*iop, *fop, *op, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_fmod(lua_State * L)
{
    mpfr_t *r, *x, *y;
    mpfr_rnd_t rnd;
    int ret;
    r = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    x = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    y = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_fmod(*r, *x, *y, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_remainder(lua_State * L)
{
    mpfr_t *r, *x, *y;
    mpfr_rnd_t rnd;
    int ret;
    r = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    x = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    y = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_remainder(*r, *x, *y, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_remquo(lua_State * L)
{
    mpfr_t *r, *x, *y;
    mpfr_rnd_t rnd;
    int ret;
    long q;
    r = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    x = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    y = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_remquo(*r, &q, *x, *y, rnd);
    lua_pushinteger(L, ret);
    lua_pushinteger(L, q);
    return 2;
}

int l_mpfr_integer_p(lua_State * L)
{
    mpfr_t *op;
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    int i = mpfr_integer_p(*op);
    lua_pushinteger(L, i);
    return 1;
}

/**********************************************************************/
/* 5.11 Rounding-Related Functions */

int l_mpfr_set_default_rounding_mode(lua_State * L)
{
    mpfr_rnd_t rnd;
    rnd = get_rounding_mode(L, 1);
    mpfr_set_default_rounding_mode(rnd);
    return 0;
}

int l_mpfr_get_default_rounding_mode(lua_State * L)
{
    mpfr_rnd_t rnd;
    rnd = mpfr_get_default_rounding_mode();
    switch (rnd) {
    case MPFR_RNDN:
        lua_pushstring(L, "MPFR_RNDN");
        break;
    case MPFR_RNDD:
        lua_pushstring(L, "MPFR_RNDD");
        break;
    case MPFR_RNDU:
        lua_pushstring(L, "MPFR_RNDU");
        break;
    case MPFR_RNDZ:
        lua_pushstring(L, "MPFR_RNDZ");
        break;
    case MPFR_RNDA:
        lua_pushstring(L, "MPFR_RNDA");
        break;
    case MPFR_RNDF:
        lua_pushstring(L, "MPFR_RNDF");
        break;
    default:
        assert(0);
    }
    return 1;
}

int l_mpfr_prec_round(lua_State * L)
{
    mpfr_t *x;
    mpfr_prec_t prec;
    mpfr_rnd_t rnd;
    int ret;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    prec = (mpfr_prec_t) luaL_checkinteger(L, 2);
    rnd = get_rounding_mode(L, 3);
    if ((prec < MPFR_PREC_MIN) || (prec > MPFR_PREC_MAX))
        luaL_error(L, "precision outside MPFR_PREC_MIN...MPFR_PREX_MAX");
    ret = mpfr_prec_round(*x, prec, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_can_round);

int l_mpfr_min_prec(lua_State * L)
{
    mpfr_t *x;
    int ret;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    ret = mpfr_min_prec(*x);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_print_rnd_mode);

/**********************************************************************/
/* 5.12 Miscellaneous Functions */

int l_mpfr_nexttoward(lua_State * L)
{
    mpfr_t *x, *y;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    y = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    mpfr_nexttoward(*x, *y);
    return 0;
}

int l_mpfr_nextabove(lua_State * L)
{
    mpfr_t *x;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    mpfr_nextabove(*x);
    return 0;
}

int l_mpfr_nextbelow(lua_State * L)
{
    mpfr_t *x;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    mpfr_nextbelow(*x);
    return 0;
}

int l_mpfr_min(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_min(*rop, *op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_max(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_max(*rop, *op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_urandomb);
NOT_IMPLEMENTED(mpfr_urandom);
NOT_IMPLEMENTED(mpfr_grandom);

int l_mpfr_get_exp(lua_State * L)
{
    mpfr_t *x;
    mpfr_exp_t e;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    e = mpfr_get_exp(*x);
    lua_pushinteger(L, (lua_Integer) e);
    return 1;
}

int l_mpfr_set_exp(lua_State * L)
{
    mpfr_t *x;
    lua_Integer e;
    int ret;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    e = luaL_checkinteger(L, 2);
    ret = mpfr_set_exp(*x, (mpfr_exp_t) e);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_signbit(lua_State * L)
{
    mpfr_t *op;
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    int i = mpfr_signbit(*op);
    lua_pushinteger(L, i);
    return 1;
}

int l_mpfr_setsign(lua_State * L)
{
    mpfr_t *rop, *op;
    lua_Integer s;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    s = luaL_checkinteger(L, 3);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_setsign(*rop, *op, s, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_copysign(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    mpfr_rnd_t rnd;
    int ret;
    rop = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op1 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 3, M_mpfr_t);
    rnd = get_rounding_mode(L, 4);
    ret = mpfr_copysign(*rop, *op1, *op2, rnd);
    lua_pushinteger(L, ret);
    return 1;
}

NOT_IMPLEMENTED(mpfr_get_version);
NOT_IMPLEMENTED(mpfr_get_patches);

int l_mpfr_buildopt_tls_p(lua_State * L)
{
    int i;
    i = mpfr_buildopt_tls_p();
    lua_pushinteger(L, i);
    return 1;
}

int l_mpfr_buildopt_decimal_p(lua_State * L)
{
    int i;
    i = mpfr_buildopt_decimal_p();
    lua_pushinteger(L, i);
    return 1;
}

int l_mpfr_buildopt_gmpinternals_p(lua_State * L)
{
    int i;
    i = mpfr_buildopt_gmpinternals_p();
    lua_pushinteger(L, i);
    return 1;
}

NOT_IMPLEMENTED(mpfr_buildopt_tune_case);

/**********************************************************************/
/* 5.13 Exception Related Functions */

int l_mpfr_get_emin(lua_State * L)
{
    mpfr_exp_t i;
    i = mpfr_get_emin();
    lua_pushinteger(L, (lua_Integer) i);
    return 1;
}

int l_mpfr_get_emax(lua_State * L)
{
    mpfr_exp_t i;
    i = mpfr_get_emax();
    lua_pushinteger(L, (lua_Integer) i);
    return 1;
}

int l_mpfr_set_emin(lua_State * L)
{
    lua_Integer e;
    int ret;
    e = luaL_checkinteger(L, 1);
    ret = mpfr_set_emin((mpfr_exp_t) e);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_set_emax(lua_State * L)
{
    lua_Integer e;
    int ret;
    e = luaL_checkinteger(L, 1);
    ret = mpfr_set_emax((mpfr_exp_t) e);
    lua_pushinteger(L, ret);
    return 1;
}

int l_mpfr_get_emin_min(lua_State * L)
{
    mpfr_exp_t i;
    i = mpfr_get_emin_min();
    lua_pushinteger(L, (lua_Integer) i);
    return 1;
}

int l_mpfr_get_emin_max(lua_State * L)
{
    mpfr_exp_t i;
    i = mpfr_get_emin_max();
    lua_pushinteger(L, (lua_Integer) i);
    return 1;
}

int l_mpfr_get_emax_min(lua_State * L)
{
    mpfr_exp_t i;
    i = mpfr_get_emax_min();
    lua_pushinteger(L, (lua_Integer) i);
    return 1;
}

int l_mpfr_get_emax_max(lua_State * L)
{
    mpfr_exp_t i;
    i = mpfr_get_emax_max();
    lua_pushinteger(L, (lua_Integer) i);
    return 1;
}

NOT_IMPLEMENTED(mpfr_check_range);
NOT_IMPLEMENTED(mpfr_subnormalize);

int l_mpfr_clear_underflow(lua_State * L)
{
    mpfr_clear_underflow();
    return 0;
}

int l_mpfr_clear_overflow(lua_State * L)
{
    mpfr_clear_overflow();
    return 0;
}

int l_mpfr_clear_divby0(lua_State * L)
{
    mpfr_clear_divby0();
    return 0;
}

int l_mpfr_clear_nanflag(lua_State * L)
{
    mpfr_clear_nanflag();
    return 0;
}

int l_mpfr_clear_inexflag(lua_State * L)
{
    mpfr_clear_inexflag();
    return 0;
}

int l_mpfr_clear_erangeflag(lua_State * L)
{
    mpfr_clear_erangeflag();
    return 0;
}

int l_mpfr_set_underflow(lua_State * L)
{
    mpfr_set_underflow();
    return 0;
}

int l_mpfr_set_overflow(lua_State * L)
{
    mpfr_set_overflow();
    return 0;
}

int l_mpfr_set_divby0(lua_State * L)
{
    mpfr_set_divby0();
    return 0;
}

int l_mpfr_set_nanflag(lua_State * L)
{
    mpfr_set_nanflag();
    return 0;
}

int l_mpfr_set_inexflag(lua_State * L)
{
    mpfr_set_inexflag();
    return 0;
}

int l_mpfr_set_erangeflag(lua_State * L)
{
    mpfr_set_erangeflag();
    return 0;
}

int l_mpfr_clear_flags(lua_State * L)
{
    mpfr_clear_flags();
    return 0;
}

int l_mpfr_underflow_p(lua_State * L)
{
    int i = mpfr_underflow_p();
    lua_pushinteger(L, i);
    return 1;
}

int l_mpfr_overflow_p(lua_State * L)
{
    int i = mpfr_overflow_p();
    lua_pushinteger(L, i);
    return 1;
}

int l_mpfr_divby0_p(lua_State * L)
{
    int i = mpfr_divby0_p();
    lua_pushinteger(L, i);
    return 1;
}

int l_mpfr_nanflag_p(lua_State * L)
{
    int i = mpfr_nanflag_p();
    lua_pushinteger(L, i);
    return 1;
}

int l_mpfr_inexflag_p(lua_State * L)
{
    int i = mpfr_inexflag_p();
    lua_pushinteger(L, i);
    return 1;
}

int l_mpfr_erangeflag_p(lua_State * L)
{
    int i = mpfr_erangeflag_p();
    lua_pushinteger(L, i);
    return 1;
}

/**********************************************************************/
/* 5.14 Memory Handling Functions */

/**********************************************************************/
/* 5.15 Compatibility With MPF */

NOT_IMPLEMENTED(mpfr_set_prec_raw);
NOT_IMPLEMENTED(mpfr_eq);
NOT_IMPLEMENTED(mpfr_reldiff);
NOT_IMPLEMENTED(mpfr_mul_2exp);
NOT_IMPLEMENTED(mpfr_div_2exp);

/**********************************************************************/
/* 5.16 Custom Interface */

NOT_IMPLEMENTED(mpfr_custom_get_size);
NOT_IMPLEMENTED(mpfr_custom_init);
NOT_IMPLEMENTED(mpfr_custom_init_set);
NOT_IMPLEMENTED(mpfr_custom_get_kind);
NOT_IMPLEMENTED(mpfr_custom_get_significand);
NOT_IMPLEMENTED(mpfr_custom_get_exp);
NOT_IMPLEMENTED(mpfr_custom_move);

/**********************************************************************/
/* 5.17 Internals */

/**********************************************************************/

static const struct luaL_Reg libmpfr_t_f[] = {
    /* 5.1 Initialization Functions */
    {"num", l_mpfr_num},        /* not part of MPFR */
    {"init2", l_mpfr_init2},
    {"inits2", l_mpfr_inits2},
    {"clear", l_mpfr_clear},
    {"clears", l_mpfr_clears},
    {"init", l_mpfr_init},
    {"inits", l_mpfr_inits},
    {"set_default_prec", l_mpfr_set_default_prec},
    {"get_default_prec", l_mpfr_get_default_prec},
    {"set_prec", l_mpfr_set_prec},
    {"get_prec", l_mpfr_get_prec},
    /* 5.2 Assignment Functions */
    {"set", l_mpfr_set},
    {"set_ui", l_mpfr_set_ui},
    {"set_si", l_mpfr_set_si},
    {"set_uj", l_mpfr_set_uj},
    {"set_sj", l_mpfr_set_sj},
    {"set_flt", l_mpfr_set_flt},
    {"set_d", l_mpfr_set_d},
    {"set_ld", l_mpfr_set_ld},
    {"set_decimal64", l_mpfr_set_decimal64},
    {"set_float128", l_mpfr_set_float128},
    {"set_decimal64", l_mpfr_set_decimal64},
    {"set_decimal128", l_mpfr_set_decimal128},
    {"set_z", l_mpfr_set_z},
    {"set_q", l_mpfr_set_q},
    {"set_f", l_mpfr_set_f},
    {"set_ui_2exp", l_mpfr_set_ui_2exp},
    {"set_si_2exp", l_mpfr_set_si_2exp},
    {"set_uj_2exp", l_mpfr_set_uj_2exp},
    {"set_sj_2exp", l_mpfr_set_sj_2exp},
    {"set_z_2exp", l_mpfr_set_z_2exp},
    {"set_str", l_mpfr_set_str},
    {"set_strtofr", l_mpfr_strtofr},
    {"set_nan", l_mpfr_set_nan},
    {"set_inf", l_mpfr_set_inf},
    {"set_zero", l_mpfr_set_zero},
    {"swap", l_mpfr_swap},
    /* 5.3 Combined Initialization and Assignment Functions */
    {"init_set", l_mpfr_init_set},
    {"init_set_ui", l_mpfr_init_set_ui},
    {"init_set_si", l_mpfr_init_set_si},
    {"init_set_d", l_mpfr_init_set_d},
    {"init_set_ld", l_mpfr_init_set_ld},
    {"init_set_z", l_mpfr_init_set_z},
    {"init_set_q", l_mpfr_init_set_q},
    {"init_set_f", l_mpfr_init_set_f},
    {"init_set_str", l_mpfr_init_set_str},
    /* 5.4 Conversion Functions */
    {"get_flt", l_mpfr_get_flt},
    {"get_d", l_mpfr_get_d},
    {"get_ld", l_mpfr_get_ld},
    {"get_float128", l_mpfr_get_float128},
    {"get_decimal64", l_mpfr_get_decimal64},
    {"get_decimal128", l_mpfr_get_decimal128},
    {"get_si", l_mpfr_get_si},
    {"get_ui", l_mpfr_get_ui},
    {"get_sj", l_mpfr_get_sj},
    {"get_sj", l_mpfr_get_uj},
    {"get_d_2exp", l_mpfr_get_d_2exp},
    {"get_ld_2exp", l_mpfr_get_ld_2exp},
    {"frexp", l_mpfr_frexp},
    {"get_z_2exp", l_mpfr_get_z_2exp},
    {"get_z", l_mpfr_get_z},
    {"get_f", l_mpfr_get_f},
    {"get_str", l_mpfr_get_str},
    {"free_str", l_mpfr_free_str},
    {"fits_ulong_p", l_mpfr_fits_ulong_p},
    {"fits_slong_p", l_mpfr_fits_slong_p},
    {"fits_uint_p", l_mpfr_fits_uint_p},
    {"fits_sint_p", l_mpfr_fits_sint_p},
    {"fits_ushort_p", l_mpfr_fits_ushort_p},
    {"fits_sshort_p", l_mpfr_fits_sshort_p},
    {"fits_uintmax_p", l_mpfr_fits_uintmax_p},
    {"fits_intmax_p", l_mpfr_fits_intmax_p},
    /* 5.5 Basic Arithmetic Functions */
    {"add", l_mpfr_add},
    {"add_ui", l_mpfr_add_ui},
    {"add_si", l_mpfr_add_si},
    {"add_d", l_mpfr_add_d},
    {"add_z", l_mpfr_add_z},
    {"add_q", l_mpfr_add_q},
    {"sub", l_mpfr_sub},
    {"ui_sub", l_mpfr_ui_sub},
    {"sub_ui", l_mpfr_sub_ui},
    {"si_sub", l_mpfr_si_sub},
    {"sub_si", l_mpfr_sub_si},
    {"d_sub", l_mpfr_d_sub},
    {"sub_d", l_mpfr_sub_d},
    {"z_sub", l_mpfr_z_sub},
    {"sub_z", l_mpfr_sub_z},
    {"sub_q", l_mpfr_sub_q},
    {"mul", l_mpfr_mul},
    {"mul_ui", l_mpfr_mul_ui},
    {"mul_si", l_mpfr_mul_si},
    {"mul_d", l_mpfr_mul_d},
    {"mul_z", l_mpfr_mul_z},
    {"mul_q", l_mpfr_mul_q},
    {"sqr", l_mpfr_sqr},
    {"div", l_mpfr_div},
    {"ui_div", l_mpfr_ui_div},
    {"div_ui", l_mpfr_div_ui},
    {"si_div", l_mpfr_si_div},
    {"div_si", l_mpfr_div_si},
    {"div_d", l_mpfr_div_d},
    {"div_z", l_mpfr_div_z},
    {"div_q", l_mpfr_div_q},
    {"sqrt", l_mpfr_sqrt},
    {"sqrt_ui", l_mpfr_sqrt_ui},
    {"rec_sqrt", l_mpfr_rec_sqrt},
    {"cbrt", l_mpfr_cbrt},
    {"root", l_mpfr_root},
    {"pow", l_mpfr_pow},
    {"pow_ui", l_mpfr_pow_ui},
    {"pow_si", l_mpfr_pow_si},
    {"pow_z", l_mpfr_pow_z},
    {"ui_pow_ui", l_mpfr_ui_pow_ui},
    {"ui_pow", l_mpfr_ui_pow},
    {"neg", l_mpfr_neg},
    {"abs", l_mpfr_abs},
    {"dim", l_mpfr_dim},
    {"mul_2ui", l_mpfr_mul_2ui},
    {"mul_2si", l_mpfr_mul_2si},
    {"div_2ui", l_mpfr_div_2ui},
    {"div_2si", l_mpfr_div_2si},
    /* 5.6 Comparison Functions */
    {"cmp", l_mpfr_cmp},
    {"cmp_ui", l_mpfr_cmp_ui},
    {"cmp_si", l_mpfr_cmp_si},
    {"cmp_d", l_mpfr_cmp_d},
    {"cmp_ld", l_mpfr_cmp_ld},
    {"cmp_z", l_mpfr_cmp_z},
    {"cmp_q", l_mpfr_cmp_q},
    {"cmp_f", l_mpfr_cmp_f},
    {"cmp_ui_2exp", l_mpfr_cmp_ui_2exp},
    {"cmp_si_2exp", l_mpfr_cmp_si_2exp},
    {"cmpabs", l_mpfr_cmpabs},
    {"nan_p", l_mpfr_nan_p},
    {"inf_p", l_mpfr_inf_p},
    {"number_p", l_mpfr_number_p},
    {"zero_p", l_mpfr_zero_p},
    {"regular_p", l_mpfr_regular_p},
    {"sgn", l_mpfr_sgn},
    {"greater_p", l_mpfr_greater_p},
    {"greaterequal_p", l_mpfr_greaterequal_p},
    {"less_p", l_mpfr_less_p},
    {"lessequal_p", l_mpfr_lessequal_p},
    {"equal_p", l_mpfr_equal_p},
    {"lessgreater_p", l_mpfr_lessgreater_p},
    {"unordered_p", l_mpfr_unordered_p},
    /* 5.7 Special Functions */
    {"log", l_mpfr_log},
    {"log2", l_mpfr_log2},
    {"log10", l_mpfr_log10},
    {"exp", l_mpfr_exp},
    {"exp2", l_mpfr_exp2},
    {"exp10", l_mpfr_exp10},
    {"cos", l_mpfr_cos},
    {"sin", l_mpfr_sin},
    {"tan", l_mpfr_tan},
    {"sin_cos", l_mpfr_sin_cos},
    {"sec", l_mpfr_sec},
    {"csc", l_mpfr_csc},
    {"cot", l_mpfr_cot},
    {"acos", l_mpfr_acos},
    {"asin", l_mpfr_asin},
    {"atan", l_mpfr_atan},
    {"atan2", l_mpfr_atan2},
    {"cosh", l_mpfr_cosh},
    {"sinh", l_mpfr_sinh},
    {"tanh", l_mpfr_tanh},
    {"sinh_cosh", l_mpfr_sinh_cosh},
    {"sech", l_mpfr_sech},
    {"csch", l_mpfr_csch},
    {"coth", l_mpfr_coth},
    {"acosh", l_mpfr_acosh},
    {"asinh", l_mpfr_asinh},
    {"atanh", l_mpfr_atanh},
    {"fac_ui", l_mpfr_fac_ui},
    {"log1p", l_mpfr_log1p},
    {"expm1", l_mpfr_expm1},
    {"eint", l_mpfr_eint},
    {"li2", l_mpfr_li2},
    {"gamma", l_mpfr_gamma},
    {"lngamma", l_mpfr_lngamma},
    {"lgamma", l_mpfr_lgamma},
    {"digamma", l_mpfr_digamma},
    {"zeta", l_mpfr_zeta},
    {"zeta_ui", l_mpfr_zeta_ui},
    {"erf", l_mpfr_erf},
    {"erfc", l_mpfr_erfc},
    {"j0", l_mpfr_j0},
    {"j1", l_mpfr_j1},
    {"jn", l_mpfr_jn},
    {"y0", l_mpfr_y0},
    {"y1", l_mpfr_y1},
    {"yn", l_mpfr_yn},
    {"fma", l_mpfr_fma},
    {"fms", l_mpfr_fms},
    {"agm", l_mpfr_agm},
    {"hypot", l_mpfr_hypot},
    {"ai", l_mpfr_ai},
    {"const_log2", l_mpfr_const_log2},
    {"const_pi", l_mpfr_const_pi},
    {"const_euler", l_mpfr_const_euler},
    {"const_catalan", l_mpfr_const_catalan},
    {"free_cache", l_mpfr_free_cache},
    {"sum", l_mpfr_sum},
    /* 5.8 Input and Output Functions */
    {"out_str", l_mpfr_out_str},
    {"inp_str", l_mpfr_inp_str},
    /* 5.9 Formatted Output Functions */
    {"fprintf", l_mpfr_fprintf},
    {"vfprintf", l_mpfr_vfprintf},
    {"printf", l_mpfr_printf},
    {"vprintf", l_mpfr_vprintf},
    {"sprintf", l_mpfr_sprintf},
    {"vsprintf", l_mpfr_vsprintf},
    {"snprintf", l_mpfr_snprintf},
    {"vsnprintf", l_mpfr_vsnprintf},
    {"asprintf", l_mpfr_asprintf},
    {"vasprintf", l_mpfr_vasprintf},
    /* 5.10 Integer and Remainder Related Functions */
    {"rint", l_mpfr_rint},
    {"ceil", l_mpfr_ceil},
    {"floor", l_mpfr_floor},
    {"round", l_mpfr_round},
    {"trunc", l_mpfr_trunc},
    {"rint_ceil", l_mpfr_rint_ceil},
    {"rint_floor", l_mpfr_rint_floor},
    {"rint_round", l_mpfr_rint_round},
    {"rint_trunc", l_mpfr_rint_trunc},
    {"frac", l_mpfr_frac},
    {"modf", l_mpfr_modf},
    {"fmod", l_mpfr_fmod},
    {"remainder", l_mpfr_remainder},
    {"remquo", l_mpfr_remquo},
    {"integer_p", l_mpfr_integer_p},
    /* 5.11 Rounding Related Functions */
    {"set_default_rounding_mode", l_mpfr_set_default_rounding_mode},
    {"prec_round", l_mpfr_prec_round},
    {"can_round", l_mpfr_can_round},
    {"min_prec", l_mpfr_min_prec},
    {"print_rnd_mode", l_mpfr_print_rnd_mode},
    /* 5.12 Miscellaneous Functions */
    {"nexttoward", l_mpfr_nexttoward},
    {"nextabove", l_mpfr_nextabove},
    {"nextbelow", l_mpfr_nextbelow},
    {"min", l_mpfr_min},
    {"max", l_mpfr_max},
    {"urandomb", l_mpfr_urandomb},
    {"urandom", l_mpfr_urandom},
    {"grandom", l_mpfr_grandom},
    {"get_exp", l_mpfr_get_exp},
    {"set_exp", l_mpfr_set_exp},
    {"signbit", l_mpfr_signbit},
    {"setsign", l_mpfr_setsign},
    {"copysign", l_mpfr_copysign},
    {"get_version", l_mpfr_get_version},
    {"get_patches", l_mpfr_get_patches},
    {"buildopt_tls_p", l_mpfr_buildopt_tls_p},
    {"buildopt_decimal_p", l_mpfr_buildopt_decimal_p},
    {"buildopt_gmpinternals_p", l_mpfr_buildopt_gmpinternals_p},
    {"buildopt_tune_case", l_mpfr_buildopt_tune_case},
    /* 5.13 Exception Related Functions */
    {"get_emin", l_mpfr_get_emin},
    {"get_emax", l_mpfr_get_emax},
    {"set_emin", l_mpfr_set_emin},
    {"set_emax", l_mpfr_set_emax},
    {"get_emin_min", l_mpfr_get_emin_min},
    {"get_emin_max", l_mpfr_get_emin_max},
    {"get_emax_min", l_mpfr_get_emax_min},
    {"get_emax_max", l_mpfr_get_emax_max},
    {"check_range", l_mpfr_check_range},
    {"subnormalize", l_mpfr_subnormalize},
    {"clear_underflow", l_mpfr_clear_underflow},
    {"clear_overflow", l_mpfr_clear_overflow},
    {"clear_divby0", l_mpfr_clear_divby0},
    {"clear_nanflag", l_mpfr_clear_nanflag},
    {"clear_inexflag", l_mpfr_clear_inexflag},
    {"clear_erangeflag", l_mpfr_clear_erangeflag},
    {"set_underflow", l_mpfr_set_underflow},
    {"set_overflow", l_mpfr_set_overflow},
    {"set_divby0", l_mpfr_set_divby0},
    {"set_nanflag", l_mpfr_set_nanflag},
    {"set_inexflag", l_mpfr_set_inexflag},
    {"set_erangeflag", l_mpfr_set_erangeflag},
    {"clear_flags", l_mpfr_clear_flags},
    {"underflow_p", l_mpfr_underflow_p},
    {"overflow_p", l_mpfr_overflow_p},
    {"divby0_p", l_mpfr_divby0_p},
    {"nanflag_p", l_mpfr_nanflag_p},
    {"inexflag_p", l_mpfr_inexflag_p},
    {"erangeflag_p", l_mpfr_erangeflag_p},
    /* 5.14 Compatibility With MPF */
    {"set_prec_raw", l_mpfr_set_prec_raw},
    {"eq", l_mpfr_eq},
    {"reldiff", l_mpfr_reldiff},
    {"mul_2exp", l_mpfr_mul_2exp},
    {"div_2exp", l_mpfr_div_2exp},
    /* 5.15 Custom Interface */
    {"custom_get_size", l_mpfr_custom_get_size},
    {"custom_init", l_mpfr_custom_init},
    {"custom_init_set", l_mpfr_custom_init_set},
    {"custom_get_kind", l_mpfr_custom_get_kind},
    {"custom_get_significand", l_mpfr_custom_get_significand},
    {"custom_get_exp", l_mpfr_custom_get_exp},
    {"custom_move", l_mpfr_custom_move},
    {NULL, NULL}                /* sentinel */
};

/**********************************************************************/

static int m__add(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    lua_Number n1, n2;
    lua_Integer i1, i2;
    int t1, t2;
    mpfr_rnd_t rnd;
    rop = new_mpfr_t(L);
    mpfr_init(*rop);
    rnd = mpfr_get_default_rounding_mode();
    t1 = lua_type(L, 1);
    t2 = lua_type(L, 2);
    if ((t1 == LUA_TUSERDATA) && (t2 == LUA_TUSERDATA)) {
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
        (void) mpfr_add(*rop, *op1, *op2, rnd);
    } else if (t1 == LUA_TUSERDATA) {
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        n2 = luaL_checknumber(L, 2);
        if (lua_isinteger(L, 2)) {
            (void) lua_numbertointeger(n2, &i2);
            (void) mpfr_add_si(*rop, *op1, i2, rnd);
        } else
            (void) mpfr_add_d(*rop, *op1, n2, rnd);
    } else if (t2 == LUA_TUSERDATA) {
        n1 = luaL_checknumber(L, 1);
        op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
        if (lua_isinteger(L, 1)) {
            (void) lua_numbertointeger(n1, &i1);
            (void) mpfr_add_si(*rop, *op2, i1, rnd);
        } else
            (void) mpfr_add_d(*rop, *op2, n1, rnd);
    } else
        assert(0);
    return 1;
}

static int m__sub(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    lua_Number n1, n2;
    lua_Integer i1, i2;
    int t1, t2;
    mpfr_rnd_t rnd;
    rop = new_mpfr_t(L);
    mpfr_init(*rop);
    rnd = mpfr_get_default_rounding_mode();
    t1 = lua_type(L, 1);
    t2 = lua_type(L, 2);
    if ((t1 == LUA_TUSERDATA) && (t2 == LUA_TUSERDATA)) {
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
        (void) mpfr_sub(*rop, *op1, *op2, rnd);
    } else if (t1 == LUA_TUSERDATA) {
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        n2 = luaL_checknumber(L, 2);
        if (lua_isinteger(L, 2)) {
            (void) lua_numbertointeger(n2, &i2);
            (void) mpfr_sub_si(*rop, *op1, i2, rnd);
        } else
            (void) mpfr_sub_d(*rop, *op1, n2, rnd);
    } else if (t2 == LUA_TUSERDATA) {
        n1 = luaL_checknumber(L, 1);
        op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
        if (lua_isinteger(L, 1)) {
            (void) lua_numbertointeger(n1, &i1);
            (void) mpfr_si_sub(*rop, i1, *op2, rnd);
        } else
            (void) mpfr_d_sub(*rop, n1, *op2, rnd);
    } else
        assert(0);
    return 1;
}

static int m__mul(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    lua_Number n1, n2;
    lua_Integer i1, i2;
    int t1, t2;
    mpfr_rnd_t rnd;
    rop = new_mpfr_t(L);
    mpfr_init(*rop);
    rnd = mpfr_get_default_rounding_mode();
    t1 = lua_type(L, 1);
    t2 = lua_type(L, 2);
    if ((t1 == LUA_TUSERDATA) && (t2 == LUA_TUSERDATA)) {
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
        (void) mpfr_mul(*rop, *op1, *op2, rnd);
    } else if (t1 == LUA_TUSERDATA) {
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        n2 = luaL_checknumber(L, 2);
        if (lua_isinteger(L, 2)) {
            (void) lua_numbertointeger(n2, &i2);
            (void) mpfr_mul_si(*rop, *op1, i2, rnd);
        } else
            (void) mpfr_mul_d(*rop, *op1, n2, rnd);
    } else if (t2 == LUA_TUSERDATA) {
        n1 = luaL_checknumber(L, 1);
        op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
        if (lua_isinteger(L, 1)) {
            (void) lua_numbertointeger(n1, &i1);
            (void) mpfr_mul_si(*rop, *op2, i1, rnd);
        } else
            (void) mpfr_mul_d(*rop, *op2, n1, rnd);
    } else
        assert(0);
    return 1;
}

static int m__div(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    lua_Number n1, n2;
    lua_Integer i1, i2;
    int t1, t2;
    mpfr_rnd_t rnd;
    rop = new_mpfr_t(L);
    mpfr_init(*rop);
    rnd = mpfr_get_default_rounding_mode();
    t1 = lua_type(L, 1);
    t2 = lua_type(L, 2);
    if ((t1 == LUA_TUSERDATA) && (t2 == LUA_TUSERDATA)) {
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
        (void) mpfr_div(*rop, *op1, *op2, rnd);
    } else if (t1 == LUA_TUSERDATA) {
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        n2 = luaL_checknumber(L, 2);
        if (lua_isinteger(L, 2)) {
            (void) lua_numbertointeger(n2, &i2);
            (void) mpfr_div_si(*rop, *op1, i2, rnd);
        } else
            (void) mpfr_div_d(*rop, *op1, n2, rnd);
    } else if (t2 == LUA_TUSERDATA) {
        n1 = luaL_checknumber(L, 1);
        op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
        if (lua_isinteger(L, 1)) {
            (void) lua_numbertointeger(n1, &i1);
            (void) mpfr_si_div(*rop, i1, *op2, rnd);
        } else
            (void) mpfr_d_div(*rop, n1, *op2, rnd);
    } else
        assert(0);
    return 1;
}

static int m__pow(lua_State * L)
{
    mpfr_t *rop, *op1, *op2;
    lua_Number n2;
    lua_Integer i2;
    int t1, t2;
    mpfr_rnd_t rnd;
    rop = new_mpfr_t(L);
    mpfr_init(*rop);
    rnd = mpfr_get_default_rounding_mode();
    t1 = lua_type(L, 1);
    t2 = lua_type(L, 2);
    if ((t1 == LUA_TUSERDATA) && (t2 == LUA_TUSERDATA)) {
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
        (void) mpfr_pow(*rop, *op1, *op2, rnd);
    } else if (t1 == LUA_TUSERDATA) {
        op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
        n2 = luaL_checknumber(L, 2);
        if (lua_isinteger(L, 2)) {
            (void) lua_numbertointeger(n2, &i2);
            (void) mpfr_pow_si(*rop, *op1, i2, rnd);
        } else
            luaL_error(L, "not implemented yet");
    } else
        luaL_error(L, "not implemented yet");
    return 1;
}

static int m__unm(lua_State * L)
{
    mpfr_t *rop, *op;
    mpfr_rnd_t rnd;
    op = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);    /* op */
    rop = new_mpfr_t(L);
    mpfr_init(*rop);
    rnd = mpfr_get_default_rounding_mode();
    (void) mpfr_neg(*rop, *op, rnd);    /* rop op */
    return 1;
}

static int m__eq(lua_State * L)
{
    mpfr_t *op1, *op2;
    int i;
    op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    i = mpfr_equal_p(*op1, *op2);
    lua_pushboolean(L, i);
    return 1;
}

static int m__lt(lua_State * L)
{
    mpfr_t *op1, *op2;
    int i;
    op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    i = mpfr_less_p(*op1, *op2);
    lua_pushboolean(L, i);
    return 1;
}

static int m__le(lua_State * L)
{
    mpfr_t *op1, *op2;
    int i;
    op1 = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    op2 = (mpfr_t *) luaL_checkudata(L, 2, M_mpfr_t);
    i = mpfr_lessequal_p(*op1, *op2);
    lua_pushboolean(L, i);
    return 1;
}

static int m__gc(lua_State * L)
{
    mpfr_t *x;
    x = (mpfr_t *) luaL_checkudata(L, 1, M_mpfr_t);
    mpfr_clear(*x);
    return 0;
}

static const struct luaL_Reg libmpfr_t_m[] = {
    /* 5.1 Initialization Functions */
    {"clear", l_mpfr_clear},
    /* 5.2 Assignment Functions */
    {"set", l_mpfr_set},
    {"set_ui", l_mpfr_set_ui},
    {"set_si", l_mpfr_set_si},
    {"set_uj", l_mpfr_set_uj},
    {"set_sj", l_mpfr_set_sj},
    {"set_flt", l_mpfr_set_flt},
    {"set_d", l_mpfr_set_d},
    {"set_ld", l_mpfr_set_ld},
    {"set_decimal64", l_mpfr_set_decimal64},
    {"set_z", l_mpfr_set_z},
    {"set_q", l_mpfr_set_q},
    {"set_f", l_mpfr_set_f},
    {"set_ui_2exp", l_mpfr_set_ui_2exp},
    {"set_si_2exp", l_mpfr_set_si_2exp},
    {"set_uj_2exp", l_mpfr_set_uj_2exp},
    {"set_sj_2exp", l_mpfr_set_sj_2exp},
    {"set_z_2exp", l_mpfr_set_z_2exp},
    {"set_str", l_mpfr_set_str},
    {"set_strtofr", l_mpfr_strtofr},
    {"set_nan", l_mpfr_set_nan},
    {"set_inf", l_mpfr_set_inf},
    {"set_zero", l_mpfr_set_zero},
    {"swap", l_mpfr_swap},
    /* 5.3 Combined Initialization and Assignment Functions */
    /* 5.4 Conversion Functions */
    {"get_flt", l_mpfr_get_flt},
    {"get_d", l_mpfr_get_d},
    {"get_ld", l_mpfr_get_ld},
    {"get_decimal64", l_mpfr_get_decimal64},
    {"get_si", l_mpfr_get_si},
    {"get_ui", l_mpfr_get_ui},
    {"get_sj", l_mpfr_get_sj},
    {"get_sj", l_mpfr_get_uj},
    {"get_d_2exp", l_mpfr_get_d_2exp},
    {"get_ld_2exp", l_mpfr_get_ld_2exp},
    {"frexp", l_mpfr_frexp},
    {"get_z_2exp", l_mpfr_get_z_2exp},
    {"get_z", l_mpfr_get_z},
    {"get_f", l_mpfr_get_f},
    {"get_str", l_mpfr_get_str},
    {"free_str", l_mpfr_free_str},
    {"fits_ulong_p", l_mpfr_fits_ulong_p},
    {"fits_slong_p", l_mpfr_fits_slong_p},
    {"fits_uint_p", l_mpfr_fits_uint_p},
    {"fits_sint_p", l_mpfr_fits_sint_p},
    {"fits_ushort_p", l_mpfr_fits_ushort_p},
    {"fits_sshort_p", l_mpfr_fits_sshort_p},
    {"fits_uintmax_p", l_mpfr_fits_uintmax_p},
    {"fits_intmax_p", l_mpfr_fits_intmax_p},
    /* 5.5 Basic Arithmetic Functions */
    {"add", l_mpfr_add},
    {"add_ui", l_mpfr_add_ui},
    {"add_si", l_mpfr_add_si},
    {"add_d", l_mpfr_add_d},
    {"add_z", l_mpfr_add_z},
    {"add_q", l_mpfr_add_q},
    {"sub", l_mpfr_sub},
    {"ui_sub", l_mpfr_ui_sub},
    {"sub_ui", l_mpfr_sub_ui},
    {"si_sub", l_mpfr_si_sub},
    {"sub_si", l_mpfr_sub_si},
    {"d_sub", l_mpfr_d_sub},
    {"sub_d", l_mpfr_sub_d},
    {"z_sub", l_mpfr_z_sub},
    {"sub_z", l_mpfr_sub_z},
    {"sub_q", l_mpfr_sub_q},
    {"mul", l_mpfr_mul},
    {"mul_ui", l_mpfr_mul_ui},
    {"mul_si", l_mpfr_mul_si},
    {"mul_d", l_mpfr_mul_d},
    {"mul_z", l_mpfr_mul_z},
    {"mul_q", l_mpfr_mul_q},
    {"sqr", l_mpfr_sqr},
    {"div", l_mpfr_div},
    {"ui_div", l_mpfr_ui_div},
    {"div_ui", l_mpfr_div_ui},
    {"si_div", l_mpfr_si_div},
    {"div_si", l_mpfr_div_si},
    {"div_d", l_mpfr_div_d},
    {"div_z", l_mpfr_div_z},
    {"div_q", l_mpfr_div_q},
    {"sqrt", l_mpfr_sqrt},
    {"sqrt_ui", l_mpfr_sqrt_ui},
    {"rec_sqrt", l_mpfr_rec_sqrt},
    {"cbrt", l_mpfr_cbrt},
    {"root", l_mpfr_root},
    {"pow", l_mpfr_pow},
    {"pow_ui", l_mpfr_pow_ui},
    {"pow_si", l_mpfr_pow_si},
    {"pow_z", l_mpfr_pow_z},
    {"ui_pow_ui", l_mpfr_ui_pow_ui},
    {"ui_pow", l_mpfr_ui_pow},
    {"neg", l_mpfr_neg},
    {"abs", l_mpfr_abs},
    {"dim", l_mpfr_dim},
    {"mul_2ui", l_mpfr_mul_2ui},
    {"mul_2si", l_mpfr_mul_2si},
    {"div_2ui", l_mpfr_div_2ui},
    {"div_2si", l_mpfr_div_2si},
    /* 5.5 Basic Arithmetic Functions */
    {"add", l_mpfr_add},
    {"add_ui", l_mpfr_add_ui},
    {"add_si", l_mpfr_add_si},
    {"add_d", l_mpfr_add_d},
    {"add_z", l_mpfr_add_z},
    {"add_q", l_mpfr_add_q},
    {"sub", l_mpfr_sub},
    {"ui_sub", l_mpfr_ui_sub},
    {"sub_ui", l_mpfr_sub_ui},
    {"si_sub", l_mpfr_si_sub},
    {"sub_si", l_mpfr_sub_si},
    {"d_sub", l_mpfr_d_sub},
    {"sub_d", l_mpfr_sub_d},
    {"z_sub", l_mpfr_z_sub},
    {"sub_z", l_mpfr_sub_z},
    {"sub_q", l_mpfr_sub_q},
    {"mul", l_mpfr_mul},
    {"mul_ui", l_mpfr_mul_ui},
    {"mul_si", l_mpfr_mul_si},
    {"mul_d", l_mpfr_mul_d},
    {"mul_z", l_mpfr_mul_z},
    {"mul_q", l_mpfr_mul_q},
    {"sqr", l_mpfr_sqr},
    {"div", l_mpfr_div},
    {"ui_div", l_mpfr_ui_div},
    {"div_ui", l_mpfr_div_ui},
    {"si_div", l_mpfr_si_div},
    {"div_si", l_mpfr_div_si},
    {"div_d", l_mpfr_div_d},
    {"div_z", l_mpfr_div_z},
    {"div_q", l_mpfr_div_q},
    {"sqrt", l_mpfr_sqrt},
    {"sqrt_ui", l_mpfr_sqrt_ui},
    {"rec_sqrt", l_mpfr_rec_sqrt},
    {"cbrt", l_mpfr_cbrt},
    {"root", l_mpfr_root},
    {"pow", l_mpfr_pow},
    {"pow_ui", l_mpfr_pow_ui},
    {"pow_si", l_mpfr_pow_si},
    {"pow_z", l_mpfr_pow_z},
    {"ui_pow_ui", l_mpfr_ui_pow_ui},
    {"ui_pow", l_mpfr_ui_pow},
    {"neg", l_mpfr_neg},
    {"abs", l_mpfr_abs},
    {"dim", l_mpfr_dim},
    {"mul_2ui", l_mpfr_mul_2ui},
    {"mul_2si", l_mpfr_mul_2si},
    {"div_2ui", l_mpfr_div_2ui},
    {"div_2si", l_mpfr_div_2si},
    /* 5.6 Comparison Functions */
    {"cmp", l_mpfr_cmp},
    {"cmp_ui", l_mpfr_cmp_ui},
    {"cmp_si", l_mpfr_cmp_si},
    {"cmp_d", l_mpfr_cmp_d},
    {"cmp_ld", l_mpfr_cmp_ld},
    {"cmp_z", l_mpfr_cmp_z},
    {"cmp_q", l_mpfr_cmp_q},
    {"cmp_f", l_mpfr_cmp_f},
    {"cmp_ui_2exp", l_mpfr_cmp_ui_2exp},
    {"cmp_si_2exp", l_mpfr_cmp_si_2exp},
    {"cmpabs", l_mpfr_cmpabs},
    {"nan_p", l_mpfr_nan_p},
    {"inf_p", l_mpfr_inf_p},
    {"number_p", l_mpfr_number_p},
    {"zero_p", l_mpfr_zero_p},
    {"regular_p", l_mpfr_regular_p},
    {"sgn", l_mpfr_sgn},
    {"greater_p", l_mpfr_greater_p},
    {"greaterequal_p", l_mpfr_greaterequal_p},
    {"less_p", l_mpfr_less_p},
    {"lessequal_p", l_mpfr_lessequal_p},
    {"equal_p", l_mpfr_equal_p},
    {"lessgreater_p", l_mpfr_lessgreater_p},
    {"unordered_p", l_mpfr_unordered_p},
    /* 5.7 Special Functions */
    {"log", l_mpfr_log},
    {"log2", l_mpfr_log2},
    {"log10", l_mpfr_log10},
    {"exp", l_mpfr_exp},
    {"exp2", l_mpfr_exp2},
    {"exp10", l_mpfr_exp10},
    {"cos", l_mpfr_cos},
    {"sin", l_mpfr_sin},
    {"tan", l_mpfr_tan},
    /* {"sin_cos", l_mpfr_sin_cos}, */
    {"sec", l_mpfr_sec},
    {"csc", l_mpfr_csc},
    {"cot", l_mpfr_cot},
    {"acos", l_mpfr_acos},
    {"asin", l_mpfr_asin},
    {"atan", l_mpfr_atan},
    {"atan2", l_mpfr_atan2},
    {"cosh", l_mpfr_cosh},
    {"sinh", l_mpfr_sinh},
    {"tanh", l_mpfr_tanh},
    /* {"sinh_cosh", l_mpfr_sinh_cosh}, */
    {"sech", l_mpfr_sech},
    {"csch", l_mpfr_csch},
    {"coth", l_mpfr_coth},
    {"acosh", l_mpfr_acosh},
    {"asinh", l_mpfr_asinh},
    {"atanh", l_mpfr_atanh},
    {"fac_ui", l_mpfr_fac_ui},
    {"log1p", l_mpfr_log1p},
    {"expm1", l_mpfr_expm1},
    {"eint", l_mpfr_eint},
    {"li2", l_mpfr_li2},
    {"gamma", l_mpfr_gamma},
    {"lngamma", l_mpfr_lngamma},
    {"lgamma", l_mpfr_lgamma},
    {"digamma", l_mpfr_digamma},
    {"zeta", l_mpfr_zeta},
    {"zeta_ui", l_mpfr_zeta_ui},
    {"erf", l_mpfr_erf},
    {"erfc", l_mpfr_erfc},
    {"j0", l_mpfr_j0},
    {"j1", l_mpfr_j1},
    {"jn", l_mpfr_jn},
    {"y0", l_mpfr_y0},
    {"y1", l_mpfr_y1},
    {"yn", l_mpfr_yn},
    {"fma", l_mpfr_fma},
    {"fms", l_mpfr_fms},
    {"agm", l_mpfr_agm},
    {"hypot", l_mpfr_hypot},
    {"ai", l_mpfr_ai},
    {"const_log2", l_mpfr_const_log2},
    {"const_pi", l_mpfr_const_pi},
    {"const_euler", l_mpfr_const_euler},
    {"const_catalan", l_mpfr_const_catalan},
    /* {"free_cache", l_mpfr_free_cache}, */
    {"sum", l_mpfr_sum},
    /* 5.8 Input and Output Functions */
    /* 5.9 Formatted Output Functions */
    {"printf", l_mpfr_printf},
    {"snprintf", l_mpfr_snprintf},
    /* 5.10 Integer and Remainder Related Functions */
    {"rint", l_mpfr_rint},
    {"ceil", l_mpfr_ceil},
    {"floor", l_mpfr_floor},
    {"round", l_mpfr_round},
    {"trunc", l_mpfr_trunc},
    {"rint_ceil", l_mpfr_rint_ceil},
    {"rint_floor", l_mpfr_rint_floor},
    {"rint_round", l_mpfr_rint_round},
    {"rint_trunc", l_mpfr_rint_trunc},
    {"frac", l_mpfr_frac},
    {"modf", l_mpfr_modf},
    {"fmod", l_mpfr_fmod},
    {"remainder", l_mpfr_remainder},
    {"remquo", l_mpfr_remquo},
    {"integer_p", l_mpfr_integer_p},
    /* 5.11 Rounding Related Functions */
    /* 5.12 Miscellaneous Functions */
    /* 5.13 Exception Related Functions */
    /* 5.14 Compatibility With MPF */
    /* 5.15 Custom Interface */
    {"__add", m__add},
    {"__sub", m__sub},
    {"__mul", m__mul},
    {"__div", m__div},
    {"__pow", m__pow},
    {"__unm", m__unm},
    {"__eq", m__eq},
    {"__lt", m__lt},
    {"__le", m__le},
    {"__gc", m__gc},
    {NULL, NULL}                /* sentinel */
};

/**********************************************************************/

int luaopen_lmpfrlib(lua_State * L)
{
    luaL_newmetatable(L, M_mpfr_t);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    lua_pushstring(L, "no user access");
    lua_setfield(L, -2, "__metatable");
    luaL_setfuncs(L, libmpfr_t_m, 0);

    luaL_newlib(L, libmpfr_t_f);
    return 1;
}
