/******************************************************************************* * Copyright (c) 2009 Luaj.org. 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, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * 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. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ package org.luaj.vm2.lib; import java.util.Random; import org.luaj.vm2.LuaDouble; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Varargs; /** * Subclass of {@link LibFunction} which implements the lua standard {@code math} * library. * <p> * It contains only the math library support that is possible on JME. * For a more complete implementation based on math functions specific to JSE * use {@link org.luaj.vm2.lib.jse.JseMathLib}. * In Particular the following math functions are <b>not</b> implemented by this library: * <ul> * <li>acos</li> * <li>asin</li> * <li>atan</li> * <li>cosh</li> * <li>log</li> * <li>sinh</li> * <li>tanh</li> * <li>atan2</li> * </ul> * <p> * The implementations of {@code exp()} and {@code pow()} are constructed by * hand for JME, so will be slower and less accurate than when executed on the JSE platform. * <p> * Typically, this library is included as part of a call to either * {@link JsePlatform#standardGlobals()} or {@link JmePlatform#standardGlobals()} * <pre> {@code * Globals globals = JsePlatform.standardGlobals(); * System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) ); * } </pre> * When using {@link JsePlaform} as in this example, the subclass {@link JseMathLib} will * be included, which also includes this base functionality. * <p> * To instantiate and use it directly, * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: * <pre> {@code * Globals globals = new Globals(); * globals.load(new JseBaseLib()); * globals.load(new PackageLib()); * globals.load(new MathLib()); * System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) ); * } </pre> * Doing so will ensure the library is properly initialized * and loaded into the globals table. * <p> * This has been implemented to match as closely as possible the behavior in the corresponding library in C. * @see LibFunction * @see JsePlatform * @see JmePlatform * @see JseMathLib * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.6">Lua 5.2 Math Lib Reference</a> */ public class MathLib extends TwoArgFunction { public static MathLib MATHLIB = null; public MathLib() { MATHLIB = this; } public LuaValue call(LuaValue modname, LuaValue env) { LuaTable math = new LuaTable(0,30); math.set("abs", new abs()); math.set("ceil", new ceil()); math.set("cos", new cos()); math.set("deg", new deg()); math.set("exp", new exp(this)); math.set("floor", new floor()); math.set("fmod", new fmod()); math.set("frexp", new frexp()); math.set("huge", LuaDouble.POSINF ); math.set("ldexp", new ldexp()); math.set("max", new max()); math.set("min", new min()); math.set("modf", new modf()); math.set("pi", Math.PI ); math.set("pow", new pow()); random r; math.set("random", r = new random()); math.set("randomseed", new randomseed(r)); math.set("rad", new rad()); math.set("sin", new sin()); math.set("sqrt", new sqrt()); math.set("tan", new tan()); env.set("math", math); env.get("package").get("loaded").set("math", math); return math; } abstract protected static class UnaryOp extends OneArgFunction { public LuaValue call(LuaValue arg) { return valueOf(call(arg.checkdouble())); } abstract protected double call(double d); } abstract protected static class BinaryOp extends TwoArgFunction { public LuaValue call(LuaValue x, LuaValue y) { return valueOf(call(x.checkdouble(), y.checkdouble())); } abstract protected double call(double x, double y); } static final class abs extends UnaryOp { protected double call(double d) { return Math.abs(d); } } static final class ceil extends UnaryOp { protected double call(double d) { return Math.ceil(d); } } static final class cos extends UnaryOp { protected double call(double d) { return Math.cos(d); } } static final class deg extends UnaryOp { protected double call(double d) { return Math.toDegrees(d); } } static final class floor extends UnaryOp { protected double call(double d) { return Math.floor(d); } } static final class rad extends UnaryOp { protected double call(double d) { return Math.toRadians(d); } } static final class sin extends UnaryOp { protected double call(double d) { return Math.sin(d); } } static final class sqrt extends UnaryOp { protected double call(double d) { return Math.sqrt(d); } } static final class tan extends UnaryOp { protected double call(double d) { return Math.tan(d); } } static final class exp extends UnaryOp { final MathLib mathlib; exp(MathLib mathlib) { this.mathlib = mathlib; } protected double call(double d) { return mathlib.dpow_lib(Math.E,d); } } static final class fmod extends BinaryOp { protected double call(double x, double y) { double q = x/y; return x - y * (q>=0? Math.floor(q): Math.ceil(q)); } } static final class ldexp extends BinaryOp { protected double call(double x, double y) { // This is the behavior on os-x, windows differs in rounding behavior. return x * Double.longBitsToDouble((((long) y) + 1023) << 52); } } static final class pow extends BinaryOp { protected double call(double x, double y) { return MathLib.dpow_default(x, y); } } static class frexp extends VarArgFunction { public Varargs invoke(Varargs args) { double x = args.checkdouble(1); if ( x == 0 ) return varargsOf(ZERO,ZERO); long bits = Double.doubleToLongBits( x ); double m = ((bits & (~(-1L<<52))) + (1L<<52)) * ((bits >= 0)? (.5 / (1L<<52)): (-.5 / (1L<<52))); double e = (((int) (bits >> 52)) & 0x7ff) - 1022; return varargsOf( valueOf(m), valueOf(e) ); } } static class max extends VarArgFunction { public Varargs invoke(Varargs args) { double m = args.checkdouble(1); for ( int i=2,n=args.narg(); i<=n; ++i ) m = Math.max(m,args.checkdouble(i)); return valueOf(m); } } static class min extends VarArgFunction { public Varargs invoke(Varargs args) { double m = args.checkdouble(1); for ( int i=2,n=args.narg(); i<=n; ++i ) m = Math.min(m,args.checkdouble(i)); return valueOf(m); } } static class modf extends VarArgFunction { public Varargs invoke(Varargs args) { double x = args.checkdouble(1); double intPart = ( x > 0 ) ? Math.floor( x ) : Math.ceil( x ); double fracPart = x - intPart; return varargsOf( valueOf(intPart), valueOf(fracPart) ); } } static class random extends LibFunction { Random random = new Random(); public LuaValue call() { return valueOf( random.nextDouble() ); } public LuaValue call(LuaValue a) { int m = a.checkint(); if (m<1) argerror(1, "interval is empty"); return valueOf( 1 + random.nextInt(m) ); } public LuaValue call(LuaValue a, LuaValue b) { int m = a.checkint(); int n = b.checkint(); if (n<m) argerror(2, "interval is empty"); return valueOf( m + random.nextInt(n+1-m) ); } } static class randomseed extends OneArgFunction { final random random; randomseed(random random) { this.random = random; } public LuaValue call(LuaValue arg) { long seed = arg.checklong(); random.random = new Random(seed); return NONE; } } /** compute power using installed math library, or default if there is no math library installed */ public static LuaValue dpow(double a, double b) { return LuaDouble.valueOf( MATHLIB!=null? MATHLIB.dpow_lib(a,b): dpow_default(a,b) ); } public static double dpow_d(double a, double b) { return MATHLIB!=null? MATHLIB.dpow_lib(a,b): dpow_default(a,b); } /** * Hook to override default dpow behavior with faster implementation. */ public double dpow_lib(double a, double b) { return dpow_default(a,b); } /** * Default JME version computes using longhand heuristics. */ protected static double dpow_default(double a, double b) { if ( b < 0 ) return 1 / dpow_default( a, -b ); double p = 1; int whole = (int) b; for ( double v=a; whole > 0; whole>>=1, v*=v ) if ( (whole & 1) != 0 ) p *= v; if ( (b -= whole) > 0 ) { int frac = (int) (0x10000 * b); for ( ; (frac&0xffff)!=0; frac<<=1 ) { a = Math.sqrt(a); if ( (frac & 0x8000) != 0 ) p *= a; } } return p; } }