package se.krka.kahlua.j2se; import se.krka.kahlua.vm.*; public class MathLib implements JavaFunction { private static final int ABS = 0; private static final int ACOS = 1; private static final int ASIN = 2; private static final int ATAN = 3; private static final int ATAN2 = 4; private static final int CEIL = 5; private static final int COS = 6; private static final int COSH = 7; private static final int DEG = 8; private static final int EXP = 9; private static final int FLOOR = 10; private static final int FMOD = 11; private static final int FREXP = 12; private static final int LDEXP = 13; private static final int LOG = 14; private static final int LOG10 = 15; private static final int MODF = 16; private static final int POW = 17; private static final int RAD = 18; private static final int SIN = 19; private static final int SINH = 20; private static final int SQRT = 21; private static final int TAN = 22; private static final int TANH = 23; private static final int NUM_FUNCTIONS = 24; private static final String[] names; private static final MathLib[] functions; static { names = new String[NUM_FUNCTIONS]; names[ABS] = "abs"; names[ACOS] = "acos"; names[ASIN] = "asin"; names[ATAN] = "atan"; names[ATAN2] = "atan2"; names[CEIL] = "ceil"; names[COS] = "cos"; names[COSH] = "cosh"; names[DEG] = "deg"; names[EXP] = "exp"; names[FLOOR] = "floor"; names[FMOD] = "fmod"; names[FREXP] = "frexp"; names[LDEXP] = "ldexp"; names[LOG] = "log"; names[LOG10] = "log10"; names[MODF] = "modf"; names[POW] = "pow"; names[RAD] = "rad"; names[SIN] = "sin"; names[SINH] = "sinh"; names[SQRT] = "sqrt"; names[TAN] = "tan"; names[TANH] = "tanh"; functions = new MathLib[NUM_FUNCTIONS]; for (int i = 0; i < NUM_FUNCTIONS; i++) { functions[i] = new MathLib(i); } } private final int index; private static final double LN2_INV = (1 / Math.log(2)); public MathLib(int index) { this.index = index; } public static void register(Platform platform, KahluaTable env) { KahluaTable math = platform.newTable(); env.rawset("math", math); math.rawset("pi", KahluaUtil.toDouble(Math.PI)); math.rawset("huge", KahluaUtil.toDouble(Double.POSITIVE_INFINITY)); for (int i = 0; i < NUM_FUNCTIONS; i++) { math.rawset(names[i], functions[i]); } } public String toString() { return "math." + names[index]; } public int call(LuaCallFrame callFrame, int nArguments) { switch (index) { case ABS: return abs(callFrame, nArguments); case ACOS: return acos(callFrame, nArguments); case ASIN: return asin(callFrame, nArguments); case ATAN: return atan(callFrame, nArguments); case ATAN2: return atan2(callFrame, nArguments); case CEIL: return ceil(callFrame, nArguments); case COS: return cos(callFrame, nArguments); case COSH: return cosh(callFrame, nArguments); case DEG: return deg(callFrame, nArguments); case EXP: return exp(callFrame, nArguments); case FLOOR: return floor(callFrame, nArguments); case FMOD: return fmod(callFrame, nArguments); case FREXP: return frexp(callFrame, nArguments); case LDEXP: return ldexp(callFrame, nArguments); case LOG: return log(callFrame, nArguments); case LOG10: return log10(callFrame, nArguments); case MODF: return modf(callFrame, nArguments); case POW: return pow(callFrame, nArguments); case RAD: return rad(callFrame, nArguments); case SIN: return sin(callFrame, nArguments); case SINH: return sinh(callFrame, nArguments); case SQRT: return sqrt(callFrame, nArguments); case TAN: return tan(callFrame, nArguments); case TANH: return tanh(callFrame, nArguments); default: return 0; } } // Generic math functions private static int abs(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[ABS]); callFrame.push(KahluaUtil.toDouble(Math.abs(x))); return 1; } private static int ceil(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[CEIL]); callFrame.push(KahluaUtil.toDouble(Math.ceil(x))); return 1; } private static int floor(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[FLOOR]); callFrame.push(KahluaUtil.toDouble(Math.floor(x))); return 1; } public static boolean isNegative(double vDouble) { return Double.doubleToLongBits(vDouble) < 0; } /** * Rounds towards even numbers * @param x */ public static double round(double x) { if (x < 0) { return -round(-x); } x += 0.5; double x2 = Math.floor(x); if (x2 == x) { return x2 - ((long) x2 & 1); } return x2; } private static int modf(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[MODF]); boolean negate = false; if (isNegative(x)) { negate = true; x = -x; } double intPart = Math.floor(x); double fracPart; if (Double.isInfinite(intPart)) { fracPart = 0; } else { fracPart = x - intPart; } if (negate) { intPart = -intPart; fracPart = -fracPart; } callFrame.push(KahluaUtil.toDouble(intPart), KahluaUtil.toDouble(fracPart)); return 2; } private static int fmod(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 2, "Not enough arguments"); double v1 = KahluaUtil.getDoubleArg(callFrame, 1, names[FMOD]); double v2 = KahluaUtil.getDoubleArg(callFrame, 2, names[FMOD]); double res; if (Double.isInfinite(v1) || Double.isNaN(v1)) { res = Double.NaN; } else if (Double.isInfinite(v2)) { res = v1; } else { v2 = Math.abs(v2); boolean negate = false; if (isNegative(v1)) { negate = true; v1 = -v1; } res = v1 - Math.floor(v1/v2) * v2; if (negate) { res = -res; } } callFrame.push(KahluaUtil.toDouble(res)); return 1; } // Hyperbolic functions private static int cosh(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[COSH]); callFrame.push(KahluaUtil.toDouble(Math.cosh(x))); return 1; } private static int sinh(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[SINH]); callFrame.push(KahluaUtil.toDouble(Math.sinh(x))); return 1; } private static int tanh(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[TANH]); callFrame.push(KahluaUtil.toDouble(Math.tanh(x))); return 1; } // Trig functions private static int deg(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[DEG]); callFrame.push(KahluaUtil.toDouble(Math.toDegrees(x))); return 1; } private static int rad(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[RAD]); callFrame.push(KahluaUtil.toDouble(Math.toRadians(x))); return 1; } private static int acos(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[ACOS]); callFrame.push(KahluaUtil.toDouble(Math.acos(x))); return 1; } private static int asin(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[ASIN]); callFrame.push(KahluaUtil.toDouble(Math.asin(x))); return 1; } private static int atan(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[ATAN]); callFrame.push(KahluaUtil.toDouble(Math.atan(x))); return 1; } private static int atan2(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 2, "Not enough arguments"); double y = KahluaUtil.getDoubleArg(callFrame, 1, names[ATAN2]); double x = KahluaUtil.getDoubleArg(callFrame, 2, names[ATAN2]); callFrame.push(KahluaUtil.toDouble(Math.atan2(y, x))); return 1; } private static int cos(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[COS]); callFrame.push(KahluaUtil.toDouble(Math.cos(x))); return 1; } private static int sin(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[SIN]); callFrame.push(KahluaUtil.toDouble(Math.sin(x))); return 1; } private static int tan(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[TAN]); callFrame.push(KahluaUtil.toDouble(Math.tan(x))); return 1; } // Power functions private static int sqrt(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[SQRT]); callFrame.push(KahluaUtil.toDouble(Math.sqrt(x))); return 1; } private static int exp(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[EXP]); callFrame.push(KahluaUtil.toDouble(Math.exp(x))); return 1; } private static int pow(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 2, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[POW]); double y = KahluaUtil.getDoubleArg(callFrame, 2, names[POW]); callFrame.push(KahluaUtil.toDouble(Math.pow(x, y))); return 1; } private static int log(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[LOG]); callFrame.push(KahluaUtil.toDouble(Math.log(x))); return 1; } private static int log10(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[LOG10]); callFrame.push(KahluaUtil.toDouble(Math.log10(x))); return 1; } private static int frexp(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 1, "Not enough arguments"); double x = KahluaUtil.getDoubleArg(callFrame, 1, names[FREXP]); double e, m; if (Double.isInfinite(x) || Double.isNaN(x)) { e = 0; m = x; } else { e = Math.ceil(Math.log(x) * LN2_INV); int div = 1 << ((int) e); m = x / div; } callFrame.push(KahluaUtil.toDouble(m), KahluaUtil.toDouble(e)); return 2; } private static int ldexp(LuaCallFrame callFrame, int nArguments) { KahluaUtil.luaAssert(nArguments >= 2, "Not enough arguments"); double m = KahluaUtil.getDoubleArg(callFrame, 1, names[LDEXP]); double dE = KahluaUtil.getDoubleArg(callFrame, 2, names[LDEXP]); double ret; double tmp = m + dE; if (Double.isInfinite(tmp) || Double.isNaN(tmp)) { ret = m; } else { int e = (int) dE; ret = m * (1 << e); } callFrame.push(KahluaUtil.toDouble(ret)); return 1; } }