Repository: metron Updated Branches: refs/heads/master 27ab28923 -> b2375a1f1
METRON-1038: Stellar should have a better collection of basic math operations closes apache/incubator-metron#650 Project: http://git-wip-us.apache.org/repos/asf/metron/repo Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/b2375a1f Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/b2375a1f Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/b2375a1f Branch: refs/heads/master Commit: b2375a1f1c66b19dc6a82cbcc8242741cdf65451 Parents: 27ab289 Author: cstella <ceste...@gmail.com> Authored: Thu Aug 3 10:17:59 2017 +0100 Committer: cstella <ceste...@gmail.com> Committed: Thu Aug 3 10:17:59 2017 +0100 ---------------------------------------------------------------------- metron-stellar/stellar-common/README.md | 185 +++++++++++++------ .../common/utils/math/MathOperation.java | 45 +++++ .../common/utils/math/MathOperations.java | 83 +++++++++ .../common/utils/math/StellarMathFunction.java | 64 +++++++ .../stellar/dsl/functions/MathFunctions.java | 166 +++++++++++++++-- .../dsl/functions/MathFunctionsTest.java | 162 +++++++++++++++- 6 files changed, 631 insertions(+), 74 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/metron/blob/b2375a1f/metron-stellar/stellar-common/README.md ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/README.md b/metron-stellar/stellar-common/README.md index 2b5d4b6..f12c77d 100644 --- a/metron-stellar/stellar-common/README.md +++ b/metron-stellar/stellar-common/README.md @@ -101,58 +101,65 @@ In the core language functions, we support basic functional programming primitiv ## Stellar Core Functions -| | -| ---------- | -| [ `ABS`](../../metron-analytics/metron-statistics#abs) | -| [ `APPEND_IF_MISSING`](#append_if_missing) | -| [ `BIN`](../../metron-analytics/metron-statistics#bin) | -| [ `BLOOM_ADD`](#bloom_add) | -| [ `BLOOM_EXISTS`](#bloom_exists) | -| [ `BLOOM_INIT`](#bloom_init) | -| [ `BLOOM_MERGE`](#bloom_merge) | -| [ `CHOP`](#chop) | -| [ `CHOMP`](#chomp) | -| [ `COUNT_MATCHES`](#count_matches) | -| [ `DAY_OF_MONTH`](#day_of_month) | -| [ `DAY_OF_WEEK`](#day_of_week) | -| [ `DAY_OF_YEAR`](#day_of_year) | -| [ `DECODE`](#decode) | -| [ `DOMAIN_REMOVE_SUBDOMAINS`](#domain_remove_subdomains) | -| [ `DOMAIN_REMOVE_TLD`](#domain_remove_tld) | -| [ `DOMAIN_TO_TLD`](#domain_to_tld) | -| [ `ENCODE`](#encode) | -| [ `ENDS_WITH`](#ends_with) | -| [ `ENRICHMENT_EXISTS`](#enrichment_exists) | -| [ `ENRICHMENT_GET`](#enrichment_get) | -| [ `FILL_LEFT`](#fill_left) | -| [ `FILL_RIGHT`](#fill_right) | -| [ `FILTER`](#filter) | -| [ `FORMAT`](#format) | -| [ `HLLP_CARDINALITY`](../../metron-analytics/metron-statistics#hllp_cardinality) | -| [ `HLLP_INIT`](../../metron-analytics/metron-statistics#hllp_init) | -| [ `HLLP_MERGE`](../../metron-analytics/metron-statistics#hllp_merge) | -| [ `HLLP_OFFER`](../../metron-analytics/metron-statistics#hllp_offer) | -| [ `GEO_GET`](#geo_get) | -| [ `GET`](#get) | -| [ `GET_FIRST`](#get_first) | -| [ `GET_LAST`](#get_last) | -| [ `GET_SUPPORTED_ENCODINGS`](#get_supported_encodings) | -| [ `IN_SUBNET`](#in_subnet) | -| [ `IS_DATE`](#is_date) | -| [ `IS_ENCODING`](#is_encoding) | -| [ `IS_DOMAIN`](#is_domain) | -| [ `IS_EMAIL`](#is_email) | -| [ `IS_EMPTY`](#is_empty) | -| [ `IS_INTEGER`](#is_integer) | -| [ `IS_IP`](#is_ip) | -| [ `IS_URL`](#is_url) | -| [ `JOIN`](#join) | -| [ `KAFKA_GET`](#kafka_get) | -| [ `KAFKA_PROPS`](#kafka_props) | -| [ `KAFKA_PUT`](#kafka_put) | -| [ `KAFKA_TAIL`](#kafka_tail) | -| [ `LENGTH`](#length) | -| [ `LIST_ADD`](#list_add) | +| | +| ---------- | +| [ `ABS`](../../metron-analytics/metron-statistics#abs) | +| [ `APPEND_IF_MISSING`](#append_if_missing) | +| [ `BIN`](../../metron-analytics/metron-statistics#bin) | +| [ `BLOOM_ADD`](#bloom_add) | +| [ `BLOOM_EXISTS`](#bloom_exists) | +| [ `BLOOM_INIT`](#bloom_init) | +| [ `BLOOM_MERGE`](#bloom_merge) | +| [ `CEILING`](#ceiling) | +| [ `COS`](#cos) | +| [ `CHOP`](#chop) | +| [ `CHOMP`](#chomp) | +| [ `COUNT_MATCHES`](#count_matches) | +| [ `DAY_OF_MONTH`](#day_of_month) | +| [ `DAY_OF_WEEK`](#day_of_week) | +| [ `DAY_OF_YEAR`](#day_of_year) | +| [ `DECODE`](#decode) | +| [ `DOMAIN_REMOVE_SUBDOMAINS`](#domain_remove_subdomains) | +| [ `DOMAIN_REMOVE_TLD`](#domain_remove_tld) | +| [ `DOMAIN_TO_TLD`](#domain_to_tld) | +| [ `ENCODE`](#encode) | +| [ `ENDS_WITH`](#ends_with) | +| [ `ENRICHMENT_EXISTS`](#enrichment_exists) | +| [ `ENRICHMENT_GET`](#enrichment_get) | +| [ `EXP`](#exp) | +| [ `FILL_LEFT`](#fill_left) | +| [ `FILL_RIGHT`](#fill_right) | +| [ `FILTER`](#filter) | +| [ `FLOOR`](#floor) | +| [ `FORMAT`](#format) | +| [ `HLLP_CARDINALITY`](../../metron-analytics/metron-statistics#hllp_cardinality) | +| [ `HLLP_INIT`](../../metron-analytics/metron-statistics#hllp_init) | +| [ `HLLP_MERGE`](../../metron-analytics/metron-statistics#hllp_merge) | +| [ `HLLP_OFFER`](../../metron-analytics/metron-statistics#hllp_offer) | +| [ `GEO_GET`](#geo_get) | +| [ `GET`](#get) | +| [ `GET_FIRST`](#get_first) | +| [ `GET_LAST`](#get_last) | +| [ `GET_SUPPORTED_ENCODINGS`](#get_supported_encodings) | +| [ `IN_SUBNET`](#in_subnet) | +| [ `IS_DATE`](#is_date) | +| [ `IS_ENCODING`](#is_encoding) | +| [ `IS_DOMAIN`](#is_domain) | +| [ `IS_EMAIL`](#is_email) | +| [ `IS_EMPTY`](#is_empty) | +| [ `IS_INTEGER`](#is_integer) | +| [ `IS_IP`](#is_ip) | +| [ `IS_URL`](#is_url) | +| [ `JOIN`](#join) | +| [ `KAFKA_GET`](#kafka_get) | +| [ `KAFKA_PROPS`](#kafka_props) | +| [ `KAFKA_PUT`](#kafka_put) | +| [ `KAFKA_TAIL`](#kafka_tail) | +| [ `LENGTH`](#length) | +| [ `LIST_ADD`](#list_add) | +| [ `LOG2`](#log2) | +| [ `LOG10`](#log10) | +| [ `LN`](#ln) | | [ `MAAS_GET_ENDPOINT`](#maas_get_endpoint) | | [ `MAAS_MODEL_APPLY`](#maas_model_apply) | | [ `MAP`](#map) | @@ -166,7 +173,10 @@ In the core language functions, we support basic functional programming primitiv | [ `REDUCE`](#reduce) | | [ `REGEXP_MATCH`](#regexp_match) | | [ `REGEXP_GROUP_VAL`](#regexp_group_val) | +| [ `ROUND`](#round) | | [ `SPLIT`](#split) | +| [ `SIN`](#sin) | +| [ `SQRT`](#sqrt) | | [ `STARTS_WITH`](#starts_with) | | [ `STATS_ADD`](../../metron-analytics/metron-statistics#stats_add) | | [ `STATS_BIN`](../../metron-analytics/metron-statistics#stats_bin) | @@ -190,6 +200,7 @@ In the core language functions, we support basic functional programming primitiv | [ `STRING_ENTROPY`](#string_entropy) | | [ `SYSTEM_ENV_GET`](#system_env_get) | | [ `SYSTEM_PROPERTY_GET`](#system_property_get) | +| [ `TAN`](#tan) | | [ `TO_DOUBLE`](#to_double) | | [ `TO_EPOCH_TIMESTAMP`](#to_epoch_timestamp) | | [ `TO_FLOAT`](#to_float) | @@ -244,6 +255,18 @@ In the core language functions, we support basic functional programming primitiv * bloomfilters - A list of bloom filters to merge * Returns: Bloom Filter or null if the list is empty +### `CEILING` + * Description: Returns the ceiling of a number. + * Input: + * number - The number to take the ceiling of + * Returns: The ceiling of the number passed in. + +### `COS` + * Description: Returns the cosine of a number. + * Input: + * number - The number to take the cosine of + * Returns: The cosine of the number passed in. + ### `CHOP` * Description: Remove the last character from a String * Input: @@ -347,6 +370,12 @@ In the core language functions, we support basic functional programming primitiv * column_family - The Column Family to use * Returns: A Map associated with the indicator and enrichment type. Empty otherwise. +### `EXP` + * Description: Returns Euler's number raised to the power of the argument. + * Input: + * number - The power to which e is raised. + * Returns: Euler's number raised to the power of the argument. + ### `FILL_LEFT` * Description: Fills or pads a given string with a given character, to a given length on the left * Input: @@ -370,6 +399,12 @@ In the core language functions, we support basic functional programming primitiv * predicate - The lambda expression to apply. This expression is assumed to take one argument and return a boolean. * Returns: The input list filtered by the predicate. +### `FLOOR` + * Description: Returns the floor of a number. + * Input: + * number - The number to take the floor of + * Returns: The floor of the number passed in. + ### `FORMAT` * Description: Returns a formatted string using the specified format string and arguments. Uses Java's string formatting conventions. * Input: @@ -510,11 +545,29 @@ In the core language functions, we support basic functional programming primitiv * list - List to add element to. * element - Element to add to list * Returns: Resulting list with the item added at the end. - + ### `GET_SUPPORTED_ENCODINGS` * Description: Returns a list of the encodings that are currently supported. * Returns: A List of String - + +### `LOG2` + * Description: Returns the log (base `2`) of a number. + * Input: + * number - The number to take the log (base `2`) of + * Returns: The log (base `2`) of the number passed in. + +### `LOG10` + * Description: Returns the log (base `10`) of a number. + * Input: + * number - The number to take the log (base `10`) of + * Returns: The log (base `10`) of the number passed in. + +### `LN` + * Description: Returns the natural log of a number. + * Input: + * number - The number to take the natural log of + * Returns: The natural log of the number passed in. + ### `MAAS_GET_ENDPOINT` * Description: Inspects ZooKeeper and returns a map containing the name, version and url for the model referred to by the input parameters. * Input: @@ -621,6 +674,24 @@ In the core language functions, we support basic functional programming primitiv * group - The integer that selects what group to select, starting at 1 * Returns: The value of the group, or null if not matched or no group at index. +### `ROUND` + * Description: Rounds a number to the nearest integer. This is half-up rounding. + * Input: + * number - The number to round + * Returns: The nearest integer (based on half-up rounding). + +### `SIN` + * Description: Returns the sine of a number. + * Input: + * number - The number to take the sine of + * Returns: The sine of the number passed in. + +### `SQRT` + * Description: Returns the square root of a number. + * Input: + * number - The number to take the square root of + * Returns: The square root of the number passed in. + ### `STRING_ENTROPY` * Description: Computes the base-2 shannon entropy of a string. * Input: @@ -653,6 +724,12 @@ In the core language functions, we support basic functional programming primitiv * key - Property to get the value for * Returns: String +### `TAN` + * Description: Returns the tangent of a number. + * Input: + * number - The number to take the tangent of + * Returns: The tangent of the number passed in. + ### `TO_DOUBLE` * Description: Transforms the first argument to a double precision number * Input: http://git-wip-us.apache.org/repos/asf/metron/blob/b2375a1f/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/MathOperation.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/MathOperation.java b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/MathOperation.java new file mode 100644 index 0000000..803d130 --- /dev/null +++ b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/MathOperation.java @@ -0,0 +1,45 @@ +/* + * + * 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.stellar.common.utils.math; + +import java.util.function.Function; + +public class MathOperation { + private int maxArgs; + private int minArgs; + private Function<Number[], Number> operation; + public MathOperation(Function<Number[], Number> operation, int minArgs, int maxArgs) { + this.operation = operation; + this.maxArgs = maxArgs; + this.minArgs = minArgs; + } + + public int getMaxArgs() { + return maxArgs; + } + + public int getMinArgs() { + return minArgs; + } + + public Function<Number[], Number> getOperation() { + return operation; + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/b2375a1f/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/MathOperations.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/MathOperations.java b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/MathOperations.java new file mode 100644 index 0000000..bccdc00 --- /dev/null +++ b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/MathOperations.java @@ -0,0 +1,83 @@ +/* + * + * 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.stellar.common.utils.math; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public enum MathOperations implements Function<Number[], Number> { + ABS(d -> Math.abs(d)), + CEIL(d -> Math.ceil(d)), + COS(d -> Math.cos(d)), + FLOOR(d -> Math.floor(d)), + LOG10(d -> Math.log10(d)), + LOG2(d -> Math.log(d)/Math.log(2)), + LN(d -> Math.log(d)), + SIN(d -> Math.sin(d)), + SQRT(d -> Math.sqrt(d)), + TAN(d -> Math.tan(d)), + EXP(d -> Math.exp(d)), + ROUND(new MathOperation(d -> { + Double val = d[0].doubleValue(); + return Double.isNaN(val)?Double.NaN:Math.round(d[0].doubleValue()); + }, 1, 1)), + ; + + private static class SingleOpFunc implements Function<Number[], Number> { + Function<Double, Number> f; + public SingleOpFunc(Function<Double, Number> f) { + this.f = f; + } + @Override + public Number apply(Number[] numbers) { + return f.apply(numbers[0].doubleValue()); + } + } + + private static class BinaryOpFunc implements Function<Number[], Number> { + BiFunction<Double, Double, Number> f; + public BinaryOpFunc(BiFunction<Double, Double, Number> f) { + this.f = f; + } + @Override + public Number apply(Number[] numbers) { + return f.apply(numbers[0].doubleValue(), numbers[1].doubleValue()); + } + } + + MathOperation op; + MathOperations(Function<Double, Number> singleArg) { + op = new MathOperation(new SingleOpFunc(singleArg), 1, 1); + } + + MathOperations(BiFunction<Double, Double, Number> binaryArg) { + op = new MathOperation(new BinaryOpFunc(binaryArg), 2, 2); + } + + MathOperations(MathOperation op) + { + this.op = op; + } + + @Override + public Number apply(Number[] in) { + return op.getOperation().apply(in); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/b2375a1f/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/StellarMathFunction.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/StellarMathFunction.java b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/StellarMathFunction.java new file mode 100644 index 0000000..0f9eb16 --- /dev/null +++ b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/math/StellarMathFunction.java @@ -0,0 +1,64 @@ +/* + * + * 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.stellar.common.utils.math; + +import org.apache.metron.stellar.dsl.Context; +import org.apache.metron.stellar.dsl.ParseException; +import org.apache.metron.stellar.dsl.StellarFunction; + +import java.util.List; + +public class StellarMathFunction implements StellarFunction { + MathOperation _func; + public StellarMathFunction(MathOperations _func) { + this._func = _func.op; + } + + public StellarMathFunction(MathOperation _func) { + this._func = _func; + } + + @Override + public Object apply(List<Object> args, Context context) throws ParseException { + if(args.size() < _func.getMinArgs()) { + return Double.NaN; + } + Number[] nums = new Number[_func.getMaxArgs()]; + for(int i = 0; i < _func.getMaxArgs();++i) { + nums[i] = (Number)args.get(i); + if(nums[i] == null) { + return Double.NaN; + } + } + + Object ret = _func.getOperation().apply(nums); + return ret; + } + + @Override + public void initialize(Context context) { + + } + + @Override + public boolean isInitialized() { + return true; + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/b2375a1f/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/MathFunctions.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/MathFunctions.java b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/MathFunctions.java index 5583ba2..a5ac366 100644 --- a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/MathFunctions.java +++ b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/MathFunctions.java @@ -19,15 +19,19 @@ */ package org.apache.metron.stellar.dsl.functions; +import org.apache.metron.stellar.common.utils.math.MathOperations; +import org.apache.metron.stellar.common.utils.math.StellarMathFunction; import org.apache.metron.stellar.dsl.Context; import org.apache.metron.stellar.dsl.ParseException; import org.apache.metron.stellar.dsl.Stellar; import org.apache.metron.stellar.dsl.StellarFunction; import java.util.List; +import java.util.function.Function; public class MathFunctions { + @Stellar(name="ABS" ,description="Returns the absolute value of a number." ,params = { @@ -35,29 +39,159 @@ public class MathFunctions { } , returns="The absolute value of the number passed in." ) - public static class Abs implements StellarFunction { + public static class Abs extends StellarMathFunction{ + + + public Abs() { + super(MathOperations.ABS); + } + } + + @Stellar(name="LOG10" + ,description="Returns the log (base 10) of a number." + ,params = { + "number - The number to take the log (base 10) value of" + } + , returns="The log (base 10) of the number passed in." + ) + public static class Log10 extends StellarMathFunction { + public Log10() { + super(MathOperations.LOG10); + } + + } + + @Stellar(name="LOG2" + ,description="Returns the log (base 2) of a number." + ,params = { + "number - The number to take the log (base 2) value of" + } + , returns="The log (base 2) of the number passed in." + ) + public static class Log2 extends StellarMathFunction { + public Log2() { + super(MathOperations.LOG2); + } - @Override - public Object apply(List<Object> args, Context context) throws ParseException { - if(args.size() < 1) { - return Double.NaN; - } - Number n = (Number)args.get(0); - if(n == null) { - return Double.NaN; - } - return Math.abs(n.doubleValue()); + } + + @Stellar(name="LN" + ,description="Returns the natural log of a number." + ,params = { + "number - The number to take the natural log value of" + } + , returns="The natural log of the number passed in." + ) + public static class Ln extends StellarMathFunction { + public Ln() { + super(MathOperations.LN); } - @Override - public void initialize(Context context) { + } + @Stellar(name="SQRT" + ,description="Returns the square root of a number." + ,params = { + "number - The number to take the square root of" + } + , returns="The square root of the number passed in." + ) + public static class Sqrt extends StellarMathFunction { + public Sqrt() { + super(MathOperations.SQRT); } - @Override - public boolean isInitialized() { - return true; + } + + @Stellar(name="CEILING" + ,description="Returns the ceiling of a number." + ,params = { + "number - The number to take the ceiling of" + } + , returns="The ceiling of the number passed in." + ) + public static class Ceil extends StellarMathFunction { + public Ceil() { + super(MathOperations.CEIL); } + } + @Stellar(name="FLOOR" + ,description="Returns the floor of a number." + ,params = { + "number - The number to take the floor of" + } + , returns="The floor of the number passed in." + ) + public static class Floor extends StellarMathFunction { + public Floor() { + super(MathOperations.FLOOR); + } + } + + @Stellar(name="SIN" + ,description="Returns the sine of a number." + ,params = { + "number - The number to take the sine of" + } + , returns="The sine of the number passed in." + ) + public static class Sin extends StellarMathFunction { + public Sin() { + super(MathOperations.SIN); + } + } + + @Stellar(name="COS" + ,description="Returns the cosine of a number." + ,params = { + "number - The number to take the cosine of" + } + , returns="The cosine of the number passed in." + ) + public static class Cos extends StellarMathFunction { + public Cos() { + super(MathOperations.COS); + } + } + + @Stellar(name="TAN" + ,description="Returns the tangent of a number." + ,params = { + "number - The number to take the tangent of" + } + , returns="The tangent of the number passed in." + ) + public static class Tan extends StellarMathFunction { + public Tan() { + super(MathOperations.TAN); + } + } + + @Stellar(name="EXP" + ,description="Returns Euler's number raised to the power of the argument" + ,params = { + "number - The power to which e is raised." + } + , returns="Euler's number raised to the power of the argument." + ) + public static class Exp extends StellarMathFunction { + public Exp() { + super(MathOperations.EXP); + } + } + + @Stellar(name="ROUND" + ,description="Rounds a number to the nearest integer. This is half-up rounding." + ,params = { + "number - The number to round" + } + , returns="The nearest integer (based on half-up rounding)." + ) + public static class Round extends StellarMathFunction { + public Round() { + super(MathOperations.ROUND); + } + } } http://git-wip-us.apache.org/repos/asf/metron/blob/b2375a1f/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/MathFunctionsTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/MathFunctionsTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/MathFunctionsTest.java index d427fb4..cdb1c42 100644 --- a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/MathFunctionsTest.java +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/MathFunctionsTest.java @@ -19,6 +19,7 @@ */ package org.apache.metron.stellar.dsl.functions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.apache.metron.stellar.common.StellarProcessor; import org.apache.metron.stellar.dsl.Context; @@ -26,9 +27,16 @@ import org.apache.metron.stellar.dsl.StellarFunctions; import org.junit.Assert; import org.junit.Test; +import java.util.HashMap; import java.util.Map; public class MathFunctionsTest { + + public static final double EPSILON = 1e-7; + public static Map<Double, Double> baseExpectations = new HashMap<Double, Double>() {{ + put(Double.NaN, Double.NaN); + }}; + public static Object run(String rule, Map<String, Object> variables) { Context context = Context.EMPTY_CONTEXT(); StellarProcessor processor = new StellarProcessor(); @@ -38,10 +46,156 @@ public class MathFunctionsTest { @Test public void testAbs() { - Assert.assertEquals((Double)run("ABS(value)", ImmutableMap.of("value", 0)), 0, 1e-7); - Assert.assertTrue(Double.isNaN((Double)run("ABS(value)", ImmutableMap.of("value", Double.NaN)))); - Assert.assertEquals((Double)run("ABS(value)", ImmutableMap.of("value", 10.5)), 10.5, 1e-7); - Assert.assertEquals((Double)run("ABS(value)", ImmutableMap.of("value", -10.5)), 10.5, 1e-7); + assertValues("ABS", + new HashMap<Double, Double>(baseExpectations) {{ + put(0d, 0d); + put(10.5d, 10.5d); + put(-10.5d, 10.5d); + }} + ); + } + + @Test + public void testSqrt() { + assertValues("SQRT", + new HashMap<Double, Double>(baseExpectations) {{ + put(0d, 0d); + put(25d, 5d); + put(-10.5d, Double.NaN); + }} + ); + } + + @Test + public void testCeiling() { + assertValues("CEILING", + new HashMap<Double, Double>(baseExpectations) {{ + put(0d, 0d); + put(10.5d, 11d); + put(-10.5d, -10d); + }} + ); + } + + @Test + public void testFloor() { + assertValues("FLOOR", + new HashMap<Double, Double>(baseExpectations) {{ + put(0d, 0d); + put(10.5d, 10d); + put(-10.5d, -11d); + }} + ); + } + + @Test + public void testSin() { + assertValues("SIN", + new HashMap<Double, Double>(baseExpectations) {{ + put(0d, 0d); + put(Math.PI/6, 0.5); + put(Math.PI/4, Math.sqrt(2)/2.0); + put(Math.PI/3, Math.sqrt(3)/2.0); + put(Math.PI/2, 1d); + }} + ); + } + + @Test + public void testCos() { + assertValues("COS", + new HashMap<Double, Double>(baseExpectations) {{ + put(0d, 1d); + put(Math.PI/6, Math.sqrt(3)/2.0); + put(Math.PI/4, Math.sqrt(2)/2.0); + put(Math.PI/3, 0.5d); + put(Math.PI/2, 0d); + }} + ); + } + + @Test + public void testTan() { + assertValues("TAN", + new HashMap<Double, Double>(baseExpectations) {{ + put(0d, 0d); + put(Math.PI/6, Math.sqrt(3)/3.0); + put(Math.PI/4, 1d); + put(Math.PI/3, Math.sqrt(3)); + put(Math.PI/2, Math.sin(Math.PI/2)/Math.cos(Math.PI/2)); + }} + ); + } + + @Test + public void testExp() { + assertValues("EXP", + new HashMap<Double, Double>(baseExpectations) {{ + put(0d, 1d); + put(0.5d, Math.sqrt(Math.E)); + put(-0.5d, 1/Math.sqrt(Math.E)); + put(1d, Math.E); + put(2d, Math.E*Math.E); + }} + ); + } + + @Test + public void testRound() { + assertValues("ROUND", + new HashMap<Double, Double>(baseExpectations) {{ + put(0d, 0d); + put(0.5d, 1d); + put(0.4d, 0d); + put(-0.5d, 0d); + }} + ); + } + + @Test + public void testNaturalLog() { + testLog("LN", Math.E); + } + + @Test + public void testLog2() { + testLog("LOG2", 2); + } + + @Test + public void testLog10() { + testLog("LOG10", 10); + } + + public void assertValues(String func, Map<Double, Double> expected) { + for(Map.Entry<Double, Double> test : expected.entrySet()) { + for(String expr : ImmutableList.of(func + "(value)" + ,func + "(" + test.getKey() + ")" + ) + ) + { + if (Double.isNaN(test.getValue())) { + Assert.assertTrue(expr + " != NaN, where value == " + test.getKey(), Double.isNaN(toDouble(run(expr, ImmutableMap.of("value", test.getKey()))))); + } else { + Assert.assertEquals(expr + " != " + test.getValue() + " (where value == " + test.getKey() + ")", test.getValue(), toDouble(run(expr, ImmutableMap.of("value", test.getKey()))), EPSILON); + } + } + } + } + + public Double toDouble(Object n) { + return ((Number)n).doubleValue(); + } + + public void testLog(String logExpr, double base) { + Map<Double, Double> expectedValues = new HashMap<Double, Double>(baseExpectations) {{ + put(base, 1d); + put(0d, Double.NEGATIVE_INFINITY); + }}; + for(int i = 1;i <= 10;++i) { + expectedValues.put(Math.pow(base, i), (double)i); + } + assertValues(logExpr, expectedValues); } }