mirror of https://github.com/apache/druid.git
Add more math functions (#2822)
* Add more math functions * added function list
This commit is contained in:
parent
e680665f1c
commit
45a3a26ef7
|
@ -122,7 +122,7 @@ class FunctionExpr implements Expr
|
|||
@Override
|
||||
public Number eval(Map<String, Number> bindings)
|
||||
{
|
||||
return Parser.func.get(name).apply(args, bindings);
|
||||
return Parser.func.get(name.toLowerCase()).apply(args, bindings);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,39 +26,608 @@ import java.util.Map;
|
|||
*/
|
||||
interface Function
|
||||
{
|
||||
String name();
|
||||
|
||||
Number apply(List<Expr> args, Map<String, Number> bindings);
|
||||
}
|
||||
|
||||
class SqrtFunc implements Function
|
||||
{
|
||||
|
||||
@Override
|
||||
public Number apply(List<Expr> args, Map<String, Number> bindings)
|
||||
abstract class SingleParam implements Function
|
||||
{
|
||||
if (args.size() != 1) {
|
||||
throw new RuntimeException("function 'sqrt' needs 1 argument");
|
||||
@Override
|
||||
public Number apply(List<Expr> args, Map<String, Number> bindings)
|
||||
{
|
||||
if (args.size() != 1) {
|
||||
throw new RuntimeException("function '" + name() + "' needs 1 argument");
|
||||
}
|
||||
Expr expr = args.get(0);
|
||||
return eval(expr.eval(bindings));
|
||||
}
|
||||
|
||||
Number x = args.get(0).eval(bindings);
|
||||
return Math.sqrt(x.doubleValue());
|
||||
protected abstract Number eval(Number x);
|
||||
}
|
||||
}
|
||||
|
||||
class ConditionFunc implements Function
|
||||
{
|
||||
|
||||
@Override
|
||||
public Number apply(List<Expr> args, Map<String, Number> bindings)
|
||||
abstract class DoubleParam implements Function
|
||||
{
|
||||
if (args.size() != 3) {
|
||||
throw new RuntimeException("function 'if' needs 3 argument");
|
||||
@Override
|
||||
public Number apply(List<Expr> args, Map<String, Number> bindings)
|
||||
{
|
||||
if (args.size() != 2) {
|
||||
throw new RuntimeException("function '" + name() + "' needs 1 argument");
|
||||
}
|
||||
Expr expr1 = args.get(0);
|
||||
Expr expr2 = args.get(1);
|
||||
return eval(expr1.eval(bindings), expr2.eval(bindings));
|
||||
}
|
||||
|
||||
Number x = args.get(0).eval(bindings);
|
||||
if (x instanceof Long) {
|
||||
return x.longValue() > 0 ? args.get(1).eval(bindings) : args.get(2).eval(bindings);
|
||||
} else {
|
||||
return x.doubleValue() > 0 ? args.get(1).eval(bindings) : args.get(2).eval(bindings);
|
||||
protected abstract Number eval(Number x, Number y);
|
||||
}
|
||||
|
||||
class Abs extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "abs";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return x instanceof Long ? Math.abs(x.longValue()) : Math.abs(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Acos extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "acos";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.acos(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Asin extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "asin";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.asin(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Atan extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "atan";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.atan(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Cbrt extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "cbrt";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.cbrt(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Ceil extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "ceil";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.ceil(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Cos extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "cos";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.cos(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Cosh extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "cosh";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.cosh(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Exp extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "exp";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.exp(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Expm1 extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "expm1";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.expm1(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Floor extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "floor";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.floor(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class GetExponent extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "getExponent";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.getExponent(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Log extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "log";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.log(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Log10 extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "log10";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.log10(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Log1p extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "log1p";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.log1p(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class NextUp extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "nextUp";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.nextUp(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Rint extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "rint";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.rint(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Round extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "round";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.round(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Signum extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "signum";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.signum(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Sin extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "sin";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.sin(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Sinh extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "sinh";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.sinh(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Sqrt extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "sqrt";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.sqrt(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Tan extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "tan";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.tan(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Tanh extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "tanh";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.tanh(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class ToDegrees extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "toDegrees";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.toDegrees(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class ToRadians extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "toRadians";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.toRadians(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Ulp extends SingleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "ulp";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x)
|
||||
{
|
||||
return Math.ulp(x.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Atan2 extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "atan2";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
{
|
||||
return Math.atan2(x.doubleValue(), y.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class CopySign extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "copySign";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
{
|
||||
return Math.copySign(x.doubleValue(), y.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Hypot extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "hypot";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
{
|
||||
return Math.hypot(x.doubleValue(), y.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Remainder extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "remainder";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
{
|
||||
return Math.IEEEremainder(x.doubleValue(), y.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Max extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "max";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
{
|
||||
if (x instanceof Long && y instanceof Long) {
|
||||
return Math.max(x.longValue(), y.longValue());
|
||||
}
|
||||
return Double.compare(x.doubleValue(), y.doubleValue()) >= 0 ? x : y;
|
||||
}
|
||||
}
|
||||
|
||||
class Min extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "min";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
{
|
||||
if (x instanceof Long && y instanceof Long) {
|
||||
return Math.min(x.longValue(), y.longValue());
|
||||
}
|
||||
return Double.compare(x.doubleValue(), y.doubleValue()) <= 0 ? x : y;
|
||||
}
|
||||
}
|
||||
|
||||
class NextAfter extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "nextAfter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
{
|
||||
return Math.nextAfter(x.doubleValue(), y.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Pow extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "pow";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
{
|
||||
return Math.pow(x.doubleValue(), y.doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
class Scalb extends DoubleParam
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "scalb";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number eval(Number x, Number y)
|
||||
{
|
||||
return Math.scalb(x.doubleValue(), y.intValue());
|
||||
}
|
||||
}
|
||||
|
||||
class ConditionFunc implements Function
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "if";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number apply(List<Expr> args, Map<String, Number> bindings)
|
||||
{
|
||||
if (args.size() != 3) {
|
||||
throw new RuntimeException("function 'if' needs 3 argument");
|
||||
}
|
||||
|
||||
Number x = args.get(0).eval(bindings);
|
||||
if (x instanceof Long) {
|
||||
return x.longValue() > 0 ? args.get(1).eval(bindings) : args.get(2).eval(bindings);
|
||||
} else {
|
||||
return x.doubleValue() > 0 ? args.get(1).eval(bindings) : args.get(2).eval(bindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ package io.druid.math.expr;
|
|||
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.metamx.common.logger.Logger;
|
||||
import io.druid.math.expr.antlr.ExprLexer;
|
||||
import io.druid.math.expr.antlr.ExprParser;
|
||||
import org.antlr.v4.runtime.ANTLRInputStream;
|
||||
|
@ -28,17 +30,28 @@ import org.antlr.v4.runtime.CommonTokenStream;
|
|||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
|
||||
public class Parser
|
||||
{
|
||||
static final Logger log = new Logger(Parser.class);
|
||||
static final Map<String, Function> func;
|
||||
|
||||
static {
|
||||
func = ImmutableMap.<String, Function>builder()
|
||||
.put("sqrt", new SqrtFunc())
|
||||
.put("if", new ConditionFunc())
|
||||
.build();
|
||||
Map<String, Function> functionMap = Maps.newHashMap();
|
||||
for (Class clazz : Function.class.getClasses()) {
|
||||
if (!Modifier.isAbstract(clazz.getModifiers()) && Function.class.isAssignableFrom(clazz)) {
|
||||
try {
|
||||
Function function = (Function)clazz.newInstance();
|
||||
functionMap.put(function.name().toLowerCase(), function);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.info("failed to instantiate " + clazz.getName() + ".. ignoring", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
func = ImmutableMap.copyOf(functionMap);
|
||||
}
|
||||
|
||||
public static Expr parse(String in)
|
||||
|
|
|
@ -26,3 +26,41 @@ Also, the following in-built functions are supported.
|
|||
|sqrt|sqrt(x) would return square root of x|
|
||||
|if|if(predicate,then,else) returns 'then' if 'predicate' evaluates to a positive number, otherwise it returns 'else'|
|
||||
|
||||
Built-in Math functions. See javadoc of java.lang.Math for detailed explanation for each function.
|
||||
|
||||
|abs|abs(x) would return the absolute value of x|
|
||||
|acos|acos(x) would return the arc cosine of x|
|
||||
|asin|asin(x) would return the arc sine of x|
|
||||
|atan|atan(x) would return the arc tangent of x|
|
||||
|atan2|atan2(y, x) would return the angle theta from the conversion of rectangular coordinates (x, y) to polar * coordinates (r, theta)|
|
||||
|cbrt|cbrt(x) would return the cube root of x|
|
||||
|ceil|ceil(x) would return the smallest (closest to negative infinity) double value that is greater than or equal to x and is equal to a mathematical integer|
|
||||
|copysign|copysign(x) would return the first floating-point argument with the sign of the second floating-point argument|
|
||||
|cos|cos(x) would return the trigonometric cosine of x|
|
||||
|cosh|cosh(x) would return the hyperbolic cosine of x|
|
||||
|exp|exp(x) would return Euler's number raised to the power of x|
|
||||
|expm1|expm1(x) would return e^x-1|
|
||||
|floor|floor(x) would return the largest (closest to positive infinity) double value that is less than or equal to x and is equal to a mathematical integer|
|
||||
|getExponent|getExponent(x) would return the unbiased exponent used in the representation of x|
|
||||
|hypot|hypot(x, y) would return sqrt(x^2+y^2) without intermediate overflow or underflow|
|
||||
|log|log(x) would return the natural logarithm of x|
|
||||
|log10|log10(x) would return the base 10 logarithm of x|
|
||||
|log1p|log1p(x) would the natural logarithm of x + 1|
|
||||
|max|max(x, y) would return the greater of two values|
|
||||
|min|min(x, y) would return the smaller of two values|
|
||||
|nextafter|nextafter(x, y) would return the floating-point number adjacent to the x in the direction of the y|
|
||||
|nextUp|nextUp(x) would return the floating-point value adjacent to x in the direction of positive infinity|
|
||||
|pow|pow(x, y) would return the value of the x raised to the power of y|
|
||||
|remainder|remainder(x, y) would return the remainder operation on two arguments as prescribed by the IEEE 754 standard|
|
||||
|rint|rint(x) would return value that is closest in value to x and is equal to a mathematical integer|
|
||||
|round|round(x) would return the closest long value to x, with ties rounding up|
|
||||
|scalb|scalb(d, sf) would return d * 2^sf rounded as if performed by a single correctly rounded floating-point multiply to a member of the double value set|
|
||||
|signum|signum(x) would return the signum function of the argument x|
|
||||
|sin|sin(x) would return the trigonometric sine of an angle x|
|
||||
|sinh|sinh(x) would return the hyperbolic sine of x|
|
||||
|sqrt|sqrt(x) would return the correctly rounded positive square root of x|
|
||||
|tan|tan(x) would return the trigonometric tangent of an angle x|
|
||||
|tanh|tanh(x) would return the hyperbolic tangent of x|
|
||||
|todegrees|todegrees(x) converts an angle measured in radians to an approximately equivalent angle measured in degrees|
|
||||
|toradians|toradians(x) converts an angle measured in degrees to an approximately equivalent angle measured in radians|
|
||||
|ulp|ulp(x) would return the size of an ulp of the argument x|
|
||||
|
|
Loading…
Reference in New Issue