| /******************************************************************************* |
| * 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.vm3.lib; |
| |
| import java.util.Random; |
| |
| import org.luaj.vm3.LuaDouble; |
| import org.luaj.vm3.LuaTable; |
| import org.luaj.vm3.LuaValue; |
| import org.luaj.vm3.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.vm3.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) { |
| double m = a.checkdouble(); |
| if (m < 1) |
| argerror(1, "interval is empty"); |
| return valueOf(Math.floor(random.nextDouble() * m) + 1L); |
| } |
| |
| public LuaValue call(LuaValue a, LuaValue b) { |
| double m = a.checkdouble(); |
| double n = b.checkdouble(); |
| if (n < m) |
| argerror(2, "interval is empty"); |
| return valueOf(Math.floor(random.nextDouble() * (n - m + 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; |
| } |
| |
| } |