You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by ce...@apache.org on 2016/12/08 14:14:50 UTC

incubator-metron git commit: METRON-364: Preserve Type for Arithmetic Expressions in Stellar closes apache/incubator-metron#390

Repository: incubator-metron
Updated Branches:
  refs/heads/master a96c4ed2e -> b36f4b2fb


METRON-364: Preserve Type for Arithmetic Expressions in Stellar closes apache/incubator-metron#390


Project: http://git-wip-us.apache.org/repos/asf/incubator-metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-metron/commit/b36f4b2f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-metron/tree/b36f4b2f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-metron/diff/b36f4b2f

Branch: refs/heads/master
Commit: b36f4b2fbf8cc4578ef3f39ac4d8306544f6bcb4
Parents: a96c4ed
Author: JJ <jj...@gmail.com>
Authored: Thu Dec 8 09:14:24 2016 -0500
Committer: cstella <ce...@gmail.com>
Committed: Thu Dec 8 09:14:24 2016 -0500

----------------------------------------------------------------------
 .../stellar/DefaultStellarExecutorTest.java     |   2 +-
 .../dsl/functions/ConversionFunctions.java      |  10 +
 .../common/stellar/BaseStellarProcessor.java    |   3 +-
 .../metron/common/stellar/StellarCompiler.java  |  50 +--
 .../stellar/evaluators/ArithmeticEvaluator.java | 102 +++++
 .../dsl/functions/ConversionFunctionsTest.java  |  44 ++
 .../common/stellar/StellarArithmeticTest.java   | 181 ++++++++
 .../metron/common/stellar/StellarTest.java      |  93 +----
 .../evaluators/ArithmeticEvaluatorTest.java     | 412 +++++++++++++++++++
 9 files changed, 779 insertions(+), 118 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/stellar/DefaultStellarExecutorTest.java
