/* ldecNumber.c * Lua wrapper for decNumber * created September 3, 2006 by e * * Copyright (c) 2006 Doug Currie, Londonderry, NH * All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ************************************************************************/ #include "lua.h" #include "lauxlib.h" #ifndef DECNUMDIGITS #define DECNUMDIGITS 69 #endif #include "decimal128.h" #include "version.h" // #define LDN_SEAL_TABLES // to make tables and metatables read only // testing // #define LDN_CACHE_TEST 1 #define LDN_ENABLE_CACHE 1 #ifndef LDN_CONTEXT_DEFAULT #define LDN_CONTEXT_DEFAULT DEC_INIT_DECIMAL128 #endif #define DN_NAME "decNumber" #define DN_CONTEXT_META " decNumber_CoNTeXT_MeTA" #define DN_DNUMBER_META " decNumber_NuMBeR_MeTA" #define DN_VERSION "Lua " DECFULLNAME " version " SVN_REVS " for " LUA_VERSION " with " DECVERSION const char *dn_context_meta = DN_CONTEXT_META; const char *dn_dnumber_meta = DN_DNUMBER_META; /* ***************** context support functions ***************** */ /* There is one decNumber decContext per Lua thread. This library stores these decContexts as full userdata instances in the LUA_ENVIRONINDEX; the keys of this table are the thread addresses of the thread owning the decContext. Because there is the overhead of a table lookup to get this decContext for every decNumber operation, this library caches the most recently used decContext. It is expected that the cache hit ratio will be large; it will be 100% for a single threaded application, and should help multi-threaded applications considerably. The cache depends on userdata not being moved by the garbage collector... */ /* From: Roberto Ierusalimschy To: Lua list Date: Tuesday, April 18, 2006, 9:04:52 AM Subject: userdata and the gc ===8<==============Original message text=============== > >The current garbage collector is non-compacting, so no GCed objects > >ever move around. IIRC, the authors have cautioned that this may not > >always be the case, though a lot of current code I've seen relies on > >unmoving userdata. The caution is about strings, not about userdata (although we actually did not say that explicitly in the manual). We have no intention of allowing userdata addresses to change during GC. Unlike strings, which are an internal data in Lua, the only purpose of userdata is to be used by C code, which prefer that things stay where they are :) > If that is the case, I'm confused about how the gc can shrink the pool > without invalidating userdata. Using sockets for userdata : The gc can shrink the pool as much as malloc/free can. In fact, Lua has no notion of "pool". It only manipulates memory through malloc/free/realloc. -- Roberto ===8<===========End of original message text=========== */ /* decContext construction and type checking */ static decContext *ldn_check_context (lua_State *L, int index) { decContext *dc = (decContext *)luaL_checkudata (L, index, dn_context_meta); if (dc == NULL) luaL_argerror (L, index, "decNumber bad context"); return dc; /* leaves context on Lua stack */ } static decContext *ldn_make_context (lua_State *L) { decContext *dc = (decContext *)lua_newuserdata(L, sizeof(decContext)); luaL_getmetatable (L, dn_context_meta); lua_setmetatable (L, -2); /* set metatable */ return dc; /* leaves context on Lua stack */ } /* decContext cache */ #if LDN_ENABLE_CACHE static lua_State *L_of_context_cache; static decContext *context_cache; #if LDN_CACHE_TEST static lua_Number hits; static lua_Number misses; #endif #endif /* decContext per thread storage */ /* the value on stack at index must be the decContext userdata corresponding to dc ldn_set_context must have no stack effect */ static void ldn_set_context (lua_State *L, int index, decContext *dc) { /* make value at index the context for this thread */ if (index < 0) index -= 1; lua_pushthread (L); /* key */ lua_pushvalue (L, index); /* value */ lua_rawset (L, LUA_ENVIRONINDEX); #if LDN_ENABLE_CACHE /* and cache */ L_of_context_cache = L; context_cache = dc; #endif } /* either * a) we need a decContext on the Lua stack, so we must bypass the cache, or * b) we have a cache miss */ static decContext *ldn_push_context (lua_State *L) { decContext *dc; lua_pushthread (L); /* key */ lua_rawget (L, LUA_ENVIRONINDEX); if (lua_isnil (L, -1) ) { /* nothing in the thread local state, so make a new context */ lua_pop (L, 1); dc = ldn_make_context (L); dc = decContextDefault (dc, LDN_CONTEXT_DEFAULT); /* make it the context for this thread */ ldn_set_context (L, -1, dc); } else { dc = ldn_check_context (L, -1); #if LDN_ENABLE_CACHE /* and cache */ L_of_context_cache = L; context_cache = dc; #endif } return dc; /* leaves context on Lua stack */ } static decContext *ldn_get_context (lua_State *L) { decContext *dc; #if LDN_ENABLE_CACHE /* try the cache first */ if (L_of_context_cache == L) { dc = context_cache; #if LDN_CACHE_TEST hits += (lua_Number )1; #endif } else #endif { /* go to the per thread storage next */ dc = ldn_push_context (L); lua_pop (L, 1); #if LDN_CACHE_TEST misses += (lua_Number )1; #endif } return dc; } #if LDN_CACHE_TEST static int dn_cache_stats (lua_State *L) { lua_pushnumber (L, hits); lua_pushnumber (L, misses); return 2; } #endif /* **************** stack/arg support functions ***************** */ static decNumber *ldn_make_decNumber (lua_State *L) { decNumber *dn = (decNumber *)lua_newuserdata(L, sizeof(decNumber)); luaL_getmetatable (L, dn_dnumber_meta); lua_setmetatable (L, -2); /* set metatable */ return dn; /* leaves decNumber on Lua stack */ } static decNumber *ldn_get (lua_State *L, decContext *dc, int index) { switch (lua_type(L,index)) { case LUA_TUSERDATA: { decNumber *dn = luaL_checkudata (L, index, dn_dnumber_meta); if (dn != NULL) return dn; else break; } case LUA_TNUMBER: case LUA_TSTRING: { decNumber *dn = ldn_make_decNumber (L); const char *s = lua_tostring (L, index); decNumberFromString (dn, s, dc); lua_replace (L, index); return dn; } } luaL_typerror (L, index, dn_dnumber_meta); return NULL; } /* ******************** decNumber functions ********************* */ static int dn_get_context (lua_State *L) { ldn_push_context (L); return 1; } static int dn_set_context (lua_State *L) { decContext *dc = ldn_check_context (L, 1); ldn_push_context (L); /* return previous context */ ldn_set_context (L, 1, dc); return 1; } static int dn_todecnumber (lua_State *L) { decContext *dc = ldn_get_context (L); ldn_get (L, dc, 1); lua_pushvalue (L, 1); return 1; } /* ***************** decNumber methods ******************** */ #define DN_OP1(name,fun) \ static int name (lua_State *L) \ { \ decContext *dc = ldn_get_context (L); \ decNumber *dn1 = ldn_get (L, dc, 1); \ decNumber *dnr = ldn_make_decNumber (L); \ fun(dnr, dn1, dc); \ return 1; \ } DN_OP1(dn_exp, decNumberExp) DN_OP1(dn_ln, decNumberLn) DN_OP1(dn_log10, decNumberLog10) DN_OP1(dn_abs, decNumberAbs) DN_OP1(dn_neg, decNumberMinus) DN_OP1(dn_norm, decNumberNormalize) DN_OP1(dn_plus, decNumberPlus) DN_OP1(dn_sqrt, decNumberSquareRoot) DN_OP1(dn_intval, decNumberToIntegralValue) #define DN_OP2(name,fun) \ static int name (lua_State *L) \ { \ decContext *dc = ldn_get_context (L); \ decNumber *dn1 = ldn_get (L, dc, 1); \ decNumber *dn2 = ldn_get (L, dc, 2); \ decNumber *dnr = ldn_make_decNumber (L); \ fun(dnr, dn1, dn2, dc); \ return 1; \ } DN_OP2(dn_add, decNumberAdd) DN_OP2(dn_div, decNumberDivide) DN_OP2(dn_mul, decNumberMultiply) DN_OP2(dn_pow, decNumberPower) DN_OP2(dn_sub, decNumberSubtract) DN_OP2(dn_compare, decNumberCompare) DN_OP2(dn_comparetotal, decNumberCompareTotal) DN_OP2(dn_divideinteger, decNumberDivideInteger) DN_OP2(dn_max, decNumberMax) DN_OP2(dn_min, decNumberMin) DN_OP2(dn_quantize, decNumberQuantize) DN_OP2(dn_remainder, decNumberRemainder) DN_OP2(dn_remaindernear, decNumberRemainderNear) DN_OP2(dn_rescale, decNumberRescale) /* mod -- needs to be fudged from remainder */ static int dn_mod (lua_State *L) { decContext *dc = ldn_get_context (L); decNumber *dn1 = ldn_get (L, dc, 1); decNumber *dn2 = ldn_get (L, dc, 2); decNumber *dnr = ldn_make_decNumber (L); decNumberRemainder (dnr, dn1, dn2, dc); if (decNumberIsNegative(dn1) != decNumberIsNegative(dn2) && !decNumberIsZero(dnr)) { // convert remainder to modulo for mismatched signs decNumberAdd (dnr, dnr, dn2, dc); } return 1; } /* samequantum -- call needs no context */ static int dn_samequantum (lua_State *L) { decContext *dc = ldn_get_context (L); decNumber *dn1 = ldn_get (L, dc, 1); decNumber *dn2 = ldn_get (L, dc, 2); decNumber *dnr = ldn_make_decNumber (L); decNumberSameQuantum(dnr, dn1, dn2); return 1; } /* trim -- needs decNumberCopy */ static int dn_trim (lua_State *L) { decContext *dc = ldn_get_context (L); decNumber *dn1 = ldn_get (L, dc, 1); decNumber *dnr = ldn_make_decNumber (L); decNumberCopy (dnr, dn1); decNumberTrim (dnr); return 1; } /* predicates */ #define DN_P1(name,pmac) \ static int name (lua_State *L) \ { \ decContext *dc = ldn_get_context (L); \ decNumber *dn1 = ldn_get (L, dc, 1); \ lua_pushboolean (L, pmac(dn1)); \ return 1; \ } DN_P1(dn_iszero, decNumberIsZero) DN_P1(dn_isneg, decNumberIsNegative) DN_P1(dn_isnan, decNumberIsNaN) DN_P1(dn_isqnan, decNumberIsQNaN) DN_P1(dn_issnan, decNumberIsSNaN) DN_P1(dn_isinf, decNumberIsInfinite) #define DN_PR2(name,fun,pmac) \ static int name (lua_State *L) \ { \ decNumber dnr; \ decContext *dc = ldn_get_context (L); \ decNumber *dn1 = ldn_get (L, dc, 1); \ decNumber *dn2 = ldn_get (L, dc, 2); \ fun(&dnr, dn1, dn2, dc); \ lua_pushboolean (L, pmac(&dnr)); \ return 1; \ } #define decNumberIsNegativeOrZero(d) (decNumberIsNegative(d) || decNumberIsZero(d)) DN_PR2(dn_eq,decNumberCompare,decNumberIsZero) DN_PR2(dn_lt,decNumberCompare,decNumberIsNegative) DN_PR2(dn_le,decNumberCompare,decNumberIsNegativeOrZero) /* to string */ static int ldn_string (lua_State *L, char *(*sf)(const decNumber *, char *)) { char buf[128]; decContext *dc = ldn_get_context (L); decNumber *dn = ldn_get (L, dc, 1); if ((dn->digits + 15) <= 128) { lua_pushstring (L, sf(dn, buf)); } else { void *ud; lua_Alloc f = lua_getallocf (L, &ud); char *dbuf = (*f) (ud, NULL, 0, dn->digits + 15); // malloc if (dbuf == NULL) luaL_error (L, "decNumber tostring cannot malloc"); lua_pushstring (L, sf(dn, dbuf)); (*f) (ud, dbuf, dn->digits + 15, 0); // free } return 1; } static int dn_string (lua_State *L) { return ldn_string (L, decNumberToString); } static int dn_engstring (lua_State *L) { return ldn_string (L, decNumberToEngString); } /* *********************** context methods ************************** */ /* Context must always be set correctly: */ /* */ /* digits -- must be in the range 1 through 999999999 */ /* emax -- must be in the range 0 through 999999999 */ /* emin -- must be in the range 0 through -999999999 */ /* round -- must be one of the enumerated rounding modes */ /* traps -- only defined bits may be set */ /* status -- [any bits may be cleared, but not set, by user] */ /* clamp -- must be either 0 or 1 */ /* extended -- must be either 0 or 1 [present only if DECSUBSET] */ static int dn_ctx_set_default (lua_State *L) { decContext *dc = ldn_check_context (L, 1); int32_t kind = luaL_checkint (L, 2); if ( kind == DEC_INIT_DECIMAL128 || kind == DEC_INIT_DECIMAL64 || kind == DEC_INIT_DECIMAL32 || kind == DEC_INIT_BASE ) { decContextDefault (dc, kind); if (kind == DEC_INIT_BASE) { dc->traps = 0; /* but turn off traps */ } } else { luaL_error (L, "arg out of range for decNumber default"); } return 0; } static int dn_ctx_dup (lua_State *L) \ { decContext *dc = ldn_check_context (L, 1); decContext *dc_dup = ldn_make_context (L); /* new ctx on top of stack now */ *dc_dup = *dc; /* copy context's fields */ return 1; } #define LDN_CTX_GETTER(field) \ static int dn_ctx_get_ ## field (lua_State *L) \ { \ decContext *dc = ldn_check_context (L, 1); \ lua_pushinteger (L, dc->field); \ return 1; \ } LDN_CTX_GETTER(digits) LDN_CTX_GETTER(emax) LDN_CTX_GETTER(emin) LDN_CTX_GETTER(round) LDN_CTX_GETTER(traps) LDN_CTX_GETTER(status) LDN_CTX_GETTER(clamp) #if DECSUBSET LDN_CTX_GETTER(extended) #endif #define LDN_CTX_SETTER(field,min,max) \ static int dn_ctx_set_ ## field (lua_State *L) \ { \ decContext *dc = ldn_check_context (L, 1); \ int32_t val = luaL_checkint (L, 2); \ int32_t oldval = dc->field; \ if (val < min || val > max) \ luaL_error (L, "arg out of range for decNumber context " # field); \ dc->field = val; \ lua_pushinteger (L, oldval); \ return 1; \ } #define LDN_ALL_FLAGS (DEC_Errors|DEC_Information) LDN_CTX_SETTER(digits,DEC_MIN_DIGITS,DECNUMDIGITS) /* can't hold DEC_MAX_DIGITS */ LDN_CTX_SETTER(emax,DEC_MIN_EMAX,DEC_MAX_EMAX) LDN_CTX_SETTER(emin,DEC_MIN_EMIN,DEC_MAX_EMIN) LDN_CTX_SETTER(round,0,(DEC_ROUND_MAX-1)) LDN_CTX_SETTER(traps,0,0) /* LDN_ALL_FLAGS -- this is unsafe since we have no handler */ LDN_CTX_SETTER(status,0,LDN_ALL_FLAGS) LDN_CTX_SETTER(clamp,0,1) #if DECSUBSET LDN_CTX_SETTER(extended,0,1) #endif static int dn_ctx_get_status_s (lua_State *L) { decContext *dc = ldn_check_context (L, 1); const char * s = decContextStatusToString(dc); lua_pushstring (L, s); return 1; } static int dn_ctx_set_status_s (lua_State *L) { decContext *dc = ldn_check_context (L, 1); const char *s = luaL_checkstring (L, 2); decContextSetStatusFromString (dc, s); return 0; } static int dn_ctx_tostring (lua_State *L) { decContext *dc = ldn_check_context (L, 1); const char * s = decContextStatusToString(dc); lua_pushfstring (L, "decNumber context (%p) %s", dc, s); return 1; } /* ************************ decNumber constants *********************** */ #define DEC_(s) { #s, DEC_ ## s }, static const struct { const char* name; int value; } dn_constants[] = { /* rounding */ DEC_(ROUND_CEILING) /* round towards +infinity */ DEC_(ROUND_UP) /* round away from 0 */ DEC_(ROUND_HALF_UP) /* 0.5 rounds up */ DEC_(ROUND_HALF_EVEN) /* 0.5 rounds to nearest even */ DEC_(ROUND_HALF_DOWN) /* 0.5 rounds down */ DEC_(ROUND_DOWN) /* round towards 0 (truncate) */ DEC_(ROUND_FLOOR) /* round towards -infinity */ /* Trap-enabler and Status flags */ DEC_(Conversion_syntax) DEC_(Division_by_zero) DEC_(Division_impossible) DEC_(Division_undefined) DEC_(Insufficient_storage) DEC_(Inexact) DEC_(Invalid_context) DEC_(Invalid_operation) #if DECSUBSET DEC_(Lost_digits) #endif DEC_(Overflow) DEC_(Clamped) DEC_(Rounded) DEC_(Subnormal) DEC_(Underflow) /* flag combinations */ DEC_(IEEE_854_Division_by_zero) DEC_(IEEE_854_Inexact) DEC_(IEEE_854_Invalid_operation) DEC_(IEEE_854_Overflow) DEC_(IEEE_854_Underflow) DEC_(Errors) /* flags which are normally errors (results are qNaN, infinite, or 0) */ DEC_(NaNs) /* flags which cause a result to become qNaN */ DEC_(Information) /* flags which are normally for information only (have finite results) */ /* Initialization descriptors, used by decContextDefault */ DEC_(INIT_BASE) DEC_(INIT_DECIMAL32) DEC_(INIT_DECIMAL64) DEC_(INIT_DECIMAL128) /* compile time config */ {"MAX_DIGITS", DECNUMDIGITS }, /* terminator */ { NULL, 0 } }; static const luaL_reg dn_dnumber_meta_lib[] = { {"eq", dn_eq }, {"lt", dn_lt }, {"le", dn_le }, {"exp", dn_exp }, {"ln", dn_ln }, {"log10", dn_log10 }, {"abs", dn_abs }, {"minus", dn_neg }, {"normalize", dn_norm }, {"plus", dn_plus }, {"squareroot", dn_sqrt }, {"tointegralvalue", dn_intval }, {"add", dn_add }, {"divide", dn_div }, {"multiply", dn_mul }, {"power", dn_pow }, {"subtract", dn_sub }, {"compare", dn_compare }, {"comparetotal", dn_comparetotal }, {"divideinteger", dn_divideinteger }, {"max", dn_max }, {"min", dn_min }, {"quantize", dn_quantize }, {"remainder", dn_remainder }, {"remaindernear", dn_remaindernear }, {"rescale", dn_rescale }, {"samequantum", dn_samequantum }, {"mod", dn_mod }, {"iszero", dn_iszero }, {"isnegative", dn_isneg }, {"isnan", dn_isnan }, {"isqnan", dn_isqnan }, {"issnan", dn_issnan }, {"isinfinite", dn_isinf }, {"trim", dn_trim }, {"tostring", dn_string }, {"toengstring", dn_engstring }, { "__unm", dn_neg }, { "__add", dn_add }, { "__sub", dn_sub }, { "__mul", dn_mul }, { "__div", dn_div }, { "__pow", dn_pow }, { "__mod", dn_mod }, { "__eq", dn_eq }, { "__lt", dn_lt }, { "__le", dn_le }, { "__tostring", dn_string }, { NULL, NULL } }; static const luaL_reg dn_context_meta_lib[] = { {"setdefault", dn_ctx_set_default }, {"getstatus", dn_ctx_get_status }, {"setstatus", dn_ctx_set_status }, {"getdigits", dn_ctx_get_digits }, {"setdigits", dn_ctx_set_digits }, {"getemax", dn_ctx_get_emax }, {"setemax", dn_ctx_set_emax }, {"getemin", dn_ctx_get_emin }, {"setemin", dn_ctx_set_emin }, {"getround", dn_ctx_get_round }, {"setround", dn_ctx_set_round }, {"gettraps", dn_ctx_get_traps }, {"settraps", dn_ctx_set_traps }, {"getclamp", dn_ctx_get_clamp }, {"setclamp", dn_ctx_set_clamp }, #if DECSUBSET {"getextended", dn_ctx_get_extended }, {"setextended", dn_ctx_set_extended }, #endif {"getstatusstring", dn_ctx_get_status_s }, {"setstatusstring", dn_ctx_set_status_s }, {"duplicate", dn_ctx_dup }, {"setcontext", dn_set_context }, {"__tostring", dn_ctx_tostring }, {NULL, NULL} }; static const luaL_reg dn_lib[] = { {"eq", dn_eq }, {"lt", dn_lt }, {"le", dn_le }, {"exp", dn_exp }, {"ln", dn_ln }, {"log10", dn_log10 }, {"abs", dn_abs }, {"minus", dn_neg }, {"normalize", dn_norm }, {"plus", dn_plus }, {"squareroot", dn_sqrt }, {"tointegralvalue", dn_intval }, {"add", dn_add }, {"divide", dn_div }, {"multiply", dn_mul }, {"power", dn_pow }, {"subtract", dn_sub }, {"compare", dn_compare }, {"comparetotal", dn_comparetotal }, {"divideinteger", dn_divideinteger }, {"max", dn_max }, {"min", dn_min }, {"quantize", dn_quantize }, {"remainder", dn_remainder }, {"remaindernear", dn_remaindernear }, {"rescale", dn_rescale }, {"samequantum", dn_samequantum }, {"mod", dn_mod }, {"iszero", dn_iszero }, {"isnegative", dn_isneg }, {"isnan", dn_isnan }, {"isqnan", dn_isqnan }, {"issnan", dn_issnan }, {"isinfinite", dn_isinf }, {"trim", dn_trim }, {"getcontext", dn_get_context }, {"setcontext", dn_set_context }, {"tonumber", dn_todecnumber }, {"tostring", dn_string }, {"toengstring", dn_engstring }, #if LDN_CACHE_TEST {"cachestats", dn_cache_stats }, #endif {NULL, NULL} }; LUALIB_API int luaopen_ldecNumber(lua_State *L) { /* create a shared environment for the decNumber functions */ lua_createtable (L, 0, 5); lua_replace(L, LUA_ENVIRONINDEX); /* create decNumber global table and register decNumber functions */ luaL_register (L, DN_NAME, dn_lib); { int i = 0; /* add constants to global table */ while (dn_constants[i].name) { lua_pushstring (L, dn_constants[i].name); lua_pushnumber (L, dn_constants[i].value); lua_rawset (L, -3); i += 1; } } lua_pushliteral (L,"version"); /** version */ lua_pushliteral (L, DN_VERSION); lua_settable (L, -3); /* */ lua_pushliteral ( L, "context_metatable"); /** context metatable */ luaL_newmetatable (L, dn_context_meta); /* register context metatable functions */ luaL_register(L, NULL, dn_context_meta_lib); lua_pushstring (L, "__index"); lua_pushvalue (L, -2); /* push metatable */ lua_rawset (L, -3); /* metatable.__index = metatable */ lua_settable(L,-3); /* */ lua_pushliteral ( L, "number_metatable"); /** number metatable */ luaL_newmetatable (L, dn_dnumber_meta); /* register decimal number metatable functions */ luaL_register(L, NULL, dn_dnumber_meta_lib); lua_pushstring (L, "__index"); lua_pushvalue (L, -2); /* push metatable */ lua_rawset (L, -3); /* metatable.__index = metatable */ lua_settable(L,-3); /* */ #if LDN_SEAL_TABLES /* set decNumber's metatable to itself - set as readonly (__newindex) */ lua_pushvalue(L, -1); lua_setmetatable(L, -2); #endif return 1; } /* end of ldecNumber.c */