
#include "wrap-string.hpp"

#include "table.hpp"

// A quick check to see if a table appears to be a array.
// Does not thoroughly verify the array.  Returns the size
// of the array.
static int check_array_quick(LuaCoreStack &LS, LuaSlot array, LuaSlot tmp) {
    LS.cktable(array, "array");
    int nkeys = LS.nkeys(array);
    if (nkeys > 0) {
        LS.rawget(tmp, array, nkeys);
        if (LS.isnil(tmp)) {
            luaL_error(LS.state(), "Not a valid array");
        }
        LS.rawget(tmp, array, nkeys + 1);
        if (!LS.isnil(tmp)) {
            luaL_error(LS.state(), "Not a valid array");
        }
    }
    return nkeys;
}

bool table_equal(LuaCoreStack &LS, LuaSlot t1, LuaSlot t2) {
    lua_State *L = LS.state();
    int top = lua_gettop(L);
    LS.cktable(t1, "table1");
    LS.cktable(t2, "table2");
    int nkeys1 = lua_nkeys(L, t1.index());
    int nkeys2 = lua_nkeys(L, t2.index());
    if (nkeys1 != nkeys2) return false;
    int total = 0;
    lua_pushnil(L);
    while (lua_next(L, t1.index()) != 0) {
        lua_pushvalue(L, -2);   // k v1 k
        lua_rawget(L, t2.index());  // k v1 v2
        if (!lua_rawequal(L, -1, -2)) {
            lua_settop(L, top);
            return false;
        }
        lua_pop(L, 2);
        total += 1;
    }
    assert(total == nkeys1);
    lua_settop(L, top);
    return true;
}

LuaDefine(table_equal, "table1,table2",
    "|Return true if two tables contain the same keys and values."
    "|"
    "|This works on arbitrary tables.  Metatables are ignored."
    "|") {
    LuaArg t1, t2;
    LuaRet eql;
    LuaDefStack LS(L, t1, t2, eql);
    LS.set(eql, table_equal(LS, t1, t2));
    return LS.result();
}

LuaDefine(table_isarray, "table",
    "|Return true if the table is a valid array."
    "|"
    "|A array is a table that has keys starting with 1, and no gaps."
    "|The empty table also counts as a valid array.  This function"
    "|scans the entire array to verify its validity, so it takes O(N)"
    "|time.  See also table.isarrayq"
    "|"
    "|The functions array.isarray and table.isarray are identical."
    "|") {
    LuaArg table;
    LuaRet result;
    LuaVar tmp;
    LuaDefStack LS(L, table, result, tmp);
    if (!LS.istable(table)) {
        LS.set(result, false);
        return LS.result();
    }
    int nkeys = lua_nkeys(L, table.index());
    for (int i = 1; i <= nkeys; i++) {
        LS.rawget(tmp, table, i);
        if (LS.isnil(tmp)) {
            LS.set(result, false);
            return LS.result();
        }
    }
    LS.set(result, true);
    return LS.result();
}

LuaDefine(table_isarrayq, "table",
    "|Return true if the table is probably a valid array."
    "|"
    "|A array is a table that has keys starting with 1, and no gaps."
    "|The empty table also counts as a valid array.  This function"
    "|does a constant-time heuristic check: it gets the number of keys"
    "|in the table, NKEYS.  Then it verifies that table[NKEYS] is not"
    "|nil and that table[NKEYS+1] is nil.  If these things are both"
    "|true, the table is very likely a valid array."
    "|"
    "|The functions array.isarrayq and table.isarrayq are identical."
    "|") {
    LuaArg table;
    LuaRet result;
    LuaVar tmp;
    LuaDefStack LS(L, table, result, tmp);
    if (!LS.istable(table)) {
        LS.set(result, false);
        return LS.result();
    }
    int nkeys = LS.nkeys(table);
    if (nkeys > 0) {
        LS.rawget(tmp, table, nkeys);
        if (LS.isnil(tmp)) {
            LS.set(result, false);
            return LS.result();
        }
        LS.rawget(tmp, table, nkeys + 1);
        if (!LS.isnil(tmp)) {
            LS.set(result, false);
            return LS.result();
        }
    }
    LS.set(result, true);
    return LS.result();
}