----------------------------------------------------------------------
diff --git a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/stellar/DefaultStellarExecutorTest.java b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/stellar/DefaultStellarExecutorTest.java
index 3110329..9a77f8e 100644
--- a/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/stellar/DefaultStellarExecutorTest.java
+++ b/metron-analytics/metron-profiler-common/src/test/java/org/apache/metron/profiler/stellar/DefaultStellarExecutorTest.java
@@ -113,7 +113,7 @@ public class DefaultStellarExecutorTest {
 
     // verify
     Object var = executor.getState().get("sum");
-    assertEquals(6.0, var);
+    assertEquals(6, var);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/main/java/org/apache/metron/common/dsl/functions/ConversionFunctions.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/dsl/functions/ConversionFunctions.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/dsl/functions/ConversionFunctions.java
index e9d2436..4ddff99 100644
--- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/dsl/functions/ConversionFunctions.java
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/dsl/functions/ConversionFunctions.java
@@ -70,4 +70,14 @@ public class ConversionFunctions {
 
     public TO_LONG() { super(Long.class); }
   }
+
+  @Stellar(name="TO_FLOAT"
+      , description="Transforms the first argument to a float"
+      , params = { "input - Object of string or numeric type"}
+      , returns = "Float version of the first argument"
+  )
+  public static class TO_FLOAT extends Cast<Float> {
+
+    public TO_FLOAT() { super(Float.class); }
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/BaseStellarProcessor.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/BaseStellarProcessor.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/BaseStellarProcessor.java
index 1a7f626..f20cf43 100644
--- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/BaseStellarProcessor.java
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/BaseStellarProcessor.java
@@ -26,6 +26,7 @@ import java.util.HashSet;
 import java.util.Set;
 import org.apache.metron.common.dsl.*;
 import org.apache.metron.common.dsl.functions.resolver.FunctionResolver;
+import org.apache.metron.common.stellar.evaluators.ArithmeticEvaluator;
 import org.apache.metron.common.stellar.generated.StellarBaseListener;
 import org.apache.metron.common.stellar.generated.StellarLexer;
 import org.apache.metron.common.stellar.generated.StellarParser;
@@ -82,7 +83,7 @@ public class BaseStellarProcessor<T> {
     TokenStream tokens = new CommonTokenStream(lexer);
     StellarParser parser = new StellarParser(tokens);
 
-    StellarCompiler treeBuilder = new StellarCompiler(variableResolver, functionResolver, context);
+    StellarCompiler treeBuilder = new StellarCompiler(variableResolver, functionResolver, context, new ArithmeticEvaluator());
     parser.addParseListener(treeBuilder);
     parser.removeErrorListeners();
     parser.addErrorListener(new ErrorListener());

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/StellarCompiler.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/StellarCompiler.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/StellarCompiler.java
index 3ac3d98..a5c4cf3 100644
--- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/StellarCompiler.java
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/StellarCompiler.java
@@ -20,6 +20,7 @@ package org.apache.metron.common.stellar;
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableSet;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.metron.common.dsl.Context;
 import org.apache.metron.common.dsl.FunctionMarker;
 import org.apache.metron.common.dsl.functions.resolver.FunctionResolver;
@@ -27,6 +28,7 @@ import org.apache.metron.common.dsl.ParseException;
 import org.apache.metron.common.dsl.StellarFunction;
 import org.apache.metron.common.dsl.Token;
 import org.apache.metron.common.dsl.VariableResolver;
+import org.apache.metron.common.stellar.evaluators.ArithmeticEvaluator;
 import org.apache.metron.common.stellar.generated.StellarBaseListener;
 import org.apache.metron.common.stellar.generated.StellarParser;
 import org.apache.metron.common.utils.ConversionUtils;
@@ -49,11 +51,13 @@ public class StellarCompiler extends StellarBaseListener {
   private FunctionResolver functionResolver;
   private VariableResolver variableResolver;
   private Throwable actualException = null;
+  private final ArithmeticEvaluator arithmeticEvaluator;
 
-  public StellarCompiler(VariableResolver variableResolver, FunctionResolver functionResolver, Context context) {
+  public StellarCompiler(VariableResolver variableResolver, FunctionResolver functionResolver, Context context, ArithmeticEvaluator arithmeticEvaluator) {
     this.variableResolver = variableResolver;
     this.functionResolver = functionResolver;
     this.context = context;
+    this.arithmeticEvaluator = arithmeticEvaluator;
   }
 
   @Override
@@ -80,15 +84,6 @@ public class StellarCompiler extends StellarBaseListener {
     return set.contains(key);
   }
 
-  private Double getDouble(Token<?> token) {
-    Number n = (Number) token.getValue();
-    if (n == null) {
-      return 0d;
-    } else {
-      return n.doubleValue();
-    }
-  }
-
   @Override
   public void exitNullConst(StellarParser.NullConstContext ctx) {
     tokenStack.push(new Token<>(null, Object.class));
@@ -96,38 +91,33 @@ public class StellarCompiler extends StellarBaseListener {
 
   @Override
   public void exitArithExpr_plus(StellarParser.ArithExpr_plusContext ctx) {
-    Token<?> right = popStack();
-    Token<?> left = popStack();
-    Double r = getDouble(right);
-    Double l = getDouble(left);
-    tokenStack.push(new Token<>(l + r, Double.class));
+    Pair<Token<? extends Number>, Token<? extends Number>> p = getArithExpressionPair();
+    tokenStack.push(arithmeticEvaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p));
   }
 
   @Override
   public void exitArithExpr_minus(StellarParser.ArithExpr_minusContext ctx) {
-    Token<?> right = popStack();
-    Token<?> left = popStack();
-    Double r = getDouble(right);
-    Double l = getDouble(left);
-    tokenStack.push(new Token<>(l - r, Double.class));
+    Pair<Token<? extends Number>, Token<? extends Number>> p = getArithExpressionPair();
+    tokenStack.push(arithmeticEvaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), p));
   }
 
   @Override
   public void exitArithExpr_div(StellarParser.ArithExpr_divContext ctx) {
-    Token<?> right = popStack();
-    Token<?> left = popStack();
-    Double r = getDouble(right);
-    Double l = getDouble(left);
-    tokenStack.push(new Token<>(l / r, Double.class));
+    Pair<Token<? extends Number>, Token<? extends Number>> p = getArithExpressionPair();
+    tokenStack.push(arithmeticEvaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p));
   }
 
   @Override
   public void exitArithExpr_mul(StellarParser.ArithExpr_mulContext ctx) {
-    Token<?> right = popStack();
-    Token<?> left = popStack();
-    Double r = getDouble(right);
-    Double l = getDouble(left);
-    tokenStack.push(new Token<>(l * r, Double.class));
+    Pair<Token<? extends Number>, Token<? extends Number>> p = getArithExpressionPair();
+    tokenStack.push(arithmeticEvaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), p));
+  }
+
+  @SuppressWarnings("unchecked")
+  private Pair<Token<? extends Number>, Token<? extends Number>> getArithExpressionPair() {
+    Token<? extends Number> right = (Token<? extends Number>) popStack();
+    Token<? extends Number> left = (Token<? extends Number>) popStack();
+    return Pair.of(left, right);
   }
 
   private void handleConditional() {

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluator.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluator.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluator.java
new file mode 100644
index 0000000..6fd372a
--- /dev/null
+++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluator.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.metron.common.stellar.evaluators;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.metron.common.dsl.ParseException;
+import org.apache.metron.common.dsl.Token;
+
+import java.util.function.BiFunction;
+
+public class ArithmeticEvaluator {
+
+  public Token<? extends Number> evaluate(BiFunction<Number, Number, Token<? extends Number>> function,
+                                          Pair<Token<? extends Number>, Token<? extends Number>> p) {
+    if (p == null || p.getKey() == null || p.getValue() == null) {
+      throw new IllegalArgumentException();
+    }
+
+    final Number l = p.getKey().getValue();
+    final Number r = p.getValue().getValue();
+
+    return function.apply(l == null ? 0 : l, r == null ? 0 : r);
+  }
+
+  /**
+   * This is a helper class that defines how to handle arithmetic operations. The conversion between number
+   * types is taken for the Java spec: http://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.6.2
+   */
+  public static final class ArithmeticEvaluatorFunctions {
+    public static BiFunction<Number, Number, Token<? extends Number>> addition() {
+      return (Number l, Number r) -> {
+        if (l instanceof Double || r instanceof Double) {
+          return new Token<>(l.doubleValue() + r.doubleValue(), Double.class);
+        } else if (l instanceof Float || r instanceof Float) {
+          return new Token<>(l.floatValue() + r.floatValue(), Float.class);
+        } else if (l instanceof Long || r instanceof Long) {
+          return new Token<>(l.longValue() + r.longValue(), Long.class);
+        } else {
+          return new Token<>(l.intValue() + r.intValue(), Integer.class);
+        }
+      };
+    }
+
+    public static BiFunction<Number, Number, Token<? extends Number>> multiplication() {
+      return (Number l, Number r) -> {
+        if (l instanceof Double || r instanceof Double) {
+          return new Token<>(l.doubleValue() * r.doubleValue(), Double.class);
+        } else if (l instanceof Float || r instanceof Float) {
+          return new Token<>(l.floatValue() * r.floatValue(), Float.class);
+        } else if (l instanceof Long || r instanceof Long) {
+          return new Token<>(l.longValue() * r.longValue(), Long.class);
+        } else {
+          return new Token<>(l.intValue() * r.intValue(), Integer.class);
+        }
+      };
+    }
+
+    public static BiFunction<Number, Number, Token<? extends Number>> subtraction() {
+      return (Number l, Number r) -> {
+        if (l instanceof Double || r instanceof Double) {
+          return new Token<>(l.doubleValue() - r.doubleValue(), Double.class);
+        } else if (l instanceof Float || r instanceof Float) {
+          return new Token<>(l.floatValue() - r.floatValue(), Float.class);
+        } else if (l instanceof Long || r instanceof Long) {
+          return new Token<>(l.longValue() - r.longValue(), Long.class);
+        } else {
+          return new Token<>(l.intValue() - r.intValue(), Integer.class);
+        }
+      };
+    }
+
+    public static BiFunction<Number, Number, Token<? extends Number>> division() {
+      return (Number l, Number r) -> {
+        if (l instanceof Double || r instanceof Double) {
+          return new Token<>(l.doubleValue() / r.doubleValue(), Double.class);
+        } else if (l instanceof Float || r instanceof Float) {
+          return new Token<>(l.floatValue() / r.floatValue(), Float.class);
+        } else if (l instanceof Long || r instanceof Long) {
+          return new Token<>(l.longValue() / r.longValue(), Long.class);
+        } else {
+          return new Token<>(l.intValue() / r.intValue(), Integer.class);
+        }
+      };
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/test/java/org/apache/metron/common/dsl/functions/ConversionFunctionsTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/dsl/functions/ConversionFunctionsTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/dsl/functions/ConversionFunctionsTest.java
new file mode 100644
index 0000000..b82ee49
--- /dev/null
+++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/dsl/functions/ConversionFunctionsTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.metron.common.dsl.functions;
+
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+
+public class ConversionFunctionsTest {
+
+  @Test
+  public void conversionFunctionsShouldProperlyConvertToSpecificType() throws Exception {
+    assertEquals(1D, new ConversionFunctions.TO_DOUBLE().apply(Collections.singletonList(1)));
+    assertEquals(1F, new ConversionFunctions.TO_FLOAT().apply(Collections.singletonList(1.0D)));
+    assertEquals(1, new ConversionFunctions.TO_INTEGER().apply(Collections.singletonList(1.0D)));
+    assertEquals(1L, new ConversionFunctions.TO_LONG().apply(Collections.singletonList(1F)));
+  }
+
+  @Test
+  public void conversionFunctionsShouldProperlyHandleNull() throws Exception {
+    assertEquals(null, new ConversionFunctions.TO_DOUBLE().apply(Collections.singletonList(null)));
+    assertEquals(null, new ConversionFunctions.TO_FLOAT().apply(Collections.singletonList(null)));
+    assertEquals(null, new ConversionFunctions.TO_INTEGER().apply(Collections.singletonList(null)));
+    assertEquals(null, new ConversionFunctions.TO_LONG().apply(Collections.singletonList(null)));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarArithmeticTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarArithmeticTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarArithmeticTest.java
new file mode 100644
index 0000000..8f65a6f
--- /dev/null
+++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarArithmeticTest.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.metron.common.stellar;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.metron.common.dsl.Token;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.metron.common.utils.StellarProcessorUtils.run;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SuppressWarnings("unchecked")
+public class StellarArithmeticTest {
+
+  @Test
+  public void addingLongsShouldYieldLong() throws Exception {
+    final long timestamp = 1452013350000L;
+    String query = "TO_EPOCH_TIMESTAMP('2016-01-05 17:02:30', 'yyyy-MM-dd HH:mm:ss', 'UTC') + 2";
+    Assert.assertEquals(timestamp + 2, run(query, new HashMap<>()));
+  }
+
+  @Test
+  public void addingIntegersShouldYieldAnInteger() throws Exception {
+    String query = "1 + 2";
+    Assert.assertEquals(3, run(query, new HashMap<>()));
+  }
+
+  @Test
+  public void addingDoublesShouldYieldADouble() throws Exception {
+    String query = "1.0 + 2.0";
+    Assert.assertEquals(3.0, run(query, new HashMap<>()));
+  }
+
+  @Test
+  public void addingDoubleAndIntegerWhereSubjectIsDoubleShouldYieldADouble() throws Exception {
+    String query = "2.1 + 1";
+    Assert.assertEquals(3.1, run(query, new HashMap<>()));
+  }
+
+  @Test
+  public void addingDoubleAndIntegerWhereSubjectIsIntegerShouldYieldADouble() throws Exception {
+    String query = "1 + 2.1";
+    Assert.assertEquals(3.1, run(query, new HashMap<>()));
+  }
+
+  @Test
+  public void testArithmetic() {
+    {
+      String query = "1 + 2";
+      Assert.assertEquals(3, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
+    }
+    {
+      String query = "1.2 + 2";
+      Assert.assertEquals(3.2, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
+    }
+    {
+      String query = "1.2e-3 + 2";
+      Assert.assertEquals(1.2e-3 + 2, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
+    }
+  }
+
+  @Test
+  public void testNumericOperations() {
+    {
+      String query = "TO_INTEGER(1 + 2*2 + 3 - 4 - 0.5)";
+      Assert.assertEquals(3, (Integer) run(query, new HashMap<>()), 1e-6);
+    }
+    {
+      String query = "1 + 2*2 + 3 - 4 - 0.5";
+      Assert.assertEquals(3.5, (Double) run(query, new HashMap<>()), 1e-6);
+    }
+    {
+      String query = "2*one*(1 + 2*2 + 3 - 4)";
+      Assert.assertEquals(8, run(query, ImmutableMap.of("one", 1)));
+    }
+    {
+      String query = "2*(1 + 2 + 3 - 4)";
+      Assert.assertEquals(4, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+    }
+    {
+      String query = "1 + 2 + 3 - 4 - 2";
+      Assert.assertEquals(0, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+    }
+    {
+      String query = "1 + 2 + 3 + 4";
+      Assert.assertEquals(10, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+    }
+    {
+      String query = "(one + 2)*3";
+      Assert.assertEquals(9, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+    }
+    {
+      String query = "TO_INTEGER((one + 2)*3.5)";
+      Assert.assertEquals(10, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+    }
+    {
+      String query = "1 + 2*3";
+      Assert.assertEquals(7, (Integer) run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
+    }
+    {
+      String query = "TO_LONG(foo)";
+      Assert.assertNull(run(query,ImmutableMap.of("foo","not a number")));
+    }
+    {
+      String query = "TO_LONG(foo)";
+      Assert.assertEquals(232321L,run(query,ImmutableMap.of("foo","00232321")));
+    }
+    {
+      String query = "TO_LONG(foo)";
+      Assert.assertEquals(Long.MAX_VALUE,run(query,ImmutableMap.of("foo", Long.toString(Long.MAX_VALUE))));
+    }
+  }
+
+  @Test
+  public void verifyExpectedReturnTypes() throws Exception {
+    Token<Integer> integer = mock(Token.class);
+    when(integer.getValue()).thenReturn(1);
+
+    Token<Long> lng = mock(Token.class);
+    when(lng.getValue()).thenReturn(1L);
+
+    Token<Double> dbl = mock(Token.class);
+    when(dbl.getValue()).thenReturn(1.0D);
+
+    Token<Float> flt = mock(Token.class);
+    when(flt.getValue()).thenReturn(1.0F);
+
+    Map<Pair<String, String>, Class<? extends Number>> expectedReturnTypeMappings =
+        new HashMap<Pair<String, String>, Class<? extends Number>>() {{
+          put(Pair.of("TO_FLOAT(3.0)", "TO_LONG(1)"), Float.class);
+          put(Pair.of("TO_FLOAT(3)", "3.0"), Double.class);
+          put(Pair.of("TO_FLOAT(3)", "TO_FLOAT(3)"), Float.class);
+          put(Pair.of("TO_FLOAT(3)", "3"), Float.class);
+
+          put(Pair.of("TO_LONG(1)", "TO_LONG(1)"), Long.class);
+          put(Pair.of("TO_LONG(1)", "3.0"), Double.class);
+          put(Pair.of("TO_LONG(1)", "TO_FLOAT(3)"), Float.class);
+          put(Pair.of("TO_LONG(1)", "3"), Long.class);
+
+          put(Pair.of("3.0", "TO_LONG(1)"), Double.class);
+          put(Pair.of("3.0", "3.0"), Double.class);
+          put(Pair.of("3.0", "TO_FLOAT(3)"), Double.class);
+          put(Pair.of("3.0", "3"), Double.class);
+
+          put(Pair.of("3", "TO_LONG(1)"), Long.class);
+          put(Pair.of("3", "3.0"), Double.class);
+          put(Pair.of("3", "TO_FLOAT(3)"), Float.class);
+          put(Pair.of("3", "3"), Integer.class);
+        }};
+
+    expectedReturnTypeMappings.forEach( (pair, expectedClass) -> {
+      assertTrue(run(pair.getLeft() + " * " + pair.getRight(), ImmutableMap.of()).getClass() == expectedClass);
+      assertTrue(run(pair.getLeft() + " + " + pair.getRight(), ImmutableMap.of()).getClass() == expectedClass);
+      assertTrue(run(pair.getLeft() + " - " + pair.getRight(), ImmutableMap.of()).getClass() == expectedClass);
+      assertTrue(run(pair.getLeft() + " / " + pair.getRight(), ImmutableMap.of()).getClass() == expectedClass);
+    });
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarTest.java
index 921fb7c..1f68e5d 100644
--- a/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarTest.java
+++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/StellarTest.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,22 +18,12 @@
 
 package org.apache.metron.common.stellar;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import org.apache.commons.collections.map.HashedMap;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.metron.common.dsl.ParseException;
 import org.apache.metron.common.dsl.Stellar;
 import org.apache.metron.common.dsl.StellarFunction;
-import org.apache.metron.common.dsl.Context;
-import org.apache.metron.common.dsl.MapVariableResolver;
-import org.apache.metron.common.dsl.ParseException;
-import org.apache.metron.common.dsl.Stellar;
-import org.apache.metron.common.dsl.StellarFunction;
-import org.apache.metron.common.dsl.StellarFunctions;
-import org.apache.metron.common.dsl.VariableResolver;
-import org.apache.metron.common.utils.SerDeUtils;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -49,9 +39,8 @@ import java.util.HashSet;
 import java.util.Map;
 
 import static org.apache.metron.common.dsl.functions.resolver.ClasspathFunctionResolver.effectiveClassPathUrls;
-import static java.util.function.Function.identity;
-import static org.apache.metron.common.utils.StellarProcessorUtils.runPredicate;
 import static org.apache.metron.common.utils.StellarProcessorUtils.run;
+import static org.apache.metron.common.utils.StellarProcessorUtils.runPredicate;
 
 public class StellarTest {
 
@@ -90,24 +79,24 @@ public class StellarTest {
   @Test
   public void testIfThenElseBug1() {
     String query = "50 + (true == true ? 10 : 20)";
-    Assert.assertEquals(60.0, run(query, new HashMap<>()));
+    Assert.assertEquals(60, run(query, new HashMap<>()));
   }
 
   @Test
   public void testIfThenElseBug2() {
     String query = "50 + (true == false ? 10 : 20)";
-    Assert.assertEquals(70.0, run(query, new HashMap<>()));
+    Assert.assertEquals(70, run(query, new HashMap<>()));
   }
 
   @Test
   public void testIfThenElseBug3() {
     String query = "50 * (true == false ? 2 : 10) + 20";
-    Assert.assertEquals(520.0, run(query, new HashMap<>()));
+    Assert.assertEquals(520, run(query, new HashMap<>()));
   }
 
   @Test
   public void testIfThenElseBug4() {
-    String query = "TO_INTEGER(true == true ? 10 : 20 )";
+    String query = "TO_INTEGER(true == true ? 10.0 : 20.0 )";
     Assert.assertEquals(10, run(query, new HashMap<>()));
   }
 
@@ -191,22 +180,6 @@ public class StellarTest {
   }
 
   @Test
-  public void testArithmetic() {
-    {
-      String query = "1 + 2";
-      Assert.assertEquals(3, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
-    }
-    {
-      String query = "1.2 + 2";
-      Assert.assertEquals(3.2, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
-    }
-    {
-      String query = "1.2e-3 + 2";
-      Assert.assertEquals(1.2e-3 + 2, ((Number)run(query, new HashMap<>())).doubleValue(), 1e-3);
-    }
-  }
-
-  @Test
   public void testIfThenElse() {
     {
       String query = "if STARTS_WITH(casey, 'case') then 'one' else 'two'";
@@ -251,62 +224,11 @@ public class StellarTest {
     }
     {
       String query = "1 < 2 ? one*3 : 'two'";
-      Assert.assertTrue(Math.abs(3.0 - (double)run(query, ImmutableMap.of("one", 1))) < 1e-6);
+      Assert.assertTrue(Math.abs(3 - (int) run(query, ImmutableMap.of("one", 1))) < 1e-6);
     }
   }
 
   @Test
-  public void testNumericOperations() {
-    {
-      String query = "TO_INTEGER(1 + 2*2 + 3 - 4 - 0.5)";
-      Assert.assertEquals(3, (Integer)run(query, new HashMap<>()), 1e-6);
-    }
-    {
-      String query = "1 + 2*2 + 3 - 4 - 0.5";
-      Assert.assertEquals(3.5, (Double)run(query, new HashMap<>()), 1e-6);
-    }
-    {
-      String query = "2*one*(1 + 2*2 + 3 - 4)";
-      Assert.assertEquals(8, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
-    }
-    {
-      String query = "2*(1 + 2 + 3 - 4)";
-      Assert.assertEquals(4, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
-    }
-    {
-      String query = "1 + 2 + 3 - 4 - 2";
-      Assert.assertEquals(0, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
-    }
-    {
-      String query = "1 + 2 + 3 + 4";
-      Assert.assertEquals(10, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
-    }
-    {
-      String query = "(one + 2)*3";
-      Assert.assertEquals(9, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
-    }
-    {
-      String query = "TO_INTEGER((one + 2)*3.5)";
-      Assert.assertEquals(10, (Integer)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
-    }
-    {
-      String query = "1 + 2*3";
-      Assert.assertEquals(7, (Double)run(query, ImmutableMap.of("one", 1, "very_nearly_one", 1.000001)), 1e-6);
-    }
-    {
-      String query = "TO_LONG(foo)";
-      Assert.assertNull(run(query,ImmutableMap.of("foo","not a number")));
-    }
-    {
-      String query = "TO_LONG(foo)";
-      Assert.assertEquals(232321L,run(query,ImmutableMap.of("foo","00232321")));
-    }
-    {
-      String query = "TO_LONG(foo)";
-      Assert.assertEquals(Long.MAX_VALUE,run(query,ImmutableMap.of("foo", Long.toString(Long.MAX_VALUE))));
-    }
-  }
-  @Test
   public void testHappyPath() {
     String query = "TO_UPPER(TRIM(foo))";
     Assert.assertEquals("CASEY", run(query, ImmutableMap.of("foo", "casey ")));
@@ -626,5 +548,4 @@ public class StellarTest {
     thrown.expectMessage("The rule 'TO_UPPER(protocol)' does not return a boolean value.");
     runPredicate("TO_UPPER(protocol)", v -> variableMap.get(v));
   }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/b36f4b2f/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluatorTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluatorTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluatorTest.java
new file mode 100644
index 0000000..042f9ac
--- /dev/null
+++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/stellar/evaluators/ArithmeticEvaluatorTest.java
@@ -0,0 +1,412 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.metron.common.stellar.evaluators;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.metron.common.dsl.Token;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SuppressWarnings("unchecked")
+public class ArithmeticEvaluatorTest {
+  ArithmeticEvaluator evaluator = new ArithmeticEvaluator();
+
+  @Test
+  public void evaluateDoubleShouldReturnDoubleAdd() throws Exception {
+    Token<Integer> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1);
+
+    Token<Double> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2D);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+    assertTrue(evaluated.getValue() instanceof Double);
+    assertEquals(3.0D, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateIntegerShouldReturnIntegerAdd() throws Exception {
+    Token<Integer> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+    assertTrue(evaluated.getValue() instanceof Integer);
+    assertEquals(3, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateFloatsShouldReturnFloatAdd() throws Exception {
+    Token<Float> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1F);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+    assertTrue(evaluated.getValue() instanceof Float);
+    assertEquals(3F, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateLongsShouldReturnLongAdd() throws Exception {
+    Token<Long> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1L);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+    assertTrue(evaluated.getValue() instanceof Long);
+    assertEquals(3L, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateIntegerShouldReturnDoubleMul() throws Exception {
+    Token<Integer> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1);
+
+    Token<Double> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2D);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), p);
+
+    assertTrue(evaluated.getValue() instanceof Double);
+    assertEquals(2.0D, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateIntegerShouldReturnIntegerMul() throws Exception {
+    Token<Integer> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), p);
+
+    assertTrue(evaluated.getValue() instanceof Integer);
+    assertEquals(2, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateFloatsShouldReturnFloatMul() throws Exception {
+    Token<Float> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1F);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), p);
+
+    assertTrue(evaluated.getValue() instanceof Float);
+    assertEquals(2F, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateLongsShouldReturnLongMul() throws Exception {
+    Token<Long> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1L);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), p);
+
+    assertTrue(evaluated.getValue() instanceof Long);
+    assertEquals(2L, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateDoubleShouldReturnDoubleSub() throws Exception {
+    Token<Integer> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1);
+
+    Token<Double> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2D);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), p);
+
+    assertTrue(evaluated.getValue() instanceof Double);
+    assertEquals(-1.0D, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateIntegerShouldReturnIntegerSub() throws Exception {
+    Token<Integer> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), p);
+
+    assertTrue(evaluated.getValue() instanceof Integer);
+    assertEquals(-1, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateFloatsShouldReturnFloatSub() throws Exception {
+    Token<Float> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1F);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), p);
+
+    assertTrue(evaluated.getValue() instanceof Float);
+    assertEquals(-1F, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateLongsShouldReturnLongSub() throws Exception {
+    Token<Long> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1L);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), p);
+
+    assertTrue(evaluated.getValue() instanceof Long);
+    assertEquals(-1L, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateDoubleShouldReturnDoubleDiv() throws Exception {
+    Token<Integer> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1);
+
+    Token<Double> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2D);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+
+    assertTrue(evaluated.getValue() instanceof Double);
+    assertEquals(1 / 2D, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateIntegerShouldReturnIntegerDiv() throws Exception {
+    Token<Integer> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+
+    assertTrue(evaluated.getValue() instanceof Integer);
+    assertEquals(1 / 2, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateFloatsShouldReturnFloatDiv() throws Exception {
+    Token<Float> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1F);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+
+    assertTrue(evaluated.getValue() instanceof Float);
+    assertEquals(0.5F, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateLongsShouldReturnLongDiv() throws Exception {
+    Token<Long> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1L);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+
+    assertTrue(evaluated.getValue() instanceof Long);
+    assertEquals(0L, evaluated.getValue());
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void evaluateShouldThroughIllegalArgumentExceptionWhenInputIsNull() throws Exception {
+    evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), null);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void evaluateShouldThroughIllegalArgumentExceptionWhenInputsKeyIsNull() throws Exception {
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(null, mock(Token.class));
+    evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void evaluateShouldThroughIllegalArgumentExceptionWhenInputsValueIsNull() throws Exception {
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(mock(Token.class), null);
+    evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), p);
+  }
+
+  @Test
+  public void evaluateShouldConvertShortsToIntegersType() throws Exception {
+    Token<Short> l = mock(Token.class);
+    when(l.getValue()).thenReturn((short) 2);
+
+    Token<Short> r = mock(Token.class);
+    when(r.getValue()).thenReturn((short) 3);
+
+    Token<? extends Number> evaluated0 = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), Pair.of(l, r));
+    Token<? extends Number> evaluated1 = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), Pair.of(l, r));
+    Token<? extends Number> evaluated2 = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), Pair.of(l, r));
+    Token<? extends Number> evaluated3 = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), Pair.of(l, r));
+
+    assertTrue(evaluated0.getValue() instanceof Integer);
+    assertEquals(5, evaluated0.getValue());
+
+    assertTrue(evaluated1.getValue() instanceof Integer);
+    assertEquals(-1, evaluated1.getValue());
+
+    assertTrue(evaluated2.getValue() instanceof Integer);
+    assertEquals(6, evaluated2.getValue());
+
+    assertTrue(evaluated3.getValue() instanceof Integer);
+    assertEquals(0, evaluated3.getValue());
+  }
+
+  @Test
+  public void evaluateIntegerShouldReturnIntegerWhenLeftsValueIsNull() throws Exception {
+    Token<Integer> l = mock(Token.class);
+    when(l.getValue()).thenReturn(null);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(2);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+    assertTrue(evaluated.getValue() instanceof Integer);
+    assertEquals(2, evaluated.getValue());
+  }
+
+  @Test
+  public void evaluateIntegerShouldReturnIntegerWhenRightsValueIsNull() throws Exception {
+    Token<Integer> l = mock(Token.class);
+    when(l.getValue()).thenReturn(1);
+
+    Token<Integer> r = mock(Token.class);
+    when(r.getValue()).thenReturn(null);
+
+    Pair<Token<? extends Number>, Token<? extends Number>> p = Pair.of(l, r);
+
+    Token<? extends Number> evaluated = evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), p);
+
+    assertTrue(evaluated.getValue() instanceof Integer);
+    assertEquals(1, evaluated.getValue());
+  }
+
+  @Test
+  public void verifyExpectedReturnTypes() throws Exception {
+    Token<Integer> integer = mock(Token.class);
+    when(integer.getValue()).thenReturn(1);
+
+    Token<Long> lng = mock(Token.class);
+    when(lng.getValue()).thenReturn(1L);
+
+    Token<Double> dbl = mock(Token.class);
+    when(dbl.getValue()).thenReturn(1.0D);
+
+    Token<Float> flt = mock(Token.class);
+    when(flt.getValue()).thenReturn(1.0F);
+
+    Map<Pair<Token<? extends Number>, Token<? extends Number>>, Class<? extends Number>> expectedReturnTypeMappings =
+        new HashMap<Pair<Token<? extends Number>, Token<? extends Number>>, Class<? extends Number>>() {{
+          put(Pair.of(flt, lng), Float.class);
+          put(Pair.of(flt, dbl), Double.class);
+          put(Pair.of(flt, flt), Float.class);
+          put(Pair.of(flt, integer), Float.class);
+
+          put(Pair.of(lng, lng), Long.class);
+          put(Pair.of(lng, dbl), Double.class);
+          put(Pair.of(lng, flt), Float.class);
+          put(Pair.of(lng, integer), Long.class);
+
+          put(Pair.of(dbl, lng), Double.class);
+          put(Pair.of(dbl, dbl), Double.class);
+          put(Pair.of(dbl, flt), Double.class);
+          put(Pair.of(dbl, integer), Double.class);
+
+          put(Pair.of(integer, lng), Long.class);
+          put(Pair.of(integer, dbl), Double.class);
+          put(Pair.of(integer, flt), Float.class);
+          put(Pair.of(integer, integer), Integer.class);
+    }};
+
+    expectedReturnTypeMappings.forEach( (pair, expectedClass) -> {
+      assertTrue(evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.addition(), pair).getValue().getClass() == expectedClass);
+      assertTrue(evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.division(), pair).getValue().getClass() == expectedClass);
+      assertTrue(evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.subtraction(), pair).getValue().getClass() == expectedClass);
+      assertTrue(evaluator.evaluate(ArithmeticEvaluator.ArithmeticEvaluatorFunctions.multiplication(), pair).getValue().getClass() == expectedClass);
+    });
+  }
+}