diff --git a/std-bits/base/src/main/java/org/enso/base/polyglot/NumericConverter.java b/std-bits/base/src/main/java/org/enso/base/polyglot/NumericConverter.java new file mode 100644 index 000000000000..d1af2066aa7f --- /dev/null +++ b/std-bits/base/src/main/java/org/enso/base/polyglot/NumericConverter.java @@ -0,0 +1,108 @@ +package org.enso.base.polyglot; + +import java.math.BigDecimal; + +/** + * The numeric converter deals with conversions of Java numeric types to the two main types + * supported by Enso - Long for integers and Double for decimals. Any other types are coerced to one + * of these types. + * + *

It provides two concepts - coercion - which allows to coerce an integer type to a decimal, but + * will not convert a decimal to an integer even if it has 0 fractional part. Then there is + * conversion which allows to convert a decimal with 0 fractional part to an integer. Conversion + * should be used when we care about the original type of the object (i.e. we want any decimals to + * require decimal storage even if they have 0 fractional part). Conversion is to be used when we + * want to be consistent with Enso's equality semantics where 2 == 2.0. + */ +public class NumericConverter { + /** + * Coerces a number (possibly an integer) to a Double. + * + *

Will throw an exception if the object is not a number. + */ + public static double coerceToDouble(Object o) { + return switch (o) { + case Double x -> x; + case BigDecimal x -> x.doubleValue(); + case Float x -> x.doubleValue(); + default -> (double) coerceToLong(o); + }; + } + + /** + * Coerces a number to an Integer. + * + *

Will throw an exception if the object is not an integer. + * + *

Decimal values are not accepted. + */ + public static long coerceToLong(Object o) { + return switch (o) { + case Long x -> x; + case Integer x -> x.longValue(); + case Short x -> x.longValue(); + case Byte x -> x.longValue(); + default -> throw new UnsupportedOperationException(); + }; + } + + /** Returns true if the object is any supported number. */ + public static boolean isCoercibleToDouble(Object o) { + return o instanceof Double + || o instanceof BigDecimal + || o instanceof Float + || isCoercibleToLong(o); + } + + /** + * Returns true if the object is any supported integer. + * + *

Returns false for decimals with 0 fractional part - the type itself must be an integer type. + */ + public static boolean isCoercibleToLong(Object o) { + return o instanceof Long || o instanceof Integer || o instanceof Short || o instanceof Byte; + } + + /** + * Tries converting the value to a Double. + * + *

It will return null if the object represented a non-numeric value. + */ + public static Double tryConvertingToDouble(Object o) { + return switch (o) { + case Double x -> x; + case BigDecimal x -> x.doubleValue(); + case Float x -> x.doubleValue(); + case Long x -> x.doubleValue(); + case Integer x -> x.doubleValue(); + case Short x -> x.doubleValue(); + case Byte x -> x.doubleValue(); + case null, default -> null; + }; + } + + /** + * Tries converting the value to a Long. + * + *

Decimal number types are accepted, only if their fractional part is 0. It will return null + * if the object represented a non-integer value. + */ + public static Long tryConvertingToLong(Object o) { + return switch (o) { + case Long x -> x; + case Integer x -> x.longValue(); + case Short x -> x.longValue(); + case Byte x -> x.longValue(); + case Double x -> x % 1.0 == 0.0 ? x.longValue() : null; + case Float x -> x % 1.0f == 0.0f ? x.longValue() : null; + case BigDecimal x -> { + try { + yield x.longValueExact(); + } catch (ArithmeticException e) { + yield null; + } + } + case null, default -> null; + }; + } +} diff --git a/std-bits/base/src/main/java/org/enso/base/Polyglot_Utils.java b/std-bits/base/src/main/java/org/enso/base/polyglot/Polyglot_Utils.java similarity index 56% rename from std-bits/base/src/main/java/org/enso/base/Polyglot_Utils.java rename to std-bits/base/src/main/java/org/enso/base/polyglot/Polyglot_Utils.java index 23abd642bdbe..14d0592ce762 100644 --- a/std-bits/base/src/main/java/org/enso/base/Polyglot_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/polyglot/Polyglot_Utils.java @@ -1,11 +1,14 @@ -package org.enso.base; - -import org.graalvm.polyglot.Value; +package org.enso.base.polyglot; import java.time.LocalDate; import java.time.LocalDateTime; +import org.graalvm.polyglot.Value; public class Polyglot_Utils { + /** + * Converts a polyglot Value ensuring that various date/time types are converted to the correct + * type. + */ public static Object convertPolyglotValue(Value item) { if (item.isDate()) { LocalDate d = item.asDate(); @@ -26,12 +29,14 @@ public static Object convertPolyglotValue(Value item) { return item.as(Object.class); } - /** A helper functions for situations where we cannot use the Value conversion directly. - *

- * Mostly happens due to the issue: https://github.com/oracle/graal/issues/4967 - * Once that issue is resolved, we should probably remove this helper. - *

- * In that case we take a generic Object, knowing that the values of interest to us will be passed as Value anyway - so we can check that and fire the conversion if needed. + /** + * A helper functions for situations where we cannot use the Value conversion directly. + * + *

Mostly happens due to the issue: https://github.com/oracle/graal/issues/4967 Once that issue + * is resolved, we should probably remove this helper. + * + *

In that case we take a generic Object, knowing that the values of interest to us will be + * passed as Value anyway - so we can check that and fire the conversion if needed. */ public static Object convertPolyglotValue(Object item) { if (item instanceof Value v) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/NumericConverter.java b/std-bits/table/src/main/java/org/enso/table/data/NumericConverter.java deleted file mode 100644 index 8c1fc2fd27f2..000000000000 --- a/std-bits/table/src/main/java/org/enso/table/data/NumericConverter.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.enso.table.data; - -import java.math.BigDecimal; - -public class NumericConverter { - public static double toDouble(Object o) { - return switch (o) { - case Double x -> x; - case BigDecimal x -> x.doubleValue(); - default -> (double) toLong(o); - }; - } - - public static long toLong(Object o) { - return switch (o) { - case Long x -> x; - case Integer x -> x.longValue(); - case Short x -> x.longValue(); - case Byte x -> x.longValue(); - default -> throw new UnsupportedOperationException(); - }; - } - - public static boolean isCoercibleToDouble(Object o) { - return o instanceof Double || o instanceof BigDecimal || isCoercibleToLong(o); - } - - public static boolean isCoercibleToLong(Object o) { - return o instanceof Long || o instanceof Integer || o instanceof Short || o instanceof Byte; - } - - public static Double tryToDouble(Object o) { - return switch (o) { - case Double x -> x; - case BigDecimal x -> x.doubleValue(); - case Long x -> x.doubleValue(); - case Integer x -> x.doubleValue(); - case Short x -> x.doubleValue(); - case Byte x -> x.doubleValue(); - case null, default -> null; - }; - } - - public static Long tryToLong(Object o) { - return switch (o) { - case Long x -> x; - case Integer x -> x.longValue(); - case Short x -> x.longValue(); - case Byte x -> x.longValue(); - case null, default -> null; - }; - } -} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/object/InferredBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/object/InferredBuilder.java index 68b3bce03008..52cac4487e86 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/object/InferredBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/object/InferredBuilder.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.builder.object; +import org.enso.base.polyglot.NumericConverter; import org.enso.table.data.column.storage.Storage; import java.math.BigDecimal; @@ -81,10 +82,10 @@ private void initBuilderFor(Object o) { int initialCapacity = Math.max(initialSize, currentSize); if (o instanceof Boolean) { currentBuilder = new BoolBuilder(); - } else if (o instanceof Double || o instanceof BigDecimal) { - currentBuilder = NumericBuilder.createDoubleBuilder(initialCapacity); - } else if (o instanceof Long) { + } else if (NumericConverter.isCoercibleToLong(o)) { currentBuilder = NumericBuilder.createLongBuilder(initialCapacity); + } else if (NumericConverter.isCoercibleToDouble(o)) { + currentBuilder = NumericBuilder.createDoubleBuilder(initialCapacity); } else if (o instanceof LocalDate) { currentBuilder = new DateBuilder(initialCapacity); } else if (o instanceof LocalTime) { @@ -106,11 +107,16 @@ private record RetypeInfo(Class clazz, int type) {} new RetypeInfo(Boolean.class, Storage.Type.BOOL), new RetypeInfo(Long.class, Storage.Type.LONG), new RetypeInfo(Double.class, Storage.Type.DOUBLE), + new RetypeInfo(String.class, Storage.Type.STRING), new RetypeInfo(BigDecimal.class, Storage.Type.DOUBLE), new RetypeInfo(LocalDate.class, Storage.Type.DATE), new RetypeInfo(LocalTime.class, Storage.Type.TIME_OF_DAY), new RetypeInfo(ZonedDateTime.class, Storage.Type.DATE_TIME), - new RetypeInfo(String.class, Storage.Type.STRING)); + new RetypeInfo(Float.class, Storage.Type.DOUBLE), + new RetypeInfo(Integer.class, Storage.Type.LONG), + new RetypeInfo(Short.class, Storage.Type.LONG), + new RetypeInfo(Byte.class, Storage.Type.LONG) + ); private void retypeAndAppend(Object o) { for (RetypeInfo info : retypePairs) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/object/NumericBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/object/NumericBuilder.java index dac4fe9885e3..ff9835713478 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/object/NumericBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/object/NumericBuilder.java @@ -1,11 +1,10 @@ package org.enso.table.data.column.builder.object; -import org.enso.table.data.NumericConverter; +import org.enso.base.polyglot.NumericConverter; import org.enso.table.data.column.storage.DoubleStorage; import org.enso.table.data.column.storage.LongStorage; import org.enso.table.data.column.storage.Storage; -import java.math.BigDecimal; import java.util.Arrays; import java.util.BitSet; @@ -70,10 +69,10 @@ public void appendNoGrow(Object o) { if (o == null) { isMissing.set(currentSize++); } else if (isDouble) { - double value = NumericConverter.toDouble(o); + double value = NumericConverter.coerceToDouble(o); data[currentSize++] = Double.doubleToRawLongBits(value); } else { - data[currentSize++] = NumericConverter.toLong(o); + data[currentSize++] = NumericConverter.coerceToLong(o); } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/aggregate/FunctionAggregator.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/aggregate/FunctionAggregator.java index 048bdeba739a..a50f08c35d9a 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/aggregate/FunctionAggregator.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/aggregate/FunctionAggregator.java @@ -1,6 +1,6 @@ package org.enso.table.data.column.operation.aggregate; -import org.enso.base.Polyglot_Utils; +import org.enso.base.polyglot.Polyglot_Utils; import org.enso.table.data.column.builder.object.InferredBuilder; import org.enso.table.data.column.storage.Storage; import org.graalvm.polyglot.Value; diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/SpecializedIsInOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/SpecializedIsInOp.java index fc10eceec680..bc3c056dd514 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/SpecializedIsInOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/SpecializedIsInOp.java @@ -1,37 +1,68 @@ package org.enso.table.data.column.operation.map; -import org.enso.base.Polyglot_Utils; -import org.enso.table.data.NumericConverter; -import org.enso.table.data.column.storage.BoolStorage; -import org.enso.table.data.column.storage.Storage; -import org.graalvm.polyglot.Value; - import java.util.BitSet; import java.util.HashSet; import java.util.List; import java.util.function.Function; +import org.enso.base.polyglot.Polyglot_Utils; +import org.enso.table.data.column.storage.BoolStorage; +import org.enso.table.data.column.storage.Storage; +/** + * A specialized implementation for the IS_IN operation for builtin types, relying on hashing. Since + * for some columns we know what types of objects can be stored, we can filter out any objects that + * do not match that type and then rely on a consistent definition of hashcode for these builtin + * types (which is not available in general for custom objects). + */ public class SpecializedIsInOp extends MapOperation { + /** + * An optimized representation of the vector of values to match. + * + *

It indicates whether the vector contained a null value and contains a hashmap of the vector + * elements for faster contains checks. + */ public record CompactRepresentation(HashSet coercedValues, boolean hasNulls) {} + private final Function, CompactRepresentation> prepareList; - public static SpecializedIsInOp make(Function, CompactRepresentation> prepareList) { + /** + * Creates a new operation with a given preprocessing function. + * + *

The responsibility of the function is to analyse the list and create a hashmap of relevant + * elements, coerced to a type that is consistent with the storage type of the given column. Any + * elements not fitting the expected type can (and should) be discarded. + * + *

It is important to correctly coerce the types, for example in Enso 2 == 2.0, so if we are + * getting a Long for a DoubleColumn, it should be converted to a Double before adding it to the + * hashmap. Similarly, for LongStorage, non-integer Doubles can be ignored, but Doubles with 0 + * fractional part need to be converted into a Long. These conversions can be achieved with the + * {@code NumericConverter} class. + */ + public static SpecializedIsInOp make( + Function, CompactRepresentation> prepareList) { return new SpecializedIsInOp<>(prepareList); } - public static SpecializedIsInOp makeForTimeColumns() { - return SpecializedIsInOp.make(list -> { - HashSet set = new HashSet<>(); - boolean hasNulls = false; - for (Object o : list) { - hasNulls |= o == null; - Object coerced = Polyglot_Utils.convertPolyglotValue(o); - if (coerced != null) { - set.add(coerced); - } - } - return new SpecializedIsInOp.CompactRepresentation(set, hasNulls); - }); + /** + * Creates a new operation which ensures the Enso Date/Time types are correctly coerced. + * + *

It uses the provided {@code storageClass} to only keep the elements that are of the same + * type as expected in the storage. + */ + public static SpecializedIsInOp makeForTimeColumns(Class storageClass) { + return SpecializedIsInOp.make( + list -> { + HashSet set = new HashSet<>(); + boolean hasNulls = false; + for (Object o : list) { + hasNulls |= o == null; + Object coerced = Polyglot_Utils.convertPolyglotValue(o); + if (storageClass.isInstance(coerced)) { + set.add(coerced); + } + } + return new SpecializedIsInOp.CompactRepresentation(set, hasNulls); + }); } SpecializedIsInOp(Function, CompactRepresentation> prepareList) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/bool/BooleanIsInOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/bool/BooleanIsInOp.java index 88aa7b4642ea..70f5b8e97414 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/bool/BooleanIsInOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/bool/BooleanIsInOp.java @@ -1,23 +1,23 @@ package org.enso.table.data.column.operation.map.bool; -import org.enso.base.Polyglot_Utils; +import java.util.BitSet; +import java.util.List; import org.enso.table.data.column.operation.map.MapOperation; import org.enso.table.data.column.storage.BoolStorage; import org.enso.table.data.column.storage.Storage; -import org.graalvm.polyglot.Value; - -import java.util.BitSet; -import java.util.HashSet; -import java.util.List; -import java.util.function.Function; -public class BooleanIsInOp extends MapOperation { +/** + * A specialized implementation for the IS_IN operation on booleans - since booleans have just three + * possible values we can have a highly efficient implementation that does not even rely on hashmap + * and after processing the input vector, performs the checks in constant time. + */ +public class BooleanIsInOp extends MapOperation { public BooleanIsInOp() { super(Storage.Maps.IS_IN); } @Override - public Storage runMap(T storage, Object arg) { + public Storage runMap(BoolStorage storage, Object arg) { if (arg instanceof List) { return runMap(storage, (List) arg); } else { @@ -25,7 +25,7 @@ public Storage runMap(T storage, Object arg) { } } - public Storage runMap(T storage, List arg) { + public Storage runMap(BoolStorage storage, List arg) { boolean hadTrue = false; boolean hadFalse = false; boolean hadNull = false; @@ -59,7 +59,7 @@ public Storage runMap(T storage, List arg) { } @Override - public Storage runZip(T storage, Storage arg) { + public Storage runZip(BoolStorage storage, Storage arg) { throw new IllegalStateException("Zip mode is not supported for this operation."); } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/BoolStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/BoolStorage.java index 3c303e923a58..183bed63f70a 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/BoolStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/BoolStorage.java @@ -289,7 +289,7 @@ public Storage runZip(BoolStorage storage, Storage arg) { } } }) - .add(new BooleanIsInOp<>()); + .add(new BooleanIsInOp()); return ops; } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/DateStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/DateStorage.java index 939a062ccff7..2bc198b38ce5 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/DateStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/DateStorage.java @@ -18,7 +18,7 @@ public DateStorage(LocalDate[] data, int size) { private static MapOpStorage> buildOps() { MapOpStorage> t = ObjectStorage.buildObjectOps(); - t.add(SpecializedIsInOp.makeForTimeColumns()); + t.add(SpecializedIsInOp.makeForTimeColumns(LocalDate.class)); return t; } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/DateTimeStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/DateTimeStorage.java index 6644af046220..8bb1680a63bd 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/DateTimeStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/DateTimeStorage.java @@ -18,7 +18,7 @@ public DateTimeStorage(ZonedDateTime[] data, int size) { private static MapOpStorage> buildOps() { MapOpStorage> t = ObjectStorage.buildObjectOps(); - t.add(SpecializedIsInOp.makeForTimeColumns()); + t.add(SpecializedIsInOp.makeForTimeColumns(ZonedDateTime.class)); return t; } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/DoubleStorage.java index 5ef3dd61b4b5..03421e57bc09 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/DoubleStorage.java @@ -1,7 +1,6 @@ package org.enso.table.data.column.storage; -import org.enso.base.ObjectComparator; -import org.enso.table.data.NumericConverter; +import org.enso.base.polyglot.NumericConverter; import org.enso.table.data.column.builder.object.NumericBuilder; import org.enso.table.data.column.operation.map.MapOpStorage; import org.enso.table.data.column.operation.map.SpecializedIsInOp; @@ -262,7 +261,7 @@ public Storage run(DoubleStorage storage) { boolean hasNulls = false; for (Object o : list) { hasNulls |= o == null; - Double x = NumericConverter.tryToDouble(o); + Double x = NumericConverter.tryConvertingToDouble(o); if (x != null) { set.add(x); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/LongStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/LongStorage.java index 34c96532002a..2de255c9ff0a 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/LongStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/LongStorage.java @@ -1,6 +1,6 @@ package org.enso.table.data.column.storage; -import org.enso.table.data.NumericConverter; +import org.enso.base.polyglot.NumericConverter; import org.enso.table.data.column.builder.object.NumericBuilder; import org.enso.table.data.column.operation.aggregate.Aggregator; import org.enso.table.data.column.operation.aggregate.numeric.LongToLongAggregator; @@ -372,7 +372,7 @@ public Storage run(LongStorage storage) { boolean hasNulls = false; for (Object o : list) { hasNulls |= o == null; - Long x = NumericConverter.tryToLong(o); + Long x = NumericConverter.tryConvertingToLong(o); if (x != null) { set.add(x); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java index 901670e77a85..af521b9d75db 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java @@ -1,6 +1,6 @@ package org.enso.table.data.column.storage; -import org.enso.base.Polyglot_Utils; +import org.enso.base.polyglot.Polyglot_Utils; import org.enso.table.data.column.builder.object.Builder; import org.enso.table.data.column.builder.object.InferredBuilder; import org.enso.table.data.column.builder.object.ObjectBuilder; diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/TimeOfDayStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/TimeOfDayStorage.java index 5dad0ed42750..c9c3b4d70185 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/TimeOfDayStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/TimeOfDayStorage.java @@ -18,7 +18,7 @@ public TimeOfDayStorage(LocalTime[] data, int size) { private static MapOpStorage> buildOps() { MapOpStorage> t = ObjectStorage.buildObjectOps(); - t.add(SpecializedIsInOp.makeForTimeColumns()); + t.add(SpecializedIsInOp.makeForTimeColumns(LocalTime.class)); return t; } diff --git a/std-bits/table/src/main/java/org/enso/table/data/table/Column.java b/std-bits/table/src/main/java/org/enso/table/data/table/Column.java index 068ad175eb23..9f8ab03ad543 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/table/Column.java +++ b/std-bits/table/src/main/java/org/enso/table/data/table/Column.java @@ -1,6 +1,6 @@ package org.enso.table.data.table; -import org.enso.base.Polyglot_Utils; +import org.enso.base.polyglot.Polyglot_Utils; import org.enso.table.data.column.builder.object.InferredBuilder; import org.enso.table.data.column.operation.aggregate.Aggregator; import org.enso.table.data.column.storage.BoolStorage; @@ -13,8 +13,6 @@ import org.enso.table.error.UnexpectedColumnTypeException; import org.graalvm.polyglot.Value; -import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.BitSet; import java.util.List; import java.util.function.Function; diff --git a/test/Table_Tests/src/Table_Spec.enso b/test/Table_Tests/src/Table_Spec.enso index b732b8cfae11..c2563fafa9f5 100644 --- a/test/Table_Tests/src/Table_Spec.enso +++ b/test/Table_Tests/src/Table_Spec.enso @@ -917,6 +917,10 @@ spec = t.filter "B" (Filter_Condition.Is_In [False]) . at "B" . to_vector . should_equal [False, False, False] t.filter "C" (Filter_Condition.Is_In [False, False]) . at "C" . to_vector . should_equal [False] + t2 = Table.new [["ints", [1, 2, 3]], ["doubles", [1.2, 0.0, 1.0]]] + t2.filter "ints" (Filter_Condition.Is_In [2.0, 1.5, 3, 4]) . at "ints" . to_vector . should_equal [2, 3] + t2.filter "doubles" (Filter_Condition.Is_In [0.1, 1, 3, 1.2]) . at "doubles" . to_vector . should_equal [1.2, 1.0] + Test.specify "should perform `Is_In` efficiently for builtin types" <| t = Table.new [["X", (200.up_to 10000 . to_vector)]] vec = 4000.up_to 13000 . to_vector