blob: 979a377db9ebb2a2caa72f31217a8d37fcae3a66 [file] [log] [blame] [raw]
/*******************************************************************************
* 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;
}
}