/*
* Copyright (c) 1998-2010 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.annotation.ReturnNullAsFalse;
import com.caucho.quercus.env.*;
import com.caucho.quercus.module.AbstractQuercusModule;
import com.caucho.util.L10N;
import java.util.Iterator;
import java.math.BigInteger;
import java.util.Random;
/**
* PHP math routines.
*/
public class MathModule extends AbstractQuercusModule {
private static final L10N L = new L10N(MathModule.class);
private static final Random rnd = new Random();
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);
}
@ReturnNullAsFalse
public static DoubleValue log(Env env, double value, double base) {
if (base <= 0) {
env.warning(L.l("base must be greater than 0"));
return null;
}
return new DoubleValue(Math.log(value) / Math.log(base));
}
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 = rnd.nextLong();
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 rnd.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);
}
}