package php.runtime.ext.core; import php.runtime.Memory; import php.runtime.annotation.Runtime; import php.runtime.annotation.Runtime.Immutable; import php.runtime.ext.support.compile.FunctionsContainer; import php.runtime.memory.ArrayMemory; import php.runtime.memory.DoubleMemory; import php.runtime.memory.LongMemory; import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; import java.math.RoundingMode; import java.util.HashMap; import java.util.Map; import java.util.Random; public class MathFunctions extends FunctionsContainer { private final static MathConstants constants = new MathConstants(); public static Random MERSENNE_TWISTER = new Random(); public static Random RANDOM = new Random(); private final static double[] COS_CACHE = new double[Byte.MAX_VALUE]; private final static double[] SIN_CACHE = new double[Byte.MAX_VALUE]; private final static int MAX_COS_NEG = COS_CACHE.length; private final static int MAX_SIN_NEG = SIN_CACHE.length; private final static int MAX_COS = COS_CACHE.length - MAX_COS_NEG; private final static int MAX_SIN = SIN_CACHE.length - MAX_SIN_NEG; static { for(int i = -MAX_COS_NEG; i < MAX_COS; i++){ COS_CACHE[i + MAX_COS_NEG] = Math.cos(i); } for(int i = -MAX_SIN_NEG; i < MAX_SIN; i++){ SIN_CACHE[i + MAX_SIN_NEG] = Math.cos(i); } } @Override protected Map<String, Method> getNativeFunctions() { return new HashMap<String, Method>(){{ put("acos", getNative(Math.class, "acos", Double.TYPE)); put("asin", getNative(Math.class, "asin", Double.TYPE)); put("atan2", getNative(Math.class, "atan2", Double.TYPE, Double.TYPE)); put("atan", getNative(Math.class, "atan", Double.TYPE)); put("ceil", getNative(Math.class, "ceil", Double.TYPE)); put("cosh", getNative(Math.class, "cosh", Double.TYPE)); put("sinh", getNative(Math.class, "sinh", Double.TYPE)); put("tanh", getNative(Math.class, "tanh", Double.TYPE)); put("tan", getNative(Math.class, "tan", Double.TYPE)); put("rad2deg", getNative(Math.class, "toDegrees", Double.TYPE)); put("deg2rad", getNative(Math.class, "toRadians", Double.TYPE)); put("exp", getNative(Math.class, "exp", Double.TYPE)); put("expm1", getNative(Math.class, "expm1", Double.TYPE)); put("floor", getNative(Math.class, "floor", Double.TYPE)); put("hypot", getNative(Math.class, "hypot", Double.TYPE, Double.TYPE)); put("is_infinite", getNative(Double.class, "isInfinite", Double.TYPE)); put("is_nan", getNative(Double.class, "isNaN", Double.TYPE)); put("log10", getNative(Math.class, "log10", Double.TYPE)); put("log1p", getNative(Math.class, "log1p", Double.TYPE)); put("log", getNative(Math.class, "log", Double.TYPE)); put("sqrt", getNative(Math.class, "sqrt", Double.TYPE)); }}; } private static double _cos(long value){ if (value >= -MAX_COS_NEG && value < MAX_COS) return COS_CACHE[(int)value + MAX_COS_NEG]; else return Math.cos(value); } private static double _sin(long value){ if (value >= -MAX_SIN_NEG && value < MAX_SIN) return SIN_CACHE[(int)value + MAX_SIN_NEG]; else return Math.sin(value); } @Immutable public static double cos(Memory memory){ switch (memory.type){ case DOUBLE: return Math.cos(memory.toDouble()); case STRING: return cos(memory.toNumeric()); default: return _cos(memory.toLong()); } } @Immutable public static double sin(Memory memory){ switch (memory.type){ case DOUBLE: return Math.sin(memory.toDouble()); case STRING: return sin(memory.toNumeric()); default: return _sin(memory.toLong()); } } @Immutable public static Memory abs(Memory value) { switch (value.type){ case DOUBLE: return new DoubleMemory(Math.abs(value.toDouble())); case STRING: return abs(value.toNumeric()); default: return LongMemory.valueOf(Math.abs(value.toLong())); } } @Immutable public static double asinh(double x) { return Math.log(x + Math.sqrt(x*x + 1.0)); } @Immutable public static double acosh(double x) { return Math.log(x + Math.sqrt(x*x - 1.0)); } @Immutable public static double atanh(double x) { return 0.5 * Math.log( (x + 1.0) / (x - 1.0) ); } @Immutable public static String base_convert(String number, int fromBase, int toBase){ return new BigInteger(number, fromBase).toString( toBase); } @Immutable public static long bindec(String binary){ try { return Long.parseLong(binary, 2); } catch (NumberFormatException e){ return 0; } } @Immutable public static String decbin(long value){ return Long.toString(value, 2); } @Immutable public static String dechex(long value){ return Long.toString(value, 16); } @Immutable public static String decoct(long value){ return Long.toString(value, 8); } @Immutable public static double fmod(double x, double y){ return x % y; } @Immutable public static long getmaxrand(){ return Integer.MAX_VALUE; } @Immutable public static long hexdec(String hex){ try { return Long.parseLong(hex, 16); } catch (NumberFormatException e){ return 0; } } @Immutable public static boolean is_finite(double value){ return !Double.isInfinite(value); } public static double lcg_value(){ return Math.random(); } @Immutable public static Memory max(Memory value, Memory... args){ if (value.isArray() && args == null){ Memory max = null; for (Memory one : (ArrayMemory)value){ if (max == null || one.greater(max)) max = one; } return max == null ? Memory.NULL : max.toImmutable(); } else { Memory max = value; if (args != null) for(Memory one : args){ if (one.greater(max)) max = one; } return max.toImmutable(); } } @Immutable public static Memory min(Memory value, Memory... args){ if (value.isArray() && args == null){ Memory min = null; for (Memory one : (ArrayMemory)value){ if (min == null || one.smaller(min)) min = one; } return min == null ? Memory.NULL : min.toImmutable(); } else { Memory min = value; for(Memory one : args){ if (one.smaller(min)) min = one; } return min.toImmutable(); } } @Immutable public static long mt_getrandmax(){ return Integer.MAX_VALUE; } public static long mt_rand(){ return MERSENNE_TWISTER.nextLong(); } public static Memory mt_rand(long min, long max){ if (max < min) return Memory.FALSE; return LongMemory.valueOf(MERSENNE_TWISTER.nextInt((int)(max - min) + 1) + min); } public static void mt_srand(){ MERSENNE_TWISTER = new Random(); } public static void mt_srand(long seed){ MERSENNE_TWISTER = new Random(seed); } public static long rand(){ return RANDOM.nextLong(); } public static Memory rand(long min, long max){ if (max < min) return Memory.FALSE; return LongMemory.valueOf(RANDOM.nextInt((int)(max - min) + 1) + min); } public static void srand(){ RANDOM = new Random(); } public static void srand(long seed){ RANDOM = new Random(seed); } @Immutable public static Memory octdec(String octalString){ try { return LongMemory.valueOf(Long.parseLong(octalString, 8)); } catch (NumberFormatException e){ return Memory.FALSE; } } @Immutable public static double pi(){ return constants.M_PI; } @Immutable public static Memory pow(Memory base, Memory exp) { Memory realBase = base.toNumeric(); Memory realExp = exp.toNumeric(); if (realBase.type == Memory.Type.INT && realExp.type == Memory.Type.INT) { double result = Math.pow(realBase.toLong(), realExp.toLong()); if (result > Long.MAX_VALUE) { return DoubleMemory.valueOf(result); } return LongMemory.valueOf((long) result); } else { return new DoubleMemory(Math.pow(base.toDouble(), exp.toDouble())); } } @Immutable public static double round(double value){ return Math.round(value); } @Immutable public static double round(double value, int precession){ return BigDecimal.valueOf(value) .setScale(precession, RoundingMode.HALF_UP) .doubleValue(); } @Immutable public static double round(double value, int precession, int mode){ MathContext context; switch (mode){ case 2: context = new MathContext(precession, RoundingMode.DOWN); break; case 3: context = new MathContext(precession, RoundingMode.HALF_EVEN); break; case 4: if ((long)value % 2 == 0) context = new MathContext(precession, RoundingMode.UP); else context = new MathContext(precession, RoundingMode.DOWN); break; default: context = new MathContext(precession, RoundingMode.UP); } return BigDecimal.valueOf(value) .round(context) .doubleValue(); } }