LuaDefineAlias(array_isarray, table_isarray);

LuaDefine(array_removeall, "array,value",
    "|Remove all occurrences of value from array."
    "|"
    "|For example, if you remove the number 3 from the array"
    "|{1,2,3,4,5,4,3,2,1} you get {1,2,4,5,4,2,1}."
    "|"
    "|Returns true if it removed something, false otherwise."
    "|") {
    LuaArg array, value;
    LuaRet result;
    LuaVar tmp;
    LuaDefStack LS(L, array, value, result, tmp);
    int nkeys = check_array_quick(LS, array, tmp);
    int dest = 1;
    for (int i = 1; i <= nkeys; i++) {
        LS.rawget(tmp, array, i);
        if (LS.isnil(tmp)) {
            luaL_error(L, "not a valid array");
            return LS.result();
        }
        if (!LS.rawequal(tmp, value)) {
            if (dest < i) {
                LS.rawset(array, dest, tmp);
            }
            dest += 1;
        }
    }
    LS.set(result, (dest < nkeys));
    while (dest <= nkeys) {
        LS.rawset(array, dest, LuaNil);
        dest += 1;
    }
    return LS.result();
}

LuaDefine(array_push, "array,value",
    "|Push a value onto the end of a array."
    "|"
    "|Argument must be a valid array.  Appends the value to"
    "|the end of the array."
    "|") {
    LuaArg array, value;
    LuaVar tmp;
    LuaDefStack LS(L, array, value, tmp);
    int nkeys = check_array_quick(LS, array, tmp);
    LS.rawset(array, nkeys + 1, value);
    return LS.result();
}

LuaDefine(array_pop, "array,value",
    "|Pop a value from the end of a array."
    "|"
    "|Argument must be a valid array.  Returns the last value"
    "|from the array.  If the array is empty, returns nil."
    "|") {
    LuaArg array;
    LuaRet value;
    LuaVar tmp;
    LuaDefStack LS(L, array, value, tmp);
    int nkeys = check_array_quick(LS, array, tmp);
    if (nkeys == 0) {
        LS.set(value, LuaNil);
    } else {
        LS.rawget(value, array, nkeys);
        LS.rawset(array, nkeys, LuaNil);
    }
    return LS.result();
}

LuaDefine(array_find, "array,value", 
    "|Find the first occurence of value in array."
    "|"
    "|Argument must be a valid array.  Returns the index of the"
    "|first occurrence of value in array.  If the value is not"
    "|found, returns nil."
    "|"
    "|Searching for 'nil' in a array is explicitly disallowed, since"
    "|a valid array cannot contain nil."
    "|") {
    LuaArg array, value;
    LuaRet index;
    LuaVar tmp;
    LuaDefStack LS(L, array, value, index, tmp);
    int nkeys = check_array_quick(LS, array, tmp);
    if (LS.isnil(value)) {
        luaL_error(L, "cannot search for nil in a array");
        return 0;
    }
    for (int i = 1; i <= nkeys; i++) {
        LS.rawget(tmp, array, i);
        if (LS.rawequal(tmp, value)) {
            LS.set(index, i);
            return LS.result();
        }
    }
    LS.set(index, LuaNil);
    return LS.result();
}

LuaDefine(table_empty, "table",
    "|Return true if the table is empty."
    "|"
    "|Empty means the table has no key-value pairs stored."
    "|Metatables and metavalues are ignored."
    "|") {
    luaL_checktype(L, -1, LUA_TTABLE);
    int total = lua_nkeys(L, -1);
    lua_pushboolean(L, (total == 0)?1:0);
    return 1;
}

LuaDefine(table_count, "table", 
    "|Return the number of key-value pairs in the table."
    "|"
    "|Returns the number of key-value pairs that have been"
    "|explicitly stored.  Metatables and metavalues don't count."
    "|") {
    luaL_checktype(L, -1, LUA_TTABLE);
    int total = lua_nkeys(L, -1);
    lua_pushinteger(L, total);
    return 1;
}

