/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.quercus.lib; import com.caucho.quercus.annotation.Optional; import com.caucho.quercus.env.*; import com.caucho.quercus.module.AbstractQuercusModule; import com.caucho.quercus.program.JavaClassDef; import com.caucho.util.L10N; import com.caucho.util.RandomUtil; import java.util.Iterator; import java.math.BigInteger; /** * PHP math routines. */ public class MathModule extends AbstractQuercusModule { private static final L10N L = new L10N(MathModule.class); public static final double M_PI = Math.PI; public static final double M_E = Math.E; public static final long RAND_MAX = Integer.MAX_VALUE; public static final double M_LOG2E = log2(Math.E); public static final double M_LOG10E = Math.log10(Math.E); public static final double M_LN2 = Math.log(2); public static final double M_LN10 = Math.log(10); public static final double M_PI_2 = Math.PI / 2; public static final double M_PI_4 = Math.PI / 4; public static final double M_1_PI = 1 / Math.PI; public static final double M_2_PI = 2 / Math.PI; public static final double M_SQRTPI = Math.sqrt(Math.PI); public static final double M_2_SQRTPI = 2 / Math.sqrt(Math.PI); public static final double M_SQRT2 = Math.sqrt(2); public static final double M_SQRT3 = Math.sqrt(3); public static final double M_SQRT1_2 = 1 / Math.sqrt(2); public static final double M_LNPI = Math.log(Math.PI); public static final double M_EULER = 0.57721566490153286061; private static double log2(double v) { return Math.log(v) / Math.log(2); } public static Value abs(Value value) { return value.abs(); } public static double acos(double value) { return Math.acos(value); } public static double acosh(Env env, double value) { return Math.log(value + Math.sqrt(value * value - 1)); } public static Value asin(Value value) { return new DoubleValue(Math.asin(value.toDouble())); } public static double asinh(double value) { return Math.log(value + Math.sqrt(value * value + 1)); } public static double atan2(double yV, double xV) { return Math.atan2(yV, xV); } public static double atan(double value) { return Math.atan(value); } public static double atanh(double value) { return 0.5 * Math.log((1 + value) / (1 - value)); } /** * Convert a number between arbitrary bases * * @param number A string represeantion of an binary number. * @param fromBase The base of the number parameter. * @param toBase The base of convert to. * @return the number as a Value, either a LongValue or a DoubleValue. */ public static Value base_convert(Env env, StringValue str, int fromBase, int toBase) { if (fromBase < 2 || fromBase > 36) { env.warning(L.l("invalid `{0}' ({1})", "from base", fromBase)); return BooleanValue.FALSE; } if (toBase < 2 || toBase > 36) { env.warning(L.l("invalid `{0}' ({1})", "to base", toBase)); return BooleanValue.FALSE; } Number num = baseToInt(env, str, fromBase); if (num instanceof BigInteger) return intToBase(env, (BigInteger) num, toBase); else return intToBase(env, num.longValue(), toBase); } private static Number baseToInt(Env env, StringValue str, int base) { long result = 0L; boolean isLong = true; int len = str.length(); for (int i = 0; i < len; i++) { int ch = str.charAt(i); int d; if ('0' <= ch && ch <= '9') d = ch - '0'; else if ('a' <= ch && ch <= 'z') d = ch - 'a' + 10; else if ('A' <= ch && ch <= 'Z') d = ch - 'A' + 10; else continue; if (base <= d) continue; if (result * base + d < result) { isLong = false; break; } result = result * base + d; } if (isLong) return Long.valueOf(result); else return new BigInteger(str.toString(), base); } private static StringValue intToBase(Env env, long num, int base) { if (num == 0) return env.createString((char) '0'); // ignore sign if (num < 0) num = num ^ Long.MAX_VALUE + 1; int bufLen = 64; char []buffer = new char[bufLen]; int i = bufLen; while (num != 0 && i > 0) { int d = (int) (num % base); if (d < 10) buffer[--i] = (char) (d + '0'); else buffer[--i] = (char) (d + 'a' - 10); num = num / base; } for (int j = i; j < bufLen; j++) { buffer[j - i] = buffer[j]; } return env.createString(buffer, bufLen - i); } private static StringValue intToBase(Env env, BigInteger num, int base) { BigInteger toBaseBig = BigInteger.valueOf(base); BigInteger zero = BigInteger.valueOf(0); StringValue sb = env.createStringBuilder(); do { BigInteger []resultArray = num.divideAndRemainder(toBaseBig); num = resultArray[0]; int d = resultArray[1].intValue(); if (d < 10) sb.append((char) (d + '0')); else sb.append((char) (d + 'a' - 10)); } while (num.compareTo(zero) != 0); StringValue toReturn = env.createStringBuilder(); int len = sb.length(); for (int i = len - 1; i >= 0; i--) { toReturn.append(sb.charAt(i)); } return toReturn; } /** * Returns the decimal equivalent of the binary number represented by the * binary string argument. * * @param bin A string representation of an binary number. * @return the decimal equivalent of the binary number */ public static Value bindec(Env env, StringValue bin) { Number num = baseToInt(env, bin, 2); if (num instanceof Long) return LongValue.create(num.longValue()); else return env.wrapJava(num); } public static double ceil(double value) { return Math.ceil(value); } public static double cos(double value) { return Math.cos(value); } public static double cosh(double value) { return Math.cosh(value); } /** * Returns a binary representation of a number. * @param value the number */ public static StringValue decbin(Env env, long value) { return intToBase(env, value, 2); /* value = value & 037777777777L; if (value == 0) return env.createString("0"); char []buffer = new char[32]; int i = 32; while (value != 0 && i >= 0) { int d = (int) (value & 0x01); value = value >>> 1; buffer[--i] = (char) (d + '0'); } for (int j = i; j < 32; j++) { buffer[j - i] = buffer[j]; } return env.createString(buffer, 32 - i); */ } /** * Returns a hexadecimal representation of a number. * @param value the number */ public static StringValue dechex(Env env, long value) { return intToBase(env, value, 16); /* value = value & 037777777777L; if (value == 0) return env.createString("0"); char []buffer = new char[16]; int i = 16; while (value != 0) { int d = (int) (value & 0xf); value = value >>> 4; if (d < 10) buffer[--i] = (char) (d + '0'); else buffer[--i] = (char) (d + 'a' - 10); } for (int j = i; j < 16; j++) { buffer[j - i] = buffer[j]; } return env.createString(buffer, 16 - i); */ } /** * Returns an octal representation of a number. * @param value the number */ public static StringValue decoct(Env env, long value) { return intToBase(env, value, 8); /* value = value & 037777777777L; if (value == 0) return env.createString("0"); char []buffer = new char[11]; int i = 11; while (value != 0 && i > 0) { int d = (int) (value & 0x7); value = value >>> 3; buffer[--i] = (char) (d + '0'); } for (int j = i; j < 11; j++) { buffer[j - i] = buffer[j]; } return env.createString(buffer, 11 - i); */ } public static double deg2rad(double value) { return value * Math.PI / 180; } public static Value exp(Value value) { return new DoubleValue(Math.exp(value.toDouble())); } public static Value expm1(Value value) { return new DoubleValue(Math.expm1(value.toDouble())); } public static Value floor(Value value) { return new DoubleValue(Math.floor(value.toDouble())); } public static double fmod(double xV, double yV) { return Math.IEEEremainder(xV, yV); } public static Value hexdec(Env env, StringValue s) { Number num = baseToInt(env, s, 16); if (num instanceof Long) return LongValue.create(num.longValue()); else return env.wrapJava(num); } public static double hypot(double a, double b) { return Math.hypot(a, b); } public static boolean is_finite(Value value) { if (value instanceof LongValue) return true; else if (value instanceof DoubleValue) { double v = value.toDouble(); return ! Double.isInfinite(v); } else return false; } public static Value is_infinite(Value value) { if (value instanceof LongValue) return BooleanValue.FALSE; else if (value instanceof DoubleValue) { double v = value.toDouble(); return Double.isInfinite(v) ? BooleanValue.TRUE : BooleanValue.FALSE; } else return BooleanValue.FALSE; } public static Value is_nan(Value value) { if (value instanceof LongValue) return BooleanValue.FALSE; else if (value instanceof DoubleValue) { double v = value.toDouble(); return Double.isNaN(v) ? BooleanValue.TRUE : BooleanValue.FALSE; } else return BooleanValue.FALSE; } public static double log(double value) { return Math.log(value); } public static double log10(double value) { return Math.log10(value); } public static double log1p(double value) { return Math.log1p(value); } public static Value getrandmax() { return mt_getrandmax(); } public static Value max(Env env, Value []args) { if (args.length == 1 && args[0] instanceof ArrayValue) { Value array = args[0]; Value max = NullValue.NULL; double maxValue = Double.MIN_VALUE; Iterator<Value> iter = array.getValueIterator(env); while (iter.hasNext()) { Value value = iter.next(); double dValue = value.toDouble(); if (maxValue < dValue) { maxValue = dValue; max = value; } } return max; } else { double maxValue = - Double.MAX_VALUE; Value max = NullValue.NULL; for (int i = 0; i < args.length; i++) { double value = args[i].toDouble(); if (maxValue < value) { maxValue = value; max = args[i]; } } return max; } } public static Value min(Env env, Value []args) { if (args.length == 1 && args[0] instanceof ArrayValue) { Value array = args[0]; Value min = NullValue.NULL; double minValue = Double.MAX_VALUE; Iterator<Value> iter = array.getValueIterator(env); while (iter.hasNext()) { Value value = iter.next(); double dValue = value.toDouble(); if (dValue < minValue) { minValue = dValue; min = value; } } return min; } else { double minValue = Double.MAX_VALUE; Value min = NullValue.NULL; for (int i = 0; i < args.length; i++) { double value = args[i].toDouble(); if (value < minValue) { minValue = value; min = args[i]; } } return min; } } public static Value mt_getrandmax() { return new LongValue(RAND_MAX); } public static long mt_rand(@Optional("0") long min, @Optional("RAND_MAX") long max) { long range = max - min + 1; if (range <= 0) return min; long value = RandomUtil.getRandomLong(); if (value < 0) value = - value; return min + value % range; } public static Value mt_srand(@Optional long seed) { return NullValue.NULL; } public static double lcg_value() { return RandomUtil.nextDouble(); } /** * Returns the decimal equivalent of the octal number represented by the * octal_string argument. * * @param oct A string represeantion of an octal number. * @return the decimal equivalent of the octal number */ public static Value octdec(Env env, StringValue oct) { Number num = baseToInt(env, oct, 8); if (num instanceof Long) return LongValue.create(num.longValue()); else return env.wrapJava(num); } public static double pi() { return M_PI; } /* * XXX: may return an integer if it can be represented * by a system-dependent integer */ public static double pow(double base, double exp) { return Math.pow(base, exp); } public static double rad2deg(double value) { return 180 * value / Math.PI; } public static long rand(@Optional int min, @Optional("RAND_MAX") int max) { return mt_rand(min, max); } public static double round(double value, @Optional int precision) { if (precision > 0) { double exp = Math.pow(10, precision); return Math.round(value * exp) / exp; } else return Math.round(value); } public static double sin(double value) { return Math.sin(value); } public static Value sinh(Value value) { return new DoubleValue(Math.sinh(value.toDouble())); } public static double sqrt(double value) { return Math.sqrt(value); } public static Value srand(@Optional long seed) { return NullValue.NULL; } public static double tan(double value) { return Math.tan(value); } public static double tanh(double value) { return Math.tanh(value); } }