content: /*******************************************************************************
  * 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;
  	}
  
  }
  