int nbGenerateIsotropic(lua_State* luaSt)
{
    static dsfmt_t* prng;
    static const mwvector* position = NULL;
    static const mwvector* velocity = NULL;
    static mwbool ignore;
    static real mass1 = 0.0, nbodyf = 0.0, radiusScale1 = 0.0;
    static real mass2 = 0.0, radiusScale2 = 0.0;

    static const MWNamedArg argTable[] =
        {
	  { "nbody",        LUA_TNUMBER,   NULL,          TRUE,  &nbodyf      },
	  { "mass1",        LUA_TNUMBER,   NULL,          TRUE,  &mass1       },
          { "mass2",        LUA_TNUMBER,   NULL,          TRUE,  &mass2       },
	  { "scaleRadius1", LUA_TNUMBER,   NULL,          TRUE,  &radiusScale1},
	  { "scaleRadius2", LUA_TNUMBER,   NULL,          TRUE,  &radiusScale2},
	  { "position",     LUA_TUSERDATA, MWVECTOR_TYPE, TRUE,  &position    },
	  { "velocity",     LUA_TUSERDATA, MWVECTOR_TYPE, TRUE,  &velocity    },
	  { "ignore",       LUA_TBOOLEAN,  NULL,          FALSE, &ignore      },
	  { "prng",         LUA_TUSERDATA, DSFMT_TYPE,    TRUE,  &prng        },
	  END_MW_NAMED_ARG
        };

    if (lua_gettop(luaSt) != 1)
        return luaL_argerror(luaSt, 1, "Expected 1 arguments");

    handleNamedArgumentTable(luaSt, argTable, 1);

    return nbGenerateIsotropicCore(luaSt, prng, (unsigned int) nbodyf, mass1, mass2, ignore,
                                 *position, *velocity, radiusScale1, radiusScale2);
}
static int evaluateBackground(lua_State* luaSt)
{
    int table;
    static BackgroundParameters bg = EMPTY_BACKGROUND_PARAMETERS;
    static const MWNamedArg bgArgTable[] =
        {
            { "alpha",   LUA_TNUMBER, NULL, FALSE, &bg.alpha   },
            { "r0",      LUA_TNUMBER, NULL, TRUE,  &bg.r0      },
            { "q",       LUA_TNUMBER, NULL, TRUE,  &bg.q       },
            { "delta",   LUA_TNUMBER, NULL, FALSE, &bg.delta   },

            { "epsilon", LUA_TNUMBER, NULL, FALSE, &bg.epsilon },

            { "a",       LUA_TNUMBER, NULL, FALSE, &bg.a       },
            { "b",       LUA_TNUMBER, NULL, FALSE, &bg.b       },
            { "c",       LUA_TNUMBER, NULL, FALSE, &bg.c       },
            END_MW_NAMED_ARG
        };

    lua_getglobal(luaSt, BACKGROUND_NAME);
    table = lua_gettop(luaSt);
    mw_lua_checktable(luaSt, table);

    bg = defaultBG;
    handleNamedArgumentTable(luaSt, bgArgTable, table);
    *_bg = bg;

    return 0;
}
static int readIntegralArea(lua_State* luaSt, IntegralArea* iaOut, int table)
{
    static IntegralArea ia;
    static real nuStepsf, muStepsf, rStepsf;
    static const MWNamedArg iaArgTable[] =
        {
            { "nu_min",   LUA_TNUMBER, NULL, TRUE, &ia.nu_min },
            { "nu_max",   LUA_TNUMBER, NULL, TRUE, &ia.nu_max },
            { "nu_steps", LUA_TNUMBER, NULL, TRUE, &nuStepsf  },

            { "mu_min",   LUA_TNUMBER, NULL, TRUE, &ia.mu_min },
            { "mu_max",   LUA_TNUMBER, NULL, TRUE, &ia.mu_max },
            { "mu_steps", LUA_TNUMBER, NULL, TRUE, &muStepsf  },

            { "r_min",    LUA_TNUMBER, NULL, TRUE, &ia.r_min  },
            { "r_max",    LUA_TNUMBER, NULL, TRUE, &ia.r_max  },
            { "r_steps",  LUA_TNUMBER, NULL, TRUE, &rStepsf   },
            END_MW_NAMED_ARG
        };

    handleNamedArgumentTable(luaSt, iaArgTable, table);

    ia.nu_steps = (unsigned int) nuStepsf;
    ia.mu_steps = (unsigned int) muStepsf;
    ia.r_steps = (unsigned int) rStepsf;
    calcIntegralStepSizes(&ia);

    *iaOut = ia;

    return 0;
}
int oneTableArgument(lua_State* luaSt, const MWNamedArg* argTable)
{
    if (lua_gettop(luaSt) != 1)
        return luaL_argerror(luaSt, 1, "Expected 1 table argument");

    handleNamedArgumentTable(luaSt, argTable, 1);
    return 0;
}
static int luaPrintReverseOrbit(lua_State* luaSt)
{
    mwvector finalPos, finalVel;
    static real dt = 0.0;
    static real tstop = 0.0;
    static real tstopf = 0.0;
    static Potential* pot = NULL;
    static const mwvector* pos = NULL;
    static const mwvector* vel = NULL;
    //static mwbool SecondDisk = FALSE;

    static const MWNamedArg argTable[] =
        {
            { "potential",  LUA_TUSERDATA, POTENTIAL_TYPE, TRUE, &pot           },
            { "position",   LUA_TUSERDATA, MWVECTOR_TYPE,  TRUE, &pos           },
            { "velocity",   LUA_TUSERDATA, MWVECTOR_TYPE,  TRUE, &vel           },
            { "tstop",      LUA_TNUMBER,   NULL,           TRUE, &tstop         },
            { "tstopf",     LUA_TNUMBER,   NULL,           TRUE, &tstopf        },
            { "dt",         LUA_TNUMBER,   NULL,           TRUE, &dt            },
            END_MW_NAMED_ARG
        };

    switch (lua_gettop(luaSt))
    {
        case 1:
            handleNamedArgumentTable(luaSt, argTable, 1);
            break;

        case 6:
            pot = checkPotential(luaSt, 1);
            pos = checkVector(luaSt, 2);
            vel = checkVector(luaSt, 3);
            tstop = luaL_checknumber(luaSt, 4);
            tstopf = luaL_checknumber(luaSt, 5);
            dt = luaL_checknumber(luaSt, 6);
            break;

        default:
            return luaL_argerror(luaSt, 1, "Expected 1 or 6 arguments");
    }

    /* Make sure precalculated constants ready for use */
    if (checkPotentialConstants(pot))
        luaL_error(luaSt, "Error with potential");

    nbPrintReverseOrbit(&finalPos, &finalVel, pot, *pos, *vel, tstop, tstopf, dt);
    pushVector(luaSt, finalPos);
    pushVector(luaSt, finalVel);

    return 2;
}
static int readIntegralArea(lua_State* luaSt, IntegralArea* iaOut, int table)
{
    uint64_t r, mu, nu;
    static IntegralArea ia;
    static real nuStepsf, muStepsf, rStepsf;
    static const MWNamedArg iaArgTable[] =
        {
            { "nu_min",   LUA_TNUMBER, NULL, TRUE, &ia.nu_min },
            { "nu_max",   LUA_TNUMBER, NULL, TRUE, &ia.nu_max },
            { "nu_steps", LUA_TNUMBER, NULL, TRUE, &nuStepsf  },

            { "mu_min",   LUA_TNUMBER, NULL, TRUE, &ia.mu_min },
            { "mu_max",   LUA_TNUMBER, NULL, TRUE, &ia.mu_max },
            { "mu_steps", LUA_TNUMBER, NULL, TRUE, &muStepsf  },

            { "r_min",    LUA_TNUMBER, NULL, TRUE, &ia.r_min  },
            { "r_max",    LUA_TNUMBER, NULL, TRUE, &ia.r_max  },
            { "r_steps",  LUA_TNUMBER, NULL, TRUE, &rStepsf   },
            END_MW_NAMED_ARG
        };

    handleNamedArgumentTable(luaSt, iaArgTable, table);

    ia.nu_steps = (unsigned int) nuStepsf;
    ia.mu_steps = (unsigned int) muStepsf;
    ia.r_steps = (unsigned int) rStepsf;

    r = (uint64_t) ia.r_steps;
    mu = (uint64_t) ia.mu_steps;
    nu = (uint64_t) ia.nu_steps;

    if (nu == 0 || mu == 0 || r == 0)
    {
        mw_printf("Integral size { %u, %u, %u } cannot be 0\n", nu, mu, r);
        return 1;
    }

    if ((r > UINT64_MAX / mu) || ((r * mu) > UINT64_MAX / nu))
    {
        mw_printf("Integral size { %u, %u, %u } will overflow progress calculation\n",
                  ia.nu_steps, ia.mu_steps, ia.r_steps);
        return 1;
    }

    calcIntegralStepSizes(&ia);

    *iaOut = ia;

    return 0;
}
static int luaPlummerTimestepIntegral(lua_State* luaSt)
{
    int nArgs;
    static real smalla = 0.0, biga = 0.0, Md = 0.0, encMass = 0.0;
    static real step = 0.0;

    static const MWNamedArg argTable[] =
        {
            { "smalla", LUA_TNUMBER, NULL, TRUE,  &smalla },
            { "biga",   LUA_TNUMBER, NULL, TRUE,  &biga   },
            { "Md",     LUA_TNUMBER, NULL, TRUE,  &Md     },
            { "step",   LUA_TNUMBER, NULL, FALSE, &step   },
            END_MW_NAMED_ARG
        };

    step = 1.0e-5;  /* Reset default step */
    nArgs = lua_gettop(luaSt);
    if (nArgs == 1 && lua_istable(luaSt, 1))
    {
        handleNamedArgumentTable(luaSt, argTable, 1);
    }
    else if (nArgs == 3 || nArgs == 4)
    {
        smalla = luaL_checknumber(luaSt, 1);
        biga = luaL_checknumber(luaSt, 2);
        Md = luaL_checknumber(luaSt, 3);
        step = luaL_optnumber(luaSt, 4, step);
    }
    else
    {
        return luaL_argerror(luaSt, 1, "Expected 1, 3 or 4 arguments");
    }

    /* Make sure the bounds / step are OK so that this integral will be sure to complete */
    if (mwCheckNormalPosNum(smalla))
        return luaL_argerror(luaSt, 1, "Invalid small radius");
    if (mwCheckNormalPosNum(biga))
        return luaL_argerror(luaSt, 2, "Invalid big radius");
    if (mwCheckNormalPosNumEps(step))
        return luaL_argerror(luaSt, 4, "Invalid step argument");

    encMass = plummerTimestepIntegral(smalla, biga, Md, step);
    lua_pushnumber(luaSt, encMass);

    return 1;
}
static int readStreamTable(lua_State* luaSt, StreamParameters* spOut, int table)
{
    static StreamParameters sp;
    static const MWNamedArg streamArgTable[] =
        {
            { "epsilon", LUA_TNUMBER, NULL, TRUE, &sp.epsilon },
            { "mu",      LUA_TNUMBER, NULL, TRUE, &sp.mu      },
            { "r",       LUA_TNUMBER, NULL, TRUE, &sp.r       },
            { "theta",   LUA_TNUMBER, NULL, TRUE, &sp.theta   },
            { "phi",     LUA_TNUMBER, NULL, TRUE, &sp.phi     },
            { "sigma",   LUA_TNUMBER, NULL, TRUE, &sp.sigma   },
            END_MW_NAMED_ARG
        };

    handleNamedArgumentTable(luaSt, streamArgTable, table);
    *spOut = sp;

    return 0;
}
static int createNBodyCtx(lua_State* luaSt)
{
    static NBodyCtx ctx;
    static const char* criterionName = NULL;
    double nStepf = 0.0;

    static const MWNamedArg argTable[] =
        {
            { "timestep",    LUA_TNUMBER,  NULL, TRUE,  &ctx.timestep    },
            { "timeEvolve",  LUA_TNUMBER,  NULL, TRUE,  &ctx.timeEvolve  },
            { "theta",       LUA_TNUMBER,  NULL, FALSE, &ctx.theta       },
            { "eps2",        LUA_TNUMBER,  NULL, TRUE,  &ctx.eps2        },
            { "treeRSize",   LUA_TNUMBER,  NULL, FALSE, &ctx.treeRSize   },
            { "sunGCDist",   LUA_TNUMBER,  NULL, FALSE, &ctx.sunGCDist   },
            { "criterion",   LUA_TSTRING,  NULL, FALSE, &criterionName   },
            { "useQuad",     LUA_TBOOLEAN, NULL, FALSE, &ctx.useQuad     },
            { "allowIncest", LUA_TBOOLEAN, NULL, FALSE, &ctx.allowIncest },
            { "quietErrors", LUA_TBOOLEAN, NULL, FALSE, &ctx.quietErrors },
            END_MW_NAMED_ARG
        };

    criterionName = NULL;
    ctx = defaultNBodyCtx;

    if (lua_gettop(luaSt) != 1)
        return luaL_argerror(luaSt, 1, "Expected named argument table");

    handleNamedArgumentTable(luaSt, argTable, 1);

    /* FIXME: Hacky handling of enum. Will result in not good error
     * messages as well as not fitting in. */
    if (criterionName) /* Not required */
    {
        ctx.criterion = readCriterion(luaSt, criterionName);
    }

    if ((ctx.criterion != Exact) && (ctx.theta < 0.0))
    {
        return luaL_argerror(luaSt, 1, "Theta argument required for criterion != 'Exact'");
    }
    else if (ctx.criterion == Exact)
    {
        /* These don't mean anything here */
        ctx.theta = 0.0;
        ctx.useQuad = FALSE;
    }

    nStepf = mw_ceil(ctx.timeEvolve / ctx.timestep);
    if (nStepf >= (double) UINT_MAX)
    {
        luaL_error(luaSt,
                   "Number of timesteps exceeds UINT_MAX: %f timesteps (%f / %f)\n",
                   nStepf,
                   ctx.timeEvolve, ctx.timestep);
    }

    ctx.nStep = (unsigned int) nStepf;

    pushNBodyCtx(luaSt, &ctx);
    return 1;
}