/* * Licensed 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 com.addthis.hydra.data.filter.bundle; import com.addthis.bundle.core.Bundle; import com.addthis.bundle.core.BundleField; import com.addthis.bundle.core.list.ListBundle; import com.addthis.bundle.core.list.ListBundleFormat; import com.addthis.codec.annotations.FieldConfig; import com.addthis.hydra.data.filter.util.BundleCalculator; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; /** * This {@link BundleFilter BundleFilter} <span class="hydra-summary">performs postfix calculator operations</span>. * <p/> * <p>This filter uses a calculation stack to perform arithmetic operations. The stack is initially empty * and values can be pushed or popped from the top of the stack. Arithmetic operations can be performed * on the element(s) at the top of the stack. * <p>The values from the input bundle can be pushed onto the stack * and the value at the top of the stack can be moved into the input bundle. * Direct arithmetic operations on the input bundle is not permitted. * If the 'columns' parameter is missing then the entire input bundle is available * to the calculation stack. Otherwise a subset of the input bundle is available * to the calculation stack. It is convenient to specify the 'columns' parameter * as the user must know the order of the elements in the input bundle. The 'columns' * parameter specifies the order of the subset of elements.</p> * <p>The conditional operations will terminate the calculation stack when the condition * is not met. If the calculation is terminated early then the filter returns false. Otherwise * the filter returns true.</p> * <p>The following operations below are available. Each operation proceeds in three steps: * (1) Zero or more values are popped off the stack, (2) an operation is performed on these values, * and (3) zero or more values are pushed onto the stack. For convenience the following table * labels the first element popped off the stack as "a" (the topmost element), the second * element as "b", etc. The variables "X","Y","Z", etc. represent numbers in the command sequence. * <table width="80%" class="num"> * <tr> * <th width="15%">name</th> * <th width="10%">pop count</th> * <th width="65%">operation</th> * <th width="10%">push count</th> * </tr> * <tr> * <td>"+" or "add"</td> * <td>2</td> * <td>add a and b</td> * <td>1</td> * </tr> * <tr> * <td>"-" or "sub"</td> * <td>2</td> * <td>subtract a from b</td> * <td>1</td> * </tr> * <tr> * <td>"*" or "mult" or "prod"</td> * <td>2</td> * <td>long integer multiplication</td> * <td>1</td> * </tr> * <tr> * <td>"dmult" or "dprod"</td> * <td>2</td> * <td>floating point multiplication</td> * <td>1</td> * </tr> * <tr> * <td>"/" or "div"</td> * <td>2</td> * <td>long integer divide the b by a. * <br>If diverr is false and a is 0 then return 0.</br></td> * <td>1</td> * </tr> * <tr> * <td>"ddiv"</td> * <td>2</td> * <td>floating point divide the b by a. * <br>If diverr is false and a is 0.0 then return 0.</br></td> * <td>1</td> * </tr> * <tr> * <td>"log"</td> * <td>1</td> * <td>compute logarithm base 10 of a</td> * <td>1</td> * </tr> * <tr> * <td>"%" or "rem"</td> * <td>2</td> * <td>modulo b by a * <br>If diverr is false and a is 0 then return 0.</br></td> * <td>1</td> * </tr> * <tr> * <td>"=" or "s" or "set"</td> * <td>2</td> * <td>assign the a-th column the value of b. * <br>If a is out of bounds then * append b to the end of the bundle</br></td> * <td>0</td> * </tr> * <tr> * <td>"pop" or "drop"</td> * <td>1</td> * <td>pop the top element off the stack</td> * <td>0</td> * </tr> * <tr> * <td>"x" or "swap"</td> * <td>2</td> * <td>swap the positions of a and b</td> * <td>2</td> * </tr> * <tr> * <td>"d" or "dup"</td> * <td>1</td> * <td>duplicate element a</td> * <td>2</td> * </tr> * <tr> * <td>">" or "gt"</td> * <td>2</td> * <td>if long integer (b > a) is false then end the calculation</td> * <td>0</td> * </tr> * <tr> * <td>">=" or "gteq"</td> * <td>2</td> * <td>if long integer (b >= a) is false then end the calculation</td> * <td>0</td> * </tr> * <tr> * <td>"<" or "lt"</td> * <td>2</td> * <td>if long integer (b < a) is false then end the calculation</td> * <td>0</td> * </tr> * <tr> * <td>"<=" or "lteq"</td> * <td>2</td> * <td>if long integer (b <= a) is false then end the calculation</td> * <td>0</td> * </tr> * <tr> * <td>"eq"</td> * <td>2</td> * <td>if long integer (a == b) is false then end the calculation</td> * <td>0</td> * </tr> * <tr> * <td>"toi" or "toint"</td> * <td>1</td> * <td>convert a to a long integer</td> * <td>1</td> * </tr> * <tr> * <td>"tof" or "tofloat"</td> * <td>1</td> * <td>convert a to a floating-point value</td> * <td>1</td> * </tr> * <tr> * <td>"tob" or "tobits"</td> * <td>1</td> * <td>convert floating point a to its bitwise long representation</td> * <td>1</td> * </tr> * <tr> * <td>"btof" or "btofloat"</td> * <td>1</td> * <td>convert the bitwise representation of long a to a floating-point value</td> * <td>1</td> * </tr> * <tr> * <td>"out" or "shiftout"</td> * <td>1</td> * <td>append a to the end of the bundle</td> * <td>0</td> * </tr> * <tr> * <td>"sqrt"</td> * <td>1</td> * <td>calculate the square root of a</td> * <td>1</td> * </tr> * <tr> * <td>"diverr"</td> * <td>0</td> * <td>set the diverr flag to true</td> * <td>0</td> * </tr> * <tr> * <td>">>" or "dgt"</td> * <td>2</td> * <td>if floating-point (b > a) is false then end the calculation</td> * <td>1</td> * </tr> * <tr> * <td>">>=" or "dgteq"</td> * <td>2</td> * <td>if floating-point (b >= a) is false then end the calculation</td> * <td>1</td> * </tr> * <tr> * <td>"<<" or "dlt"</td> * <td>2</td> * <td>if floating-point (b < a) is false then end the calculation</td> * <td>1</td> * </tr> * <tr> * <td>"<<=" or "dlteq"</td> * <td>2</td> * <td>if floating-point (b <= a) is false then end the calculation</td> * <td>1</td> * </tr> * <tr> * <td>"==" or "deq"</td> * <td>2</td> * <td>if floating-point (b == a) is false then end the calculation</td> * <td>1</td> * </tr> * <tr> * <td>"min"</td> * <td>2</td> * <td>return a copy of the minimum of a and b</td> * <td>1</td> * </tr> * <tr> * <td>"max"</td> * <td>2</td> * <td>return a copy of the maximum of a and b</td> * <td>1</td> * </tr> * <tr> * <td>"minif"</td> * <td>2</td> * <td>return the minimum of a and b</td> * <td>1</td> * </tr> * <tr> * <td>"maxif"</td> * <td>2</td> * <td>return the maximum of a and b</td> * <td>1</td> * </tr> * <tr> * <td>"abs"</td> * <td>1</td> * <td>return the absolute value of a as a float</td> * <td>1</td> * </tr> * <tr> * <td>"mean"</td> * <td>ALL</td> * <td>consumes all values on the stack and returns the mean</td> * <td>1</td> * </tr> * <tr> * <td>"variance"</td> * <td>ALL</td> * <td>consumes all values on the stack and returns the (population) variance</td> * <td>1</td> * </tr> * <tr> * <td>"aX:Y:Z..."</td> * <td>0</td> * <td>push the arrays in columns X, Y, Z,... onto the stack</td> * <td>array length</td> * </tr> * <tr> * <td>"cX:Y:Z..."</td> * <td>0</td> * <td>push the value in columns X, Y, Z,... onto the stack</td> * <td>len(args)</td> * </tr> * <tr> * <td>"nX:Y:Z..."</td> * <td>0</td> * <td>push the values X, Y, Z,... onto the stack</td> * <td>len(args)</td> * </tr> * <tr> * <td>"vX"</td> * <td>0</td> * <td>push the value X onto the stack</td> * <td>1</td> * </tr> * <tr> * <td>"vector"</td> * <td>0</td> * <td>modifies the behavior of the next operation.<br> * Next operation pops all elements off the stack<br> * and pushes a single value onto the stack. Available<br> * for "+", "mult", "dmult", "min", and "max". * <td>0</td> * </tr> * </table> * <p>Examples:</p> * <pre> * {num {columns:["END_TIME", "START_TIME", "WALL_TIME"], define:"c0,c1,sub,v1000,ddiv,toint,v2,set"}} * {num {columns:["WALL_TIME", "TASKS", "CLUSTER_TIME"], define:"c0,c1,mult,v2,set"}} * </pre> * * @user-reference */ public class BundleFilterNum implements BundleFilter { /** * Sequence of commands to execute (comma-delimited) */ final private String define; /** * Subset of fields from the bundle filter that are used in calculation. */ final private String[] columns; final private BundleCalculator calculator; @JsonCreator public BundleFilterNum(@JsonProperty(value = "define", required = true) String define, @JsonProperty("columns") String[] columns) { this.define = define; this.columns = columns; this.calculator = new BundleCalculator(define); } protected Bundle makeAltBundle(Bundle bundle) { ListBundleFormat format = new ListBundleFormat(); Bundle alt = new ListBundle(format); for (int i = 0; i < columns.length; i++) { BundleField field = format.getField(columns[i]); alt.setValue(field, bundle.getValue(bundle.getFormat().getField(columns[i]))); } return alt; } protected void mergeBundles(Bundle orig, Bundle nBundle) { for (BundleField bf : nBundle) { orig.setValue(orig.getFormat().getField(bf.getName()), nBundle.getValue(bf)); } } @Override public boolean filter(Bundle bundle) { if (columns == null) { return calculator.calculate(bundle) != null; } else { Bundle alt = makeAltBundle(bundle); boolean res = calculator.calculate(alt) != null; mergeBundles(bundle, alt); return res; } } }