Skip to content

Commit

Permalink
Add a vectorized ternary if to the query library. (#2876)
Browse files Browse the repository at this point in the history
* Add a vectorized ternary if to the query library.

* Rerun static import generator.

* Address review comments

* Address review comments
  • Loading branch information
chipkent authored Sep 20, 2022
1 parent f564b7a commit 1233561
Show file tree
Hide file tree
Showing 3 changed files with 259 additions and 0 deletions.
139 changes: 139 additions & 0 deletions engine/function/src/templates/Basic.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,77 @@ public class Basic {
return NULL_LONG;
}

/**
* Returns elements from either trueCase or falseCase, depending on condition.
*
* @param condition a boolean value used to select output values.
* @param trueCase value returned when condition is true.
* @param falseCase value returned when condition is false.
* @return trueCase value if condition is true, falseCase value if condition is false, or null if condition is null.
*/
public static <T> T ifelseObj(Boolean condition, T trueCase, T falseCase) {
if (condition == null) {
return null;
}

return condition ? trueCase : falseCase;
}

/**
* Returns elements from either trueCase or falseCase, depending on condition.
*
* @param condition a boolean value used to select output values.
* @param trueCase value returned when condition is true.
* @param falseCase value returned when condition is false.
* @return An array of T whose values are determined by the corresponding elements of condition, trueCase, and falseCase.
* The result element will be the trueCase element if the condition element is true;
* the falseCase element if the condition element is false; or null if the condition element is null.
* Returns null if any of the inputs is null.
*/
public static <T> T[] ifelseObj(BooleanVector condition, ObjectVector<T> trueCase, ObjectVector<T> falseCase) {
if (condition == null || trueCase == null || falseCase == null) {
return null;
}

final int n_c = condition.intSize("condition");
final int n_t = trueCase.intSize("trueCase");
final int n_f = falseCase.intSize("falseCase");

if (n_c != n_t || n_c != n_f) {
throw new IllegalArgumentException("Inconsistent input sizes: condition=" + n_c + " trueCase=" + n_t + " falseCase=" + n_f);
}

if (!trueCase.getComponentType().equals(falseCase.getComponentType())) {
throw new IllegalArgumentException("Input vectors have different element types. trueCase=" + trueCase.getComponentType() + " falseCase=" + falseCase.getComponentType());
}

@SuppressWarnings("unchecked") final T[] result = (T[])Array.newInstance(trueCase.getComponentType(), n_c);

for (int i=0; i < n_c; i++) {
result[i] = condition.get(i) == null ? null : (condition.get(i) ? trueCase.get(i) : falseCase.get(i));
}

return result;
}

/**
* Returns elements from either trueCase or falseCase, depending on condition.
*
* @param condition a boolean value used to select output values.
* @param trueCase value returned when condition is true.
* @param falseCase value returned when condition is false.
* @return An array of T whose values are determined by the corresponding elements of condition, trueCase, and falseCase.
* The result element will be the trueCase element if the condition element is true;
* the falseCase element if the condition element is false; or null if the condition element is null.
* Returns null if any of the inputs is null.
*/
public static <T> T[] ifelseObj(Boolean[] condition, T[] trueCase, T[] falseCase) {
if (condition == null || trueCase == null || falseCase == null) {
return null;
}

return ifelseObj(new BooleanVectorDirect(condition), new ObjectVectorDirect<T>(trueCase), new ObjectVectorDirect<T>(falseCase));
}

<#list primitiveTypes as pt>
<#if !pt.valueType.isBoolean >
Expand Down Expand Up @@ -1402,6 +1473,74 @@ public class Basic {
return NULL_LONG;
}

/**
* Returns elements from either trueCase or falseCase, depending on condition.
*
* @param condition a boolean value used to select output values.
* @param trueCase value returned when condition is true.
* @param falseCase value returned when condition is false.
* @return trueCase value if condition is true, falseCase value if condition is false, or the Deephaven null constant if condition is null.
*/
public static ${pt.primitive} ifelse(Boolean condition, ${pt.primitive} trueCase, ${pt.primitive} falseCase) {
if (condition == null) {
return ${pt.null};
}

return condition ? trueCase : falseCase;
}

/**
* Returns elements from either trueCase or falseCase, depending on condition.
*
* @param condition a boolean value used to select output values.
* @param trueCase value returned when condition is true.
* @param falseCase value returned when condition is false.
* @return An array of ${pt.primitive} whose values are determined by the corresponding elements of condition, trueCase, and falseCase.
* The result element will be the trueCase element if the condition element is true;
* the falseCase element if the condition element is false; or the Deephaven null constant if the condition element is null.
* Returns null if any of the inputs is null.
*/
public static ${pt.primitive}[] ifelse(BooleanVector condition, ${pt.dbArray} trueCase, ${pt.dbArray} falseCase) {
if (condition == null || trueCase == null || falseCase == null) {
return null;
}

final int n_c = condition.intSize("condition");
final int n_t = trueCase.intSize("trueCase");
final int n_f = falseCase.intSize("falseCase");

if (n_c != n_t || n_c != n_f) {
throw new IllegalArgumentException("Inconsistent input sizes: condition=" + n_c + " trueCase=" + n_t + " falseCase=" + n_f);
}

final ${pt.primitive}[] result = new ${pt.primitive}[n_c];

for (int i=0; i < n_c; i++) {
result[i] = condition.get(i) == null ? ${pt.null} : (condition.get(i) ? trueCase.get(i) : falseCase.get(i));
}

return result;
}

/**
* Returns elements from either trueCase or falseCase, depending on condition.
*
* @param condition a boolean value used to select output values.
* @param trueCase value returned when condition is true.
* @param falseCase value returned when condition is false.
* @return An array of ${pt.primitive} whose values are determined by the corresponding elements of condition, trueCase, and falseCase.
* The result element will be the trueCase element if the condition element is true;
* the falseCase element if the condition element is false; or the Deephaven null constant if the condition element is null.
* Returns null if any of the inputs is null.
*/
public static ${pt.primitive}[] ifelse(Boolean[] condition, ${pt.primitive}[] trueCase, ${pt.primitive}[] falseCase) {
if (condition == null || trueCase == null || falseCase == null) {
return null;
}

return ifelse(new BooleanVectorDirect(condition), new ${pt.dbArrayDirect}(trueCase), new ${pt.dbArrayDirect}(falseCase));
}

public static ${pt.primitive}[] forwardFill(${pt.primitive}... values){
if(values == null){
return null;
Expand Down
72 changes: 72 additions & 0 deletions engine/function/src/templates/TestBasic.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,43 @@ public class TestBasic extends BaseArrayTestCase {
assertTrue(inObj(1000000L, 1000000L, 2000000L, 3000000L));
}

public void testObjIfelseScalar() {
final Integer i1 = Integer.valueOf(1);
final Integer i2 = Integer.valueOf(2);
assertEquals(null, ifelseObj(null, i1, i2));
assertEquals(i1, ifelseObj(true, i1, i2));
assertEquals(i2, ifelseObj(false, i1, i2));
}

public void testObjIfelseVec() {
final BooleanVector bv = new BooleanVectorDirect(new Boolean[]{null, true, false});
final ObjectVector<Integer> iv1 = new ObjectVectorDirect<Integer>(new Integer[]{1, 2, 3});
final ObjectVector<Integer> iv2 = new ObjectVectorDirect<Integer>(new Integer[]{11, 12, 13});
assertEquals(new Integer[]{null, 2, 13}, ifelseObj(bv, iv1, iv2));
assertEquals(null, ifelseObj((BooleanVector) null, iv1, iv2));
assertEquals(null, ifelseObj(bv, null, iv2));
assertEquals(null, ifelseObj(bv, iv1, null));

try {
ifelseObj(new BooleanVectorDirect(new Boolean[]{null, true, false, false}), iv1, iv2);
fail("Should have raised an IllegalArgumentException");
} catch(IllegalArgumentException e) {
}
}

public void testObjIfelseArray() {
assertEquals(new Integer[]{null, 2, 13}, ifelseObj(new Boolean[]{null, true, false}, new Integer[]{1, 2, 3}, new Integer[]{11, 12, 13}));
assertEquals(null, ifelseObj((Boolean[]) null, new Integer[]{1, 2, 3}, new Integer[]{11, 12, 13}));
assertEquals(null, ifelseObj(new Boolean[]{null, true, false}, null, new Integer[]{11, 12, 13}));
assertEquals(null, ifelseObj(new Boolean[]{null, true, false}, new Integer[]{1, 2, 3}, null));

try {
ifelseObj(new Boolean[]{null, true, false, false}, new Integer[]{1, 2, 3}, new Integer[]{11, 12, 13});
fail("Should have raised an IllegalArgumentException");
} catch(IllegalArgumentException e) {
}
}


//////////////////////////// boolean ////////////////////////////

Expand Down Expand Up @@ -659,6 +696,41 @@ public class TestBasic extends BaseArrayTestCase {
assertEquals(NULL_LONG, firstIndexOf((${pt.primitive})40, (${pt.dbArray}) null));
}

public void test${pt.boxed}IfelseScalar() {
assertEquals(${pt.null}, ifelse(null, (${pt.primitive})1, (${pt.primitive})2));
assertEquals((${pt.primitive})1, ifelse(true, (${pt.primitive})1, (${pt.primitive})2));
assertEquals((${pt.primitive})2, ifelse(false, (${pt.primitive})1, (${pt.primitive})2));
}

public void test${pt.boxed}IfelseVec() {
final BooleanVector bv = new BooleanVectorDirect(new Boolean[]{null, true, false});
final ${pt.dbArray} iv1 = new ${pt.dbArrayDirect}(new ${pt.primitive}[]{1, 2, 3});
final ${pt.dbArray} iv2 = new ${pt.dbArrayDirect}(new ${pt.primitive}[]{11, 12, 13});
assertEquals(new ${pt.primitive}[]{${pt.null}, 2, 13}, ifelse(bv, iv1, iv2));
assertEquals(null, ifelse((BooleanVector) null, iv1, iv2));
assertEquals(null, ifelse(bv, null, iv2));
assertEquals(null, ifelse(bv, iv1, null));

try {
ifelse(new BooleanVectorDirect(new Boolean[]{null, true, false, false}), iv1, iv2);
fail("Should have raised an IllegalArgumentException");
} catch(IllegalArgumentException e) {
}
}

public void test${pt.boxed}IfelseArray() {
assertEquals(new ${pt.primitive}[]{${pt.null}, 2, 13}, ifelse(new Boolean[]{null, true, false}, new ${pt.primitive}[]{1, 2, 3}, new ${pt.primitive}[]{11, 12, 13}));
assertEquals(null, ifelse((Boolean[]) null, new ${pt.primitive}[]{1, 2, 3}, new ${pt.primitive}[]{11, 12, 13}));
assertEquals(null, ifelse(new Boolean[]{null, true, false}, null, new ${pt.primitive}[]{11, 12, 13}));
assertEquals(null, ifelse(new Boolean[]{null, true, false}, new ${pt.primitive}[]{1, 2, 3}, null));

try {
ifelse(new Boolean[]{null, true, false, false}, new ${pt.primitive}[]{1, 2, 3}, new ${pt.primitive}[]{11, 12, 13});
fail("Should have raised an IllegalArgumentException");
} catch(IllegalArgumentException e) {
}
}

</#if>
</#list>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,54 @@ public class GroovyStaticImports {
public static long[] forwardFill( io.deephaven.vector.LongVector values ) {return Basic.forwardFill( values );}
/** @see io.deephaven.function.Basic#forwardFill(io.deephaven.vector.ShortVector) */
public static short[] forwardFill( io.deephaven.vector.ShortVector values ) {return Basic.forwardFill( values );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean[],byte[],byte[]) */
public static byte[] ifelse( java.lang.Boolean[] condition, byte[] trueCase, byte[] falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean[],char[],char[]) */
public static char[] ifelse( java.lang.Boolean[] condition, char[] trueCase, char[] falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean[],double[],double[]) */
public static double[] ifelse( java.lang.Boolean[] condition, double[] trueCase, double[] falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean[],float[],float[]) */
public static float[] ifelse( java.lang.Boolean[] condition, float[] trueCase, float[] falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean[],int[],int[]) */
public static int[] ifelse( java.lang.Boolean[] condition, int[] trueCase, int[] falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean[],long[],long[]) */
public static long[] ifelse( java.lang.Boolean[] condition, long[] trueCase, long[] falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean[],short[],short[]) */
public static short[] ifelse( java.lang.Boolean[] condition, short[] trueCase, short[] falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean,byte,byte) */
public static byte ifelse( java.lang.Boolean condition, byte trueCase, byte falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean,char,char) */
public static char ifelse( java.lang.Boolean condition, char trueCase, char falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean,double,double) */
public static double ifelse( java.lang.Boolean condition, double trueCase, double falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean,float,float) */
public static float ifelse( java.lang.Boolean condition, float trueCase, float falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean,int,int) */
public static int ifelse( java.lang.Boolean condition, int trueCase, int falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean,long,long) */
public static long ifelse( java.lang.Boolean condition, long trueCase, long falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(java.lang.Boolean,short,short) */
public static short ifelse( java.lang.Boolean condition, short trueCase, short falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(io.deephaven.vector.BooleanVector,io.deephaven.vector.ByteVector,io.deephaven.vector.ByteVector) */
public static byte[] ifelse( io.deephaven.vector.BooleanVector condition, io.deephaven.vector.ByteVector trueCase, io.deephaven.vector.ByteVector falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(io.deephaven.vector.BooleanVector,io.deephaven.vector.CharVector,io.deephaven.vector.CharVector) */
public static char[] ifelse( io.deephaven.vector.BooleanVector condition, io.deephaven.vector.CharVector trueCase, io.deephaven.vector.CharVector falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(io.deephaven.vector.BooleanVector,io.deephaven.vector.DoubleVector,io.deephaven.vector.DoubleVector) */
public static double[] ifelse( io.deephaven.vector.BooleanVector condition, io.deephaven.vector.DoubleVector trueCase, io.deephaven.vector.DoubleVector falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(io.deephaven.vector.BooleanVector,io.deephaven.vector.FloatVector,io.deephaven.vector.FloatVector) */
public static float[] ifelse( io.deephaven.vector.BooleanVector condition, io.deephaven.vector.FloatVector trueCase, io.deephaven.vector.FloatVector falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(io.deephaven.vector.BooleanVector,io.deephaven.vector.IntVector,io.deephaven.vector.IntVector) */
public static int[] ifelse( io.deephaven.vector.BooleanVector condition, io.deephaven.vector.IntVector trueCase, io.deephaven.vector.IntVector falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(io.deephaven.vector.BooleanVector,io.deephaven.vector.LongVector,io.deephaven.vector.LongVector) */
public static long[] ifelse( io.deephaven.vector.BooleanVector condition, io.deephaven.vector.LongVector trueCase, io.deephaven.vector.LongVector falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelse(io.deephaven.vector.BooleanVector,io.deephaven.vector.ShortVector,io.deephaven.vector.ShortVector) */
public static short[] ifelse( io.deephaven.vector.BooleanVector condition, io.deephaven.vector.ShortVector trueCase, io.deephaven.vector.ShortVector falseCase ) {return Basic.ifelse( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelseObj(java.lang.Boolean[],T[],T[]) */
public static <T> T[] ifelseObj( java.lang.Boolean[] condition, T[] trueCase, T[] falseCase ) {return Basic.ifelseObj( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelseObj(java.lang.Boolean,T,T) */
public static <T> T ifelseObj( java.lang.Boolean condition, T trueCase, T falseCase ) {return Basic.ifelseObj( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#ifelseObj(io.deephaven.vector.BooleanVector,io.deephaven.vector.ObjectVector,io.deephaven.vector.ObjectVector) */
public static <T> T[] ifelseObj( io.deephaven.vector.BooleanVector condition, io.deephaven.vector.ObjectVector<T> trueCase, io.deephaven.vector.ObjectVector<T> falseCase ) {return Basic.ifelseObj( condition, trueCase, falseCase );}
/** @see io.deephaven.function.Basic#in(byte,byte[]) */
public static boolean in( byte testedValues, byte[] possibleValues ) {return Basic.in( testedValues, possibleValues );}
/** @see io.deephaven.function.Basic#in(char,char[]) */
Expand Down

0 comments on commit 1233561

Please sign in to comment.