LuaDefine(table_clear, "table,metaflag",
    "|Clear the table key-value pairs, and optionally the metatable"
    "|"
    "|This removes all key-value pairs from the table.  If the metaflag"
    "|is set, it also removes the metatable.  If you try to clear the"
    "|metatable when the metatable is locked, this will throw an error."
    "|") {
    LuaArg tab, clearmeta;
    LuaVar metatable, metafield;
    LuaDefStack LS(L, tab, clearmeta, metatable, metafield);
    LS.cktable(tab, "table");
    if (LS.ckboolean(clearmeta)) {
        LS.getmetatable(metatable, tab);
        if (LS.istable(metatable)) {
            LS.rawget(metafield, metatable, "__metatable");
            if (!LS.isnil(metafield)) {
                luaL_error(L, "Cannot clear metatable.");
                return LS.result();
            }
        }
        LS.cleartable(tab, true);
    } else {
        LS.cleartable(tab, false);
    }
    return LS.result();
}

LuaDefine(table_getflagbits, "table", "get the table's flag bits (debugging only)") {
    LuaArg tab;
    LuaRet bits;
    LuaDefStack LS(L, tab, bits);
    uint16_t ubits = lua_getflagbits(L, tab.index());
    LS.set(bits, ubits);
    return LS.result();
}

LuaDefine(table_setflagbits, "table,bits", "set the table's flag bits (debugging only)") {
    LuaArg tab, bits;
    LuaDefStack LS(L, tab, bits);
    uint16_t ubits = LS.ckinteger(bits);
    lua_setflagbits(L, tab.index(), ubits);
    return LS.result();
}

/////////////////////////////////////////////////////////////
//
// Deque operators.
//
/////////////////////////////////////////////////////////////

#define DEQUE_LEFT 1
#define DEQUE_FILL 2
#define DEQUE_MAX 3
#define DEQUE_BASE 4

void deque_checktype(lua_State *L, int slot, int type) {
    if (lua_type(L, slot) != type) {
        luaL_error(L, "object is not a valid deque");
    }
}

void deque_get_info(lua_State *L, int deque, int *left, int *fill, int *max) {
    luaL_checktype(L, deque, LUA_TTABLE);
    if (left) {
        lua_rawgeti(L, deque, DEQUE_LEFT);
        deque_checktype(L, -1, LUA_TNUMBER);
        *left = lua_tointeger(L, -1);
        lua_pop(L, 1);
    }
    if (fill) {
        lua_rawgeti(L, deque, DEQUE_FILL);
        deque_checktype(L, -1, LUA_TNUMBER);
        *fill = lua_tointeger(L, -1);
        lua_pop(L, 1);
    }
    if (max) {
        lua_rawgeti(L, deque, DEQUE_MAX);
        deque_checktype(L, -1, LUA_TNUMBER);
        *max = lua_tointeger(L, -1);
        lua_pop(L, 1);
    }
}

void deque_put_info(lua_State *L, int deque, int *left, int *fill, int *max) {
    if (left) {
        lua_pushinteger(L, *left);
        lua_rawseti(L, deque, DEQUE_LEFT);
    }
    if (fill) {
        lua_pushinteger(L, *fill);
        lua_rawseti(L, deque, DEQUE_FILL);
    }
    if (max) {
        lua_pushinteger(L, *max);
        lua_rawseti(L, deque, DEQUE_MAX);
    }
}
    
int deque_make_room(lua_State *L, int deque, int left, int fill, int max) {
    if (fill == max) {
        for (int i = 0; i < left; i++) {
            lua_rawgeti(L, deque, DEQUE_BASE + i);
            lua_rawseti(L, deque, DEQUE_BASE + i + max);
            lua_pushinteger(L, 0);
            lua_rawseti(L, deque, DEQUE_BASE + i);
        }
        for (int i = left; i < max; i++) {
            lua_pushinteger(L, 0);
            lua_rawseti(L, deque, DEQUE_BASE + i + max);
        }
        max *= 2;
        lua_pushinteger(L, max);
        lua_rawseti(L, deque, DEQUE_MAX);
    }
    return max;
}

LuaDefine(deque_create, "", "create a deque") {
    LuaRet rdeque;
    LuaVar classobj;
    LuaDefStack LS(L, rdeque, classobj);
    const int imax = 4;
    eng::string err = LS.getclass(classobj, "deque");
    if (err != "") {
        luaL_error(L, "Class deque has been corrupted");
    }
    LS.createtable(rdeque, DEQUE_BASE + imax - 1, 0);
    LS.rawset(rdeque, DEQUE_LEFT, 0);
    LS.rawset(rdeque, DEQUE_FILL, 0);
    LS.rawset(rdeque, DEQUE_MAX, imax);
    for (int i = 0; i < imax; i++) {
        LS.rawset(rdeque, DEQUE_BASE + i, 0);
    }
    LS.setmetatable(rdeque, classobj);
    return LS.result();
}

LuaDefine(deque_pushl, "deque,value", "push onto the left end of a deque") {
    LuaArg deque, elt;
    LuaDefStack LS(L, deque, elt);
    int left, fill, max;
    deque_get_info(L, deque.index(), &left, &fill, &max);
    max = deque_make_room(L, deque.index(), left, fill, max);
    int target = (left - 1) & (max-1);
    LS.rawset(deque, DEQUE_BASE + target, elt);
    fill += 1;
    left = (left - 1) & (max - 1);
    deque_put_info(L, deque.index(), &left, &fill, NULL);
    return LS.result();
}

LuaDefine(deque_pushr, "deque,value", "push onto the right end of a deque") {
    LuaArg deque, elt;
    LuaDefStack LS(L, deque, elt);
    int left, fill, max;
    deque_get_info(L, deque.index(), &left, &fill, &max);
    max = deque_make_room(L, deque.index(), left, fill, max);
    int target = (left + fill) & (max-1);
    LS.rawset(deque, DEQUE_BASE + target, elt);
    fill += 1;
    deque_put_info(L, deque.index(), NULL, &fill, NULL);
    return LS.result();
}

LuaDefine(deque_popl, "deque", "pop the left end of a deque") {
    LuaArg deque;
    LuaRet result;
    LuaDefStack LS(L, deque, result);
    int left, fill, max;
    deque_get_info(L, deque.index(), &left, &fill, &max);
    if (fill == 0) {
        LS.set(result, LuaNil);
        return LS.result();
    }
    LS.rawget(result, deque, DEQUE_BASE + left);
    LS.rawset(deque, DEQUE_BASE + left, 0);
    left = (left + 1) & (max - 1);
    fill -= 1;
    deque_put_info(L, deque.index(), &left, &fill, NULL);
    return LS.result();
}

LuaDefine(deque_popr, "deque", "pop the right end of a deque") {
    LuaArg deque;
    LuaRet result;
    LuaDefStack LS(L, deque, result);
    int left, fill, max;
    deque_get_info(L, deque.index(), &left, &fill, &max);
    if (fill == 0) {
        LS.set(result, LuaNil);
        return LS.result();
    }
    int target = (left + fill - 1) & (max - 1);
    LS.rawget(result, deque, DEQUE_BASE + target);
    LS.rawset(deque, DEQUE_BASE + target, 0);
    fill -= 1;
    deque_put_info(L, deque.index(), NULL, &fill, NULL);
    return LS.result();
}

LuaDefine(deque_nthl, "deque,n", "return the nth item from the left end of a deque") {
    LuaArg deque, nn;
    LuaRet result;
    LuaDefStack LS(L, deque, nn, result);
    int left, fill, max;
    deque_get_info(L, deque.index(), &left, &fill, &max);
    int n = LS.ckint(nn);
    if ((n < 1) || (n > fill)) {
        LS.set(result, LuaNil);
        return LS.result();
    }
    int target = (left + n - 1) & (max - 1);
    LS.rawget(result, deque, DEQUE_BASE + target);
    return LS.result();
}

LuaDefine(deque_nthr, "deque,n", "return the nth item from the right end of a deque") {
    LuaArg deque, nn;
    LuaRet result;
    LuaDefStack LS(L, deque, nn, result);
    int left, fill, max;
    deque_get_info(L, deque.index(), &left, &fill, &max);
    int n = LS.ckint(nn);
    if ((n < 1) || (n > fill)) {
        LS.set(result, LuaNil);
        return LS.result();
    }
    int target = (left + fill - n) & (max - 1);
    LS.rawget(result, deque, DEQUE_BASE + target);
    return LS.result();
}

LuaDefine(deque_setl, "deque,n,value", "set the nth item from the left end of a deque") {
    LuaArg deque, nn, val;
    LuaDefStack LS(L, deque, nn, val);
    int left, fill, max;
    deque_get_info(L, deque.index(), &left, &fill, &max);
    int n = LS.ckint(nn);
    if ((n < 1) || (n > fill)) {
        luaL_error(L, "invalid index");
        return LS.result();
    }
    int target = (left + n - 1) & (max - 1);
    LS.rawset(deque, DEQUE_BASE + target, val);
    return LS.result();
}

LuaDefine(deque_setr, "deque,n,value", "set the nth item from the right end of a deque") {
    LuaArg deque, nn, val;
    LuaDefStack LS(L, deque, nn, val);
    int left, fill, max;
    deque_get_info(L, deque.index(), &left, &fill, &max);
    int n = LS.ckint(nn);
    if ((n < 1) || (n > fill)) {
        luaL_error(L, "invalid index");
        return LS.result();
    }
    int target = (left + fill - n) & (max - 1);
    LS.rawset(deque, DEQUE_BASE + target, val);
    return LS.result();
}

LuaDefine(deque_findl, "deque,value", "find the first occurence of value in deque, starting from left") {
    LuaArg deque, val;
    LuaRet pos;
    LuaVar check;
    LuaDefStack LS(L, deque, val, pos, check);
    int left, fill, max;
    deque_get_info(L, deque.index(), &left, &fill, &max);
    for (int i = 0; i < fill; i++) {
        int index = (left + i) & (max - 1);
        LS.rawget(check, deque, DEQUE_BASE + index);
        if (LS.rawequal(check, val)) {
            LS.set(pos, i + 1);
            return LS.result();
        }
    }
    LS.set(pos, LuaNil);
    return LS.result();
}

LuaDefine(deque_findr, "deque,value", "find the first occurrence of value in deque, starting from right") {
    LuaArg deque, val;
    LuaRet pos;
    LuaVar check;
    LuaDefStack LS(L, deque, val, pos, check);
    int left, fill, max;
    deque_get_info(L, deque.index(), &left, &fill, &max);
    int base = left + fill - 1;
    for (int i = 0; i < fill; i++) {
        int index = (base - i) & (max - 1);
        LS.rawget(check, deque, DEQUE_BASE + index);
        if (LS.rawequal(check, val)) {
            LS.set(pos, i + 1);
            return LS.result();
        }
    }
    LS.set(pos, LuaNil);
    return LS.result();
}

LuaDefine(deque_size, "deque", "return the number of items in the deque") {
    LuaArg deque;
    LuaRet size;
    LuaDefStack LS(L, deque, size);
    LS.cktable(deque, "deque");
    LS.rawget(size, deque, DEQUE_FILL);
    if (!LS.tryinteger(size)) {
        luaL_error(L, "deque has been corrupted");
    }
    return LS.result();
}


/////////////////////////////////////////////////////////////
//
// table_getpairs
//
// Given a table, return all the pairs in the table as a sequence.
// The resulting sequence contains a 1 in the first position,
// followed by the pairs, like so:
//
// sortedpairs({a=1,b=2,c=3}) => {1,"a",1,"b",2,"c",3}
// 
// Note that this function is not directly exposed to lua.
// It's exposed only through the 'sortedpairs' iterator.
//
/////////////////////////////////////////////////////////////

static void pushkey (lua_State *L, int tab, int i) {
    lua_rawgeti(L, tab, i*2);
}

static void push2keys (lua_State *L, int tab, int i, int j) {
    lua_rawgeti(L, tab, i*2);
    lua_rawgeti(L, tab, j*2);
}

static void pop2keys (lua_State *L, int tab, int i, int j) {
    lua_rawseti(L, tab, i*2);
    lua_rawseti(L, tab, j*2);
}

static void swap2values (lua_State *L, int tab, int i, int j) {
    lua_rawgeti(L, tab, i*2+1);
    lua_rawgeti(L, tab, j*2+1);
    lua_rawseti(L, tab, i*2+1);
    lua_rawseti(L, tab, j*2+1);
}

static void auxsort (lua_State *L, int tab, int l, int u) {
    while (l < u) { /* for tail recursion */
        int i, j;
        /* sort elements a[l], a[(l+u)/2] and a[u] */
        push2keys(L, tab, l, u);
        if (lua_genlt(L, -1, -2)) {
            pop2keys(L, tab, l, u);
            swap2values(L, tab, l, u);
        } else {
            lua_pop(L, 2);
        }
        if (u - l == 1) break; /* only 2 elements */
        i = (l + u) / 2;
        push2keys(L, tab, i, l);
        if (lua_genlt(L, -2, -1)) {
            pop2keys(L, tab, i, l);
            swap2values(L, tab, i, l);
        } else {
            lua_pop(L, 1); /* remove a[l] */
            pushkey(L, tab, u);
            if (lua_genlt(L, -1, -2)) {
                pop2keys(L, tab, i, u);
                swap2values(L, tab, i, u);
            } else {
                lua_pop(L, 2);
            }
        }
        if (u - l == 2) break; /* only 3 elements */
        /* put the pivot value on top of the stack and keep it there */
        pushkey(L, tab, i);
        /* move the pivot from i to u-1 */
        lua_pushvalue(L, -1);
        pushkey(L, tab, u-1);
        pop2keys(L, tab, i, u-1);
        swap2values(L, tab, i, u-1);
        /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
        i = l;
        j = u - 1;
        for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
            /* repeat ++i until a[i] >= P */
            while (pushkey(L, tab, ++i), lua_genlt(L, -1, -2)) {
                if (i >= u) luaL_error(L, "invalid order function for sorting");
                lua_pop(L, 1); /* remove a[i] */
            }
            /* repeat --j until a[j] <= P */
            while (pushkey(L, tab, --j), lua_genlt(L, -3, -1)) {
                if (j <= l) luaL_error(L, "invalid order function for sorting");
                lua_pop(L, 1); /* remove a[j] */
            }
            if (j < i) {
                lua_pop(L, 3); /* pop pivot, a[i], a[j] */
                break;
            }
            pop2keys(L, tab, i, j);
            swap2values(L, tab, i, j);
        }
        push2keys(L, tab, u-1, i);
        pop2keys(L, tab, u-1, i);
        swap2values(L, tab, u-1, i);
        /* a[l..i-1] <= a[i] == P <= a[i+1..u] */
        /* adjust so that smaller half is in [j..i] and larger one in [l..u] */
        if (i - l < u - i) {
            j = l;
            i = i - 1;
            l = i + 2;
        } else {
            j = i + 1;
            i = u;
            u = j - 2;
        }
        auxsort(L, tab, j, i); /* call recursively the smaller one */
    }                          /* repeat the routine for the larger one */
}

bool table_getpairs(LuaCoreStack &LS0, LuaSlot tab, LuaSlot pairs, bool sort) {
    lua_State *L = LS0.state();
    LuaVar key, value;
    LuaExtStack LS(L, key, value);
    bool sorted = true;
    // Create the table, store the initial 1.
    int total = lua_nkeys(L, tab.index());
    LS.createtable(pairs, total * 2 + 1, 0);
    LS.rawset(pairs, 1, 1);
    // Transfer the pairs into the sequence.
    lua_pushnil(L);
    int offset = 2;
    while (lua_next(L, tab.index()) != 0) {
        int ktype = lua_type(L, -2);
        if (ktype != LUA_TNUMBER && ktype != LUA_TSTRING && ktype != LUA_TBOOLEAN) {
            sorted = false;
        }
        lua_pushvalue(L, -2); // K V K
        lua_rawseti(L, pairs.index(), offset++);
        lua_rawseti(L, pairs.index(), offset++);
    }
    if (sort) {
        auxsort(L, pairs.index(), 1, total);
    }
    return sorted;
}

/////////////////////////////////////////////////////////////
//
// Given a sortedpairs array, return the (key, value) pairs
// one by one.  The first element of the array is used as a
// counter to keep track of our position.
//
/////////////////////////////////////////////////////////////

LuaDefine(table_nextsortedpair, "sortedpairs,dummy", "next function used by sortedpairs") {
    if (lua_gettop(L) < 2) {
        luaL_error(L, "Not enough arguments to nextpair");
    }
    luaL_checktype(L, 1, LUA_TTABLE);
    lua_rawgeti(L, 1, 1);
    lua_Integer i = luaL_checkinteger(L, -1);
    lua_pop(L, 1);
    lua_pushinteger(L, i+1);
    lua_rawseti(L, 1, 1);
    lua_rawgeti(L, 1, i*2);
    if (lua_isnil(L, -1)) {
        return 1;
    } else {
        lua_rawgeti(L, 1, i*2+1);
        return 2;
    }
}

LuaDefine(table_sortedpairs, "table",
    "|Iterate over table, sorting all keys"
    "|"
    "|Some keys can't be sorted.  For example, you can use a closure"
    "|as a table key.  If you try to iterate over a table containing"
    "|a non-sortable key, the error 'Cannot sort the table keys' will"
    "|be generated."
    "|"
    "|See doc(genlt) for information about the sort order."
    "|"
    "|") {
    LuaArg tab;
    LuaRet closure, rtab, key;
    LuaDefStack LS(L, tab, closure, rtab, key);
    bool sorted = table_getpairs(LS, tab, rtab, true);
    if (!sorted) {
        luaL_error(L, "Cannot sort the table keys");
    }
    LS.set(closure, lfn_table_nextsortedpair);
    LS.set(key, LuaNil);
    return LS.result();
}

LuaDefine(table_semisortedpairs, "table", 
    "|Iterate over table, sorting all the keys that can be sorted."
    "|"
    "|Some keys can't be sorted.  For example, you can use a closure"
    "|as a table key.  If you try to iterate over a table containing"
    "|a non-sortable key, the non-sortable elements will appear at"
    "|the end of the iteration, after all the sortable elements.  The"
    "|non-sortable elements will be in an arbitrary order."
    "|"
    "|See doc(genlt) for information about the sort order."
    "|") {
    LuaArg tab;
    LuaRet closure, rtab, key;
    LuaDefStack LS(L, tab, closure, rtab, key);
    table_getpairs(LS, tab, rtab, true);
    LS.set(closure, lfn_table_nextsortedpair);
    LS.set(key, LuaNil);
    return LS.result();
}

LuaDefine(genlt, "obj1,obj2", 
    "|Generalized less-than function"
    "|"
    "|This comparison function can compare any two objects. The"
    "|return value is as follows:"
    "|"
    "|* Numbers are compared in the obvious numeric manner."
    "|* Strings are compared alphabetically."
    "|* Booleans are compared with false being less than true."
    "|* Tokens are compared alphabetically."
    "|* Tables are all considered equal to other tables."
    "|* Functions are all considered equal to other functions."
    "|* Coroutines are all considered equal to other coroutines."
    "|"
    "|Values of different types are printed in this order:"
    "|"
    "|  Numbers, Strings, Booleans, Tokens,"
    "|  Functions, Threads, Tables, and NIL"
    "|") {
    LuaArg o1,o2;
    LuaRet lt;
    LuaDefStack LS(L, o1, o2, lt);
    int ltf = lua_genlt(L, o1.index(), o2.index());
    LS.set(lt, ltf ? true:false);
    return LS.result();
}
