diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/DoubleBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/DoubleBuilder.java index db2d250d8462..7625acb2b99e 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/DoubleBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/DoubleBuilder.java @@ -89,7 +89,7 @@ public void appendBulkStorage(Storage storage) { if (storage instanceof DoubleStorage doubleStorage) { int n = (int) doubleStorage.getSize(); ensureFreeSpaceFor(n); - System.arraycopy(doubleStorage.getRawData(), 0, data, currentSize, n); + System.arraycopy(doubleStorage.getArray(), 0, data, currentSize, n); BitSets.copy(doubleStorage.getIsNothingMap(), isNothing, currentSize, n); currentSize += n; } else if (storage instanceof ColumnDoubleStorage doubleStorage) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/LongBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/LongBuilder.java index 72dbddbd1f8f..52cfe732ddee 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/LongBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/LongBuilder.java @@ -92,7 +92,7 @@ public void appendBulkStorage(Storage storage) { // A fast path for the same type - no conversions/checks needed. int n = (int) longStorage.getSize(); ensureFreeSpaceFor(n); - System.arraycopy(longStorage.getRawData(), 0, data, currentSize, n); + System.arraycopy(longStorage.getArray(), 0, data, currentSize, n); BitSets.copy(longStorage.getIsNothingMap(), isNothing, currentSize, n); currentSize += n; } else if (storage.getType() instanceof IntegerType otherType && getType().fits(otherType)) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/StorageIterators.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/StorageIterators.java index f17dc0879a3b..2ac19eba6092 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/StorageIterators.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/StorageIterators.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.operation; +import java.util.function.LongFunction; import org.enso.table.data.column.builder.Builder; import org.enso.table.data.column.builder.BuilderForType; import org.enso.table.data.column.storage.ColumnBooleanStorage; @@ -26,15 +27,16 @@ public static ColumnStorage buildObjectOverStorage( boolean preserveNothing, Builder builder, BuildObjectOperation operation) { - long size = source.getSize(); Context context = Context.getCurrent(); - for (long index = 0; index < size; index++) { - if (preserveNothing && source.isNothing(index)) { + long idx = 0; + for (S item : source) { + if (preserveNothing && item == null) { builder.appendNulls(1); } else { - operation.apply(builder, index, source.getItemBoxed(index)); + operation.apply(builder, idx, item); } context.safepoint(); + idx++; } return builder.seal(); } @@ -45,13 +47,17 @@ public static ColumnStorage buildObjectOverDoubleStorage( boolean preserveNothing, Builder builder, DoubleBuildObjectOperation operation) { - long size = source.getSize(); Context context = Context.getCurrent(); - for (long index = 0; index < size; index++) { - if (preserveNothing && source.isNothing(index)) { - builder.appendNulls(1); + var iterator = source.iterator(); + while (iterator.moveNext()) { + if (iterator.isNothing()) { + if (preserveNothing) { + builder.appendNulls(1); + } else { + operation.apply(builder, iterator.getIndex(), Double.NaN, true); + } } else { - operation.apply(builder, index, source.getItemAsDouble(index), source.isNothing(index)); + operation.apply(builder, iterator.getIndex(), iterator.getItemAsDouble(), false); } context.safepoint(); } @@ -87,10 +93,10 @@ public interface BooleanBuildOperation> { * @param source the source storage to read from and iterate over. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Builder type. * @param Input Java type. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static , S, T> ColumnStorage buildOverStorage( ColumnStorage source, B builder, BuildOperation operation) { @@ -107,22 +113,23 @@ public static , S, T> ColumnStorage buildOverStor * the operation. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Builder type. * @param Input Java type. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static , S, T> ColumnStorage buildOverStorage( ColumnStorage source, boolean preserveNothing, B builder, BuildOperation operation) { - long size = source.getSize(); Context context = Context.getCurrent(); - for (long index = 0; index < size; index++) { - if (preserveNothing && source.isNothing(index)) { + long idx = 0; + for (S item : source) { + if (preserveNothing && item == null) { builder.appendNulls(1); } else { - operation.apply(builder, index, source.getItemBoxed(index)); + operation.apply(builder, idx, item); } context.safepoint(); + idx++; } return builder.seal(); } @@ -136,9 +143,9 @@ public static , S, T> ColumnStorage buildOverStor * @param source the source storage to read from and iterate over. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Builder type. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static , T> ColumnStorage buildOverLongStorage( ColumnLongStorage source, B builder, LongBuildOperation operation) { @@ -155,22 +162,26 @@ public static , T> ColumnStorage buildOverLongSto * the operation. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Builder type. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static , T> ColumnStorage buildOverLongStorage( ColumnLongStorage source, boolean preserveNothing, B builder, LongBuildOperation operation) { - long size = source.getSize(); Context context = Context.getCurrent(); - for (long index = 0; index < size; index++) { - if (preserveNothing && source.isNothing(index)) { - builder.appendNulls(1); + var iterator = source.iterator(); + while (iterator.moveNext()) { + if (iterator.isNothing()) { + if (preserveNothing) { + builder.appendNulls(1); + } else { + operation.apply(builder, iterator.getIndex(), 0, true); + } } else { - operation.apply(builder, index, source.getItemAsLong(index), source.isNothing(index)); + operation.apply(builder, iterator.getIndex(), iterator.getItemAsLong(), false); } context.safepoint(); } @@ -186,9 +197,9 @@ public static , T> ColumnStorage buildOverLongSto * @param source the source storage to read from and iterate over. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Builder type. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static , T> ColumnStorage buildOverDoubleStorage( ColumnDoubleStorage source, B builder, DoubleBuildOperation operation) { @@ -205,22 +216,26 @@ public static , T> ColumnStorage buildOverDoubleS * the operation. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Builder type. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static , T> ColumnStorage buildOverDoubleStorage( ColumnDoubleStorage source, boolean preserveNothing, B builder, DoubleBuildOperation operation) { - long size = source.getSize(); Context context = Context.getCurrent(); - for (long index = 0; index < size; index++) { - if (preserveNothing && source.isNothing(index)) { - builder.appendNulls(1); + var iterator = source.iterator(); + while (iterator.moveNext()) { + if (iterator.isNothing()) { + if (preserveNothing) { + builder.appendNulls(1); + } else { + operation.apply(builder, iterator.getIndex(), Double.NaN, true); + } } else { - operation.apply(builder, index, source.getItemAsDouble(index), source.isNothing(index)); + operation.apply(builder, iterator.getIndex(), iterator.getItemAsDouble(), false); } context.safepoint(); } @@ -236,9 +251,9 @@ public static , T> ColumnStorage buildOverDoubleS * @param source the source storage to read from and iterate over. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Builder type. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static , T> ColumnStorage buildOverBooleanStorage( ColumnBooleanStorage source, B builder, BooleanBuildOperation operation) { @@ -255,22 +270,26 @@ public static , T> ColumnStorage buildOverBoolean * the operation. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Builder type. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static , T> ColumnStorage buildOverBooleanStorage( ColumnBooleanStorage source, boolean preserveNothing, B builder, BooleanBuildOperation operation) { - long size = source.getSize(); Context context = Context.getCurrent(); - for (long index = 0; index < size; index++) { - if (preserveNothing && source.isNothing(index)) { - builder.appendNulls(1); + var iterator = source.iterator(); + while (iterator.moveNext()) { + if (iterator.isNothing()) { + if (preserveNothing) { + builder.appendNulls(1); + } else { + operation.apply(builder, iterator.getIndex(), false, true); + } } else { - operation.apply(builder, index, source.getItemAsBoolean(index), source.isNothing(index)); + operation.apply(builder, iterator.getIndex(), iterator.getItemAsBoolean(), false); } context.safepoint(); } @@ -278,7 +297,7 @@ public static , T> ColumnStorage buildOverBoolean } @FunctionalInterface - public interface MapOperation { + public interface MapOperation { T apply(long index, S value); } @@ -305,12 +324,12 @@ public interface BooleanMapOperation { * @param source the source storage to read from and iterate over. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Input Java type. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static ColumnStorage mapOverStorage( - ColumnStorage source, BuilderForType builder, MapOperation operation) { + ColumnStorage source, BuilderForType builder, MapOperation operation) { return mapOverStorage(source, true, builder, operation); } @@ -323,24 +342,25 @@ public static ColumnStorage mapOverStorage( * the operation. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Input Java type. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static ColumnStorage mapOverStorage( ColumnStorage source, boolean preserveNothing, BuilderForType builder, - MapOperation operation) { - long size = source.getSize(); + MapOperation operation) { Context context = Context.getCurrent(); - for (long index = 0; index < size; index++) { - if (preserveNothing && source.isNothing(index)) { + long idx = 0; + for (S item : source) { + if (preserveNothing && item == null) { builder.appendNulls(1); } else { - var result = operation.apply(index, source.getItemBoxed(index)); + var result = operation.apply(idx, item); builder.append(result); } + idx++; context.safepoint(); } return builder.seal(); @@ -354,8 +374,8 @@ public static ColumnStorage mapOverStorage( * @param source the source storage to read from and iterate over. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static ColumnStorage mapOverLongStorage( ColumnLongStorage source, BuilderForType builder, LongMapOperation operation) { @@ -371,21 +391,26 @@ public static ColumnStorage mapOverLongStorage( * the operation. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static ColumnStorage mapOverLongStorage( ColumnLongStorage source, boolean preserveNothing, BuilderForType builder, LongMapOperation operation) { - long size = source.getSize(); Context context = Context.getCurrent(); - for (long index = 0; index < size; index++) { - if (preserveNothing && source.isNothing(index)) { - builder.appendNulls(1); + var iterator = source.iterator(); + while (iterator.moveNext()) { + if (iterator.isNothing()) { + if (preserveNothing) { + builder.appendNulls(1); + } else { + var result = operation.apply(iterator.getIndex(), 0, true); + builder.append(result); + } } else { - var result = operation.apply(index, source.getItemAsLong(index), source.isNothing(index)); + var result = operation.apply(iterator.getIndex(), iterator.getItemAsLong(), false); builder.append(result); } context.safepoint(); @@ -401,8 +426,8 @@ public static ColumnStorage mapOverLongStorage( * @param source the source storage to read from and iterate over. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static ColumnStorage mapOverDoubleStorage( ColumnDoubleStorage source, BuilderForType builder, DoubleMapOperation operation) { @@ -418,21 +443,26 @@ public static ColumnStorage mapOverDoubleStorage( * the operation. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static ColumnStorage mapOverDoubleStorage( ColumnDoubleStorage source, boolean preserveNothing, BuilderForType builder, DoubleMapOperation operation) { - long size = source.getSize(); Context context = Context.getCurrent(); - for (long index = 0; index < size; index++) { - if (preserveNothing && source.isNothing(index)) { - builder.appendNulls(1); + var iterator = source.iterator(); + while (iterator.moveNext()) { + if (iterator.isNothing()) { + if (preserveNothing) { + builder.appendNulls(1); + } else { + var result = operation.apply(iterator.getIndex(), Double.NaN, true); + builder.append(result); + } } else { - var result = operation.apply(index, source.getItemAsDouble(index), source.isNothing(index)); + var result = operation.apply(iterator.getIndex(), iterator.getItemAsDouble(), false); builder.append(result); } context.safepoint(); @@ -448,8 +478,8 @@ public static ColumnStorage mapOverDoubleStorage( * @param source the source storage to read from and iterate over. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static ColumnStorage mapOverBooleanStorage( ColumnBooleanStorage source, BuilderForType builder, BooleanMapOperation operation) { @@ -465,26 +495,259 @@ public static ColumnStorage mapOverBooleanStorage( * the operation. * @param builder the output builder. * @param operation a callback to process a single value. - * @return a built ColumnStorage from sealing the builder. * @param Output Java type. + * @return a built ColumnStorage from sealing the builder. */ public static ColumnStorage mapOverBooleanStorage( ColumnBooleanStorage source, boolean preserveNothing, BuilderForType builder, BooleanMapOperation operation) { - long size = source.getSize(); Context context = Context.getCurrent(); - for (long index = 0; index < size; index++) { - if (preserveNothing && source.isNothing(index)) { + var iterator = source.iterator(); + while (iterator.moveNext()) { + if (iterator.isNothing()) { + if (preserveNothing) { + builder.appendNulls(1); + } else { + var result = operation.apply(iterator.getIndex(), false, true); + builder.append(result); + } + } else { + var result = operation.apply(iterator.getIndex(), iterator.getItemAsBoolean(), false); + builder.append(result); + } + context.safepoint(); + } + return builder.seal(); + } + + @FunctionalInterface + public interface ZipOperation { + T apply(long index, R value1, S value2); + } + + @FunctionalInterface + public interface LongZipOperation { + // Note if isNothing1 is true then value1 is undefined, likewise for isNothing2 and value2. + T apply(long index, long value1, boolean isNothing1, long value2, boolean isNothing2); + } + + @FunctionalInterface + public interface LongDoubleZipOperation { + // Note if isNothing1 is true then value1 is undefined, likewise for isNothing2 and value2. + T apply(long index, long value1, boolean isNothing1, double value2, boolean isNothing2); + } + + @FunctionalInterface + public interface DoubleLongZipOperation { + // Note if isNothing1 is true then value1 is undefined, likewise for isNothing2 and value2. + T apply(long index, double value1, boolean isNothing1, long value2, boolean isNothing2); + } + + @FunctionalInterface + public interface DoubleZipOperation { + // Note if isNothing1 is true then value1 is undefined, likewise for isNothing2 and value2. + T apply(long index, double value1, boolean isNothing1, double value2, boolean isNothing2); + } + + /** + * Zips two storages together, applying an operation to each pair of values. The operation's + * result is appended to the builder. The builderConstructor will be passed the expected size to + * create a new builder. If skipNothing is true, then if either value is Nothing, the result will + * be Nothing and appended automatically. + * + * @param source1 the first source storage to read from and iterate over. + * @param source2 the second source storage to read from and iterate over. + * @param builderConstructor a function to create a new builder of the correct type. + * @param skipNothing if true, then if either value is Nothing, the result will be Nothing. + * @param operation a callback to process a pair of values. + * @param Input Java type for the first source. + * @param Input Java type for the second source. + * @param Output Java type for the storage. + * @return a built ColumnStorage from sealing the builder. + */ + public static ColumnStorage zipOverStorages( + ColumnStorage source1, + ColumnStorage source2, + LongFunction> builderConstructor, + boolean skipNothing, + ZipOperation operation) { + var iterator1 = source1.iterator(); + var iterator2 = source2.iterator(); + + long size = Math.max(source1.getSize(), source2.getSize()); + var builder = builderConstructor.apply(size); + + Context context = Context.getCurrent(); + for (long idx = 0; idx < size; idx++) { + R value1 = iterator1.hasNext() ? iterator1.next() : null; + S value2 = iterator2.hasNext() ? iterator2.next() : null; + if (skipNothing && (value1 == null || value2 == null)) { builder.appendNulls(1); } else { - var result = - operation.apply(index, source.getItemAsBoolean(index), source.isNothing(index)); + var result = operation.apply(idx, value1, value2); builder.append(result); } context.safepoint(); } + + return builder.seal(); + } + + /** + * Zips two long storages together, applying an operation to each pair of values. The operation's + * result is appended to the builder. The builderConstructor will be passed the expected size to + * create a new builder. If skipNothing is true, then if either value is Nothing, the result will + * be Nothing and appended automatically. + * + * @param source1 the first source storage to read from and iterate over. + * @param source2 the second source storage to read from and iterate over. + * @param builderConstructor a function to create a new builder of the correct type. + * @param skipNothing if true, then if either value is Nothing, the result will be Nothing. + * @param operation a callback to process a pair of values. + * @param Output Java type for the storage. + * @return a built ColumnStorage from sealing the builder. + */ + public static ColumnStorage zipOverLongStorages( + ColumnLongStorage source1, + ColumnLongStorage source2, + LongFunction> builderConstructor, + boolean skipNothing, + LongZipOperation operation) { + long size = Math.max(source1.getSize(), source2.getSize()); + var builder = builderConstructor.apply(size); + + source1 + .iterator() + .zip( + source2, + (idx, value1, isNothing1, value2, isNothing2) -> { + if (skipNothing && (isNothing1 || isNothing2)) { + builder.appendNulls(1); + } else { + var result = operation.apply(idx, value1, isNothing1, value2, isNothing2); + builder.append(result); + } + }); + + return builder.seal(); + } + + /** + * Zips a long and a double storages together, applying an operation to each pair of values. The + * operation's result is appended to the builder. The builderConstructor will be passed the + * expected size to create a new builder. If skipNothing is true, then if either value is Nothing, + * the result will be Nothing and appended automatically. + * + * @param source1 the first source storage to read from and iterate over. + * @param source2 the second source storage to read from and iterate over. + * @param builderConstructor a function to create a new builder of the correct type. + * @param skipNothing if true, then if either value is Nothing, the result will be Nothing. + * @param operation a callback to process a pair of values. + * @param Output Java type for the storage. + * @return a built ColumnStorage from sealing the builder. + */ + public static ColumnStorage zipOverLongDoubleStorages( + ColumnLongStorage source1, + ColumnDoubleStorage source2, + LongFunction> builderConstructor, + boolean skipNothing, + LongDoubleZipOperation operation) { + long size = Math.max(source1.getSize(), source2.getSize()); + var builder = builderConstructor.apply(size); + + source1 + .iterator() + .zip( + source2, + (idx, value1, isNothing1, value2, isNothing2) -> { + if (skipNothing && (isNothing1 || isNothing2)) { + builder.appendNulls(1); + } else { + var result = operation.apply(idx, value1, isNothing1, value2, isNothing2); + builder.append(result); + } + }); + + return builder.seal(); + } + + /** + * Zips a long and a double storages together, applying an operation to each pair of values. The + * operation's result is appended to the builder. The builderConstructor will be passed the + * expected size to create a new builder. If skipNothing is true, then if either value is Nothing, + * the result will be Nothing and appended automatically. + * + * @param source1 the first source storage to read from and iterate over. + * @param source2 the second source storage to read from and iterate over. + * @param builderConstructor a function to create a new builder of the correct type. + * @param skipNothing if true, then if either value is Nothing, the result will be Nothing. + * @param operation a callback to process a pair of values. + * @param Output Java type for the storage. + * @return a built ColumnStorage from sealing the builder. + */ + public static ColumnStorage zipOverDoubleLongStorages( + ColumnDoubleStorage source1, + ColumnLongStorage source2, + LongFunction> builderConstructor, + boolean skipNothing, + DoubleLongZipOperation operation) { + long size = Math.max(source1.getSize(), source2.getSize()); + var builder = builderConstructor.apply(size); + + source1 + .iterator() + .zip( + source2, + (idx, value1, isNothing1, value2, isNothing2) -> { + if (skipNothing && (isNothing1 || isNothing2)) { + builder.appendNulls(1); + } else { + var result = operation.apply(idx, value1, isNothing1, value2, isNothing2); + builder.append(result); + } + }); + + return builder.seal(); + } + + /** + * Zips two double storages together, applying an operation to each pair of values. The + * operation's result is appended to the builder. The builderConstructor will be passed the + * expected size to create a new builder. If skipNothing is true, then if either value is Nothing, + * the result will be Nothing and appended automatically. + * + * @param source1 the first source storage to read from and iterate over. + * @param source2 the second source storage to read from and iterate over. + * @param builderConstructor a function to create a new builder of the correct type. + * @param skipNothing if true, then if either value is Nothing, the result will be Nothing. + * @param operation a callback to process a pair of values. + * @param Output Java type for the storage. + * @return a built ColumnStorage from sealing the builder. + */ + public static ColumnStorage zipOverDoubleStorages( + ColumnDoubleStorage source1, + ColumnDoubleStorage source2, + LongFunction> builderConstructor, + boolean skipNothing, + DoubleZipOperation operation) { + long size = Math.max(source1.getSize(), source2.getSize()); + var builder = builderConstructor.apply(size); + + source1 + .iterator() + .zip( + source2, + (idx, value1, isNothing1, value2, isNothing2) -> { + if (skipNothing && (isNothing1 || isNothing2)) { + builder.appendNulls(1); + } else { + var result = operation.apply(idx, value1, isNothing1, value2, isNothing2); + builder.append(result); + } + }); + return builder.seal(); } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpCoalescing.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpCoalescing.java index e97dfb0009af..a527d90ec89b 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpCoalescing.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpCoalescing.java @@ -3,15 +3,13 @@ import java.math.BigDecimal; import java.math.BigInteger; import org.enso.table.data.column.builder.Builder; +import org.enso.table.data.column.operation.StorageIterators; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; -import org.enso.table.data.column.operation.map.numeric.helpers.BigDecimalArrayAdapter; -import org.enso.table.data.column.operation.map.numeric.helpers.BigIntegerArrayAdapter; -import org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter; +import org.enso.table.data.column.storage.ColumnDoubleStorage; +import org.enso.table.data.column.storage.ColumnLongStorage; +import org.enso.table.data.column.storage.ColumnStorage; import org.enso.table.data.column.storage.Storage; -import org.enso.table.data.column.storage.numeric.AbstractLongStorage; import org.enso.table.data.column.storage.type.FloatType; -import org.enso.table.data.column.storage.type.IntegerType; -import org.graalvm.polyglot.Context; /** * A variant of NumericBinaryOpImplementation that has different null behaviour: if one of the @@ -24,224 +22,199 @@ public NumericBinaryOpCoalescing(String name) { } @Override - protected Storage runDoubleZip( - DoubleArrayAdapter a, DoubleArrayAdapter b, MapOperationProblemAggregator problemAggregator) { - long n = a.size(); - long m = Math.min(n, b.size()); - var builder = Builder.getForDouble(FloatType.FLOAT_64, n, problemAggregator); - Context context = Context.getCurrent(); - - for (long i = 0; i < n; i++) { - boolean aNothing = a.isNothing(i); - boolean bNothing = i >= m || b.isNothing(i); - if (aNothing && bNothing) { - builder.appendNulls(1); - } else { - double r; - if (aNothing) { - r = b.getItemAsDouble(i); - } else if (bNothing) { - r = a.getItemAsDouble(i); - } else { - r = doDouble(a.getItemAsDouble(i), b.getItemAsDouble(i), i, problemAggregator); - } - builder.appendDouble(r); - } - - context.safepoint(); + public Storage runBinaryMap( + I storage, Object arg, MapOperationProblemAggregator problemAggregator) { + if (arg == null) { + return storage; } - - return builder.seal(); + return super.runBinaryMap(storage, arg, problemAggregator); } @Override - protected Storage runDoubleMap( - DoubleArrayAdapter a, Double b, MapOperationProblemAggregator problemAggregator) { - if (b == null) { - return a.intoStorage(); - } + protected Storage runDoubleZip( + ColumnDoubleStorage a, + ColumnDoubleStorage b, + MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.zipOverDoubleStorages( + a, + b, + s -> Builder.getForDouble(FloatType.FLOAT_64, s, problemAggregator), + false, + (index, value1, isNothing1, value2, isNothing2) -> { + if (isNothing1 && isNothing2) { + return null; + } else if (isNothing1) { + return value2; + } else if (isNothing2) { + return value1; + } else { + return doDouble(value1, value2, index, problemAggregator); + } + }); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - double bNonNull = b; - Context context = Context.getCurrent(); - long n = a.size(); - var builder = Builder.getForDouble(FloatType.FLOAT_64, n, problemAggregator); - for (long i = 0; i < n; i++) { - builder.appendDouble( - a.isNothing(i) - ? bNonNull - : doDouble(a.getItemAsDouble(i), bNonNull, i, problemAggregator)); - context.safepoint(); - } + @Override + protected Storage runDoubleLongMap( + ColumnLongStorage a, Double b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverLongStorage( + a, + false, + Builder.getForDouble(FloatType.FLOAT_64, a.getSize(), problemAggregator), + (builder, index, value, isNothing) -> + builder.append( + isNothing ? b : doDouble((double) value, b, index, problemAggregator))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - return builder.seal(); + @Override + protected Storage runDoubleMap( + ColumnDoubleStorage a, Double b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverDoubleStorage( + a, + true, + Builder.getForDouble(FloatType.FLOAT_64, a.getSize(), problemAggregator), + (builder, index, value, isNothing) -> + builder.append(isNothing ? b : doDouble(value, b, index, problemAggregator))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } @Override protected Storage runLongZip( - AbstractLongStorage a, - AbstractLongStorage b, - MapOperationProblemAggregator problemAggregator) { - long n = a.getSize(); - long m = Math.min(n, b.getSize()); - var builder = Builder.getForLong(IntegerType.INT_64, n, problemAggregator); - Context context = Context.getCurrent(); - - for (long i = 0; i < n; i++) { - boolean aNothing = a.isNothing(i); - boolean bNothing = i >= m || b.isNothing(i); - if (aNothing && bNothing) { - builder.appendNulls(1); - } else { - if (aNothing) { - builder.appendLong(b.getItemAsLong(i)); - } else if (bNothing) { - builder.appendLong(a.getItemAsLong(i)); - } else { - Long r = doLong(a.getItemAsLong(i), b.getItemAsLong(i), i, problemAggregator); - if (r == null) { - builder.appendNulls(1); - } else { - builder.appendLong(r); - } - } - } - - context.safepoint(); - } - - return builder.seal(); + ColumnLongStorage a, ColumnLongStorage b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.zipOverLongStorages( + a, + b, + s -> Builder.getForLong(INTEGER_RESULT_TYPE, s, problemAggregator), + false, + (index, value1, isNothing1, value2, isNothing2) -> { + if (isNothing1 && isNothing2) { + return null; + } else if (isNothing1) { + return value2; + } else if (isNothing2) { + return value1; + } else { + return doLong(value1, value2, index, problemAggregator); + } + }); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } + @Override protected Storage runLongMap( - AbstractLongStorage a, Long b, MapOperationProblemAggregator problemAggregator) { - if (b == null) { - return a; - } - - long bNonNull = b; - Context context = Context.getCurrent(); - long n = a.getSize(); - var builder = Builder.getForLong(IntegerType.INT_64, n, problemAggregator); - for (long i = 0; i < n; i++) { - if (a.isNothing(i)) { - builder.appendLong(bNonNull); - } else { - Long r = doLong(a.getItemAsLong(i), bNonNull, i, problemAggregator); - if (r == null) { - builder.appendNulls(1); - } else { - builder.appendLong(r); - } - } - - context.safepoint(); - } - - return builder.seal(); + ColumnLongStorage a, Long b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverLongStorage( + a, + false, + Builder.getForLong(INTEGER_RESULT_TYPE, a.getSize(), problemAggregator), + (builder, index, value, isNothing) -> + builder.append(isNothing ? b : doLong(value, b, index, problemAggregator))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } + @Override protected Storage runBigIntegerZip( - BigIntegerArrayAdapter a, - BigIntegerArrayAdapter b, + ColumnStorage a, + ColumnStorage b, MapOperationProblemAggregator problemAggregator) { - long n = a.size(); - long m = Math.min(n, b.size()); - var builder = Builder.getForBigInteger(n, problemAggregator); - Context context = Context.getCurrent(); - - for (long i = 0; i < n; i++) { - BigInteger x = a.getItem(i); - BigInteger y = i >= m ? null : b.getItem(i); - if (x == null && y == null) { - builder.appendNulls(1); - } else { - if (x == null) { - builder.append(y); - } else if (y == null) { - builder.append(x); - } else { - BigInteger r = doBigInteger(x, y, i, problemAggregator); - builder.append(r); - } - } - context.safepoint(); - } + var result = + StorageIterators.zipOverStorages( + a, + b, + s -> Builder.getForBigInteger(s, problemAggregator), + false, + (index, x, y) -> { + if (x == null && y == null) { + return null; + } else if (x == null) { + return y; + } else if (y == null) { + return x; + } else { + return doBigInteger(x, y, index, problemAggregator); + } + }); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - return builder.seal(); + @Override + protected Storage runBigIntegerLongMap( + ColumnLongStorage a, BigInteger b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverLongStorage( + a, + false, + Builder.getForBigInteger(a.getSize(), problemAggregator), + (builder, index, value, isNothing) -> + builder.append( + isNothing + ? b + : doBigInteger(BigInteger.valueOf(value), b, index, problemAggregator))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } + @Override protected Storage runBigIntegerMap( - BigIntegerArrayAdapter a, BigInteger b, MapOperationProblemAggregator problemAggregator) { - if (b == null) { - return a.intoStorage(); - } - - Context context = Context.getCurrent(); - long n = a.size(); - var builder = Builder.getForBigInteger(n, problemAggregator); - for (long i = 0; i < n; i++) { - BigInteger x = a.getItem(i); - if (x == null) { - builder.append(b); - } else { - builder.append(doBigInteger(x, b, i, problemAggregator)); - } - - context.safepoint(); - } - - return builder.seal(); + ColumnStorage a, BigInteger b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.mapOverStorage( + a, + false, + Builder.getForBigInteger(a.getSize(), problemAggregator), + (index, value) -> value == null ? b : doBigInteger(value, b, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } + @Override protected Storage runBigDecimalZip( - BigDecimalArrayAdapter a, - BigDecimalArrayAdapter b, + ColumnStorage a, + ColumnStorage b, MapOperationProblemAggregator problemAggregator) { - long n = a.size(); - long m = Math.min(a.size(), b.size()); - var builder = Builder.getForBigDecimal(n); - Context context = Context.getCurrent(); - - for (long i = 0; i < n; i++) { - BigDecimal x = a.getItem(i); - BigDecimal y = i >= m ? null : b.getItem(i); - if (x == null && y == null) { - builder.appendNulls(1); - } else { - if (x == null) { - builder.append(y); - } else if (y == null) { - builder.append(x); - } else { - builder.append(doBigDecimal(x, y, i, problemAggregator)); - } - } - context.safepoint(); - } - - return builder.seal(); + var result = + StorageIterators.zipOverStorages( + a, + b, + s -> Builder.getForBigDecimal(s), + false, + (index, x, y) -> { + if (x == null && y == null) { + return null; + } else if (x == null) { + return y; + } else if (y == null) { + return x; + } else { + return doBigDecimal(x, y, index, problemAggregator); + } + }); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } + @Override protected Storage runBigDecimalMap( - BigDecimalArrayAdapter a, BigDecimal b, MapOperationProblemAggregator problemAggregator) { - if (b == null) { - return a.intoStorage(); - } - - Context context = Context.getCurrent(); - long n = a.size(); - var builder = Builder.getForBigDecimal(n); - for (long i = 0; i < n; i++) { - BigDecimal x = a.getItem(i); - if (x == null) { - builder.append(b); - } else { - builder.append(doBigDecimal(x, b, i, problemAggregator)); - } - - context.safepoint(); - } - - return builder.seal(); + ColumnStorage a, BigDecimal b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.mapOverStorage( + a, + false, + Builder.getForBigDecimal(a.getSize()), + (index, value) -> value == null ? b : doBigDecimal(value, b, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpDefinition.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpDefinition.java deleted file mode 100644 index 61f63cade6f6..000000000000 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpDefinition.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.enso.table.data.column.operation.map.numeric.arithmetic; - -import java.math.BigDecimal; -import java.math.BigInteger; -import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; - -public interface NumericBinaryOpDefinition { - double doDouble(double a, double b, long ix, MapOperationProblemAggregator problemAggregator); - - Long doLong(long a, long b, long ix, MapOperationProblemAggregator problemAggregator); - - BigInteger doBigInteger( - BigInteger a, BigInteger b, long ix, MapOperationProblemAggregator problemAggregator); - - BigDecimal doBigDecimal( - BigDecimal a, BigDecimal b, long ix, MapOperationProblemAggregator problemAggregator); -} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpImplementation.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpImplementation.java index 05c7b1a2310c..d467a242d9b4 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpImplementation.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpImplementation.java @@ -4,25 +4,57 @@ import java.math.BigInteger; import org.enso.base.polyglot.NumericConverter; import org.enso.table.data.column.builder.Builder; +import org.enso.table.data.column.operation.StorageIterators; import org.enso.table.data.column.operation.map.BinaryMapOperation; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; -import org.enso.table.data.column.operation.map.numeric.helpers.BigDecimalArrayAdapter; -import org.enso.table.data.column.operation.map.numeric.helpers.BigIntegerArrayAdapter; -import org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter; +import org.enso.table.data.column.storage.ColumnDoubleStorage; +import org.enso.table.data.column.storage.ColumnLongStorage; +import org.enso.table.data.column.storage.ColumnStorage; +import org.enso.table.data.column.storage.ColumnStorageFacade; import org.enso.table.data.column.storage.Storage; -import org.enso.table.data.column.storage.numeric.AbstractLongStorage; import org.enso.table.data.column.storage.numeric.BigDecimalStorage; import org.enso.table.data.column.storage.numeric.BigIntegerStorage; import org.enso.table.data.column.storage.numeric.DoubleStorage; +import org.enso.table.data.column.storage.numeric.DoubleStorageFacade; import org.enso.table.data.column.storage.numeric.LongStorage; import org.enso.table.data.column.storage.type.FloatType; import org.enso.table.data.column.storage.type.IntegerType; import org.enso.table.error.UnexpectedTypeException; -import org.graalvm.polyglot.Context; /** An operation expecting a numeric argument and returning a numeric column. */ public abstract class NumericBinaryOpImplementation> - extends BinaryMapOperation implements NumericBinaryOpDefinition { + extends BinaryMapOperation { + public static ColumnStorage asBigDecimal(ColumnStorage storage) { + return new ColumnStorageFacade<>(storage, BigDecimal::new); + } + + public static ColumnStorage asBigDecimal(ColumnDoubleStorage storage) { + return new ColumnStorageFacade<>(storage, BigDecimal::valueOf); + } + + public static ColumnStorage asBigDecimal(ColumnLongStorage storage) { + return new ColumnStorageFacade<>(storage, BigDecimal::valueOf); + } + + public static ColumnStorage asBigInteger(ColumnLongStorage storage) { + return new ColumnStorageFacade<>(storage, BigInteger::valueOf); + } + + protected abstract double doDouble( + double a, double b, long ix, MapOperationProblemAggregator problemAggregator); + + protected abstract Long doLong( + long a, long b, long ix, MapOperationProblemAggregator problemAggregator); + + protected abstract BigInteger doBigInteger( + BigInteger a, BigInteger b, long ix, MapOperationProblemAggregator problemAggregator); + + protected abstract BigDecimal doBigDecimal( + BigDecimal a, BigDecimal b, long ix, MapOperationProblemAggregator problemAggregator); + + static IllegalStateException newUnsupported(Object arg) { + return new IllegalStateException("Unsupported storage: " + arg.getClass().getCanonicalName()); + } // The type to use for small integer results (regardless of the input bit size). public static final IntegerType INTEGER_RESULT_TYPE = IntegerType.INT_64; @@ -32,325 +64,263 @@ public NumericBinaryOpImplementation(String name) { } @Override - public Storage runBinaryMap( + public Storage runBinaryMap( I storage, Object arg, MapOperationProblemAggregator problemAggregator) { if (arg == null) { return allNullStorageOfSameType(storage); + } + + if (arg instanceof BigInteger rhs) { + return switch (storage) { + case BigDecimalStorage s -> runBigDecimalMap(s, new BigDecimal(rhs), problemAggregator); + case BigIntegerStorage s -> runBigIntegerMap(s, rhs, problemAggregator); + case ColumnDoubleStorage s -> runDoubleMap(s, rhs.doubleValue(), problemAggregator); + case ColumnLongStorage s -> runBigIntegerLongMap(s, rhs, problemAggregator); + default -> throw newUnsupported(storage); + }; + } else if (NumericConverter.isCoercibleToLong(arg)) { + long argAsLong = NumericConverter.coerceToLong(arg); + return switch (storage) { + case BigDecimalStorage s -> runBigDecimalMap( + s, BigDecimal.valueOf(argAsLong), problemAggregator); + case BigIntegerStorage s -> runBigIntegerMap( + s, BigInteger.valueOf(argAsLong), problemAggregator); + case ColumnDoubleStorage s -> runDoubleMap(s, (double) argAsLong, problemAggregator); + case ColumnLongStorage s -> runLongMap(s, argAsLong, problemAggregator); + default -> throw newUnsupported(storage); + }; + } else if (NumericConverter.isCoercibleToDouble(arg)) { + double argAsDouble = NumericConverter.coerceToDouble(arg); + return switch (storage) { + case BigDecimalStorage s -> runBigDecimalMap( + s, BigDecimal.valueOf(argAsDouble), problemAggregator); + case BigIntegerStorage s -> runDoubleMap( + DoubleStorageFacade.forBigInteger(s), argAsDouble, problemAggregator); + case ColumnDoubleStorage s -> runDoubleMap(s, argAsDouble, problemAggregator); + case ColumnLongStorage s -> runDoubleLongMap(s, argAsDouble, problemAggregator); + default -> throw newUnsupported(storage); + }; + } else if (arg instanceof BigDecimal bd) { + return switch (storage) { + case BigDecimalStorage s -> runBigDecimalMap(s, bd, problemAggregator); + case BigIntegerStorage s -> runBigDecimalMap(asBigDecimal(s), bd, problemAggregator); + case ColumnDoubleStorage s -> runBigDecimalMap(asBigDecimal(s), bd, problemAggregator); + case ColumnLongStorage s -> runBigDecimalMap(asBigDecimal(s), bd, problemAggregator); + default -> throw newUnsupported(storage); + }; } else { - if (arg instanceof BigInteger rhs) { - return switch (storage) { - case AbstractLongStorage s -> runBigIntegerMap( - BigIntegerArrayAdapter.fromStorage(s), rhs, problemAggregator); - case BigIntegerStorage s -> runBigIntegerMap( - BigIntegerArrayAdapter.fromStorage(s), rhs, problemAggregator); - case BigDecimalStorage s -> runBigDecimalMap( - BigDecimalArrayAdapter.fromBigDecimalStorage(s), - new BigDecimal(rhs), - problemAggregator); - case DoubleStorage s -> runDoubleMap(s, rhs.doubleValue(), problemAggregator); - default -> throw new IllegalStateException( - "Unsupported storage: " + storage.getClass().getCanonicalName()); - }; - } else if (NumericConverter.isCoercibleToLong(arg)) { - long argAsLong = NumericConverter.coerceToLong(arg); - return switch (storage) { - case AbstractLongStorage s -> runLongMap(s, argAsLong, problemAggregator); - case BigIntegerStorage s -> runBigIntegerMap( - BigIntegerArrayAdapter.fromStorage(s), - BigInteger.valueOf(argAsLong), - problemAggregator); - case BigDecimalStorage s -> runBigDecimalMap( - BigDecimalArrayAdapter.fromBigDecimalStorage(s), - BigDecimal.valueOf(argAsLong), - problemAggregator); - case DoubleStorage s -> runDoubleMap(s, (double) argAsLong, problemAggregator); - default -> throw new IllegalStateException( - "Unsupported storage: " + storage.getClass().getCanonicalName()); - }; - } else if (NumericConverter.isCoercibleToDouble(arg)) { - double doubleArg = NumericConverter.coerceToDouble(arg); - return switch (storage) { - case AbstractLongStorage s -> runDoubleMap( - DoubleArrayAdapter.fromStorage(s), doubleArg, problemAggregator); - case BigIntegerStorage s -> runDoubleMap( - DoubleArrayAdapter.fromBigIntegerStorage(s), doubleArg, problemAggregator); - case BigDecimalStorage s -> runBigDecimalMap( - BigDecimalArrayAdapter.fromBigDecimalStorage(s), - BigDecimal.valueOf(doubleArg), - problemAggregator); - case DoubleStorage s -> runDoubleMap(s, doubleArg, problemAggregator); - default -> throw new IllegalStateException( - "Unsupported storage: " + storage.getClass().getCanonicalName()); - }; - } else if (arg instanceof BigDecimal bd) { - return runBigDecimalMap( - BigDecimalArrayAdapter.fromAnyStorage(storage), bd, problemAggregator); - } else { - throw new UnexpectedTypeException("a Number."); - } + throw new UnexpectedTypeException("a Number."); } } @Override public Storage runZip( I storage, Storage arg, MapOperationProblemAggregator problemAggregator) { - return switch (storage) { - case DoubleStorage lhs -> switch (arg) { - case BigDecimalStorage rhs -> { - BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromStorage(lhs); - BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromBigDecimalStorage(rhs); - yield runBigDecimalZip(left, right, problemAggregator); - } - default -> runDoubleZip(lhs, DoubleArrayAdapter.fromAnyStorage(arg), problemAggregator); + if (storage instanceof ColumnDoubleStorage lhs) { + return switch (arg) { + case BigDecimalStorage rhs -> runBigDecimalZip(asBigDecimal(lhs), rhs, problemAggregator); + case BigIntegerStorage rhs -> runDoubleZip( + lhs, DoubleStorageFacade.forBigInteger(rhs), problemAggregator); + case ColumnDoubleStorage rhs -> runDoubleZip(lhs, rhs, problemAggregator); + case ColumnLongStorage rhs -> runDoubleLongZip(lhs, rhs, problemAggregator); + default -> throw newUnsupported(arg); }; - - case AbstractLongStorage lhs -> switch (arg) { - case AbstractLongStorage rhs -> runLongZip(lhs, rhs, problemAggregator); - case BigIntegerStorage rhs -> { - BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs); - BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs); - yield runBigIntegerZip(left, right, problemAggregator); - } - case DoubleStorage rhs -> runDoubleZip( - DoubleArrayAdapter.fromStorage(lhs), rhs, problemAggregator); - case BigDecimalStorage rhs -> { - BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromStorage(lhs); - BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromBigDecimalStorage(rhs); - yield runBigDecimalZip(left, right, problemAggregator); - } - default -> throw new IllegalStateException( - "Unsupported storage: " + arg.getClass().getCanonicalName()); + } else if (storage instanceof ColumnLongStorage lhs) { + return switch (arg) { + case BigDecimalStorage rhs -> runBigDecimalZip(asBigDecimal(lhs), rhs, problemAggregator); + case BigIntegerStorage rhs -> runBigIntegerZip(asBigInteger(lhs), rhs, problemAggregator); + case ColumnDoubleStorage rhs -> runLongDoubleZip(lhs, rhs, problemAggregator); + case ColumnLongStorage rhs -> runLongZip(lhs, rhs, problemAggregator); + default -> throw newUnsupported(arg); }; - - case BigIntegerStorage lhs -> { - yield switch (arg) { - case AbstractLongStorage rhs -> { - BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs); - BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs); - yield runBigIntegerZip(left, right, problemAggregator); - } - case BigIntegerStorage rhs -> { - BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs); - BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs); - yield runBigIntegerZip(left, right, problemAggregator); - } - case DoubleStorage rhs -> runDoubleZip( - DoubleArrayAdapter.fromBigIntegerStorage(lhs), rhs, problemAggregator); - case BigDecimalStorage rhs -> { - BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromBigIntegerStorage(lhs); - BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromBigDecimalStorage(rhs); - yield runBigDecimalZip(left, right, problemAggregator); - } - default -> throw new IllegalStateException( - "Unsupported storage: " + arg.getClass().getCanonicalName()); - }; - } - - case BigDecimalStorage lhs -> { - BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromBigDecimalStorage(lhs); - BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromAnyStorage(arg); - yield runBigDecimalZip(left, right, problemAggregator); - } - - default -> throw new IllegalStateException( - "Unsupported storage: " + storage.getClass().getCanonicalName()); - }; - } - - protected Storage runDoubleZip( - DoubleArrayAdapter a, DoubleArrayAdapter b, MapOperationProblemAggregator problemAggregator) { - Context context = Context.getCurrent(); - long n = a.size(); - long m = Math.min(n, b.size()); - var builder = Builder.getForDouble(FloatType.FLOAT_64, n, problemAggregator); - for (long i = 0; i < m; i++) { - if (a.isNothing(i) || b.isNothing(i)) { - builder.appendNulls(1); - } else { - builder.append(doDouble(a.getItemAsDouble(i), b.getItemAsDouble(i), i, problemAggregator)); - } - - context.safepoint(); - } - - if (m < n) { - builder.appendNulls(Math.toIntExact(n - m)); + } else if (storage instanceof BigIntegerStorage lhs) { + return switch (arg) { + case BigDecimalStorage rhs -> runBigDecimalZip(asBigDecimal(lhs), rhs, problemAggregator); + case BigIntegerStorage rhs -> runBigIntegerZip(lhs, rhs, problemAggregator); + case ColumnDoubleStorage rhs -> runDoubleZip( + DoubleStorageFacade.forBigInteger(lhs), rhs, problemAggregator); + case ColumnLongStorage rhs -> runBigIntegerZip(lhs, asBigInteger(rhs), problemAggregator); + default -> throw newUnsupported(arg); + }; + } else if (storage instanceof BigDecimalStorage lhs) { + return switch (arg) { + case BigDecimalStorage rhs -> runBigDecimalZip(lhs, rhs, problemAggregator); + case BigIntegerStorage rhs -> runBigDecimalZip(lhs, asBigDecimal(rhs), problemAggregator); + case ColumnDoubleStorage rhs -> runBigDecimalZip(lhs, asBigDecimal(rhs), problemAggregator); + case ColumnLongStorage rhs -> runBigDecimalZip(lhs, asBigDecimal(rhs), problemAggregator); + default -> throw newUnsupported(arg); + }; + } else { + throw newUnsupported(storage); } - - return builder.seal(); } private static Storage allNullStorageOfSameType(Storage storage) { return switch (storage) { - case AbstractLongStorage s -> LongStorage.makeEmpty(storage.getSize(), INTEGER_RESULT_TYPE); + case ColumnLongStorage s -> LongStorage.makeEmpty(storage.getSize(), INTEGER_RESULT_TYPE); case BigIntegerStorage s -> BigIntegerStorage.makeEmpty(storage.getSize()); - case DoubleStorage s -> DoubleStorage.makeEmpty(storage.getSize()); - default -> throw new IllegalStateException( - "Unsupported storage: " + storage.getClass().getCanonicalName()); + case BigDecimalStorage s -> BigDecimalStorage.makeEmpty(storage.getSize()); + case ColumnDoubleStorage s -> DoubleStorage.makeEmpty(storage.getSize()); + default -> throw newUnsupported(storage); }; } - protected Storage runDoubleMap( - DoubleArrayAdapter a, Double b, MapOperationProblemAggregator problemAggregator) { - if (b == null) { - return DoubleStorage.makeEmpty(a.size()); - } + protected Storage runDoubleZip( + ColumnDoubleStorage a, + ColumnDoubleStorage b, + MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.zipOverDoubleStorages( + a, + b, + s -> Builder.getForDouble(FloatType.FLOAT_64, s, problemAggregator), + true, + (index, value1, isNothing1, value2, isNothing2) -> + doDouble(value1, value2, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - double bNonNull = b; - Context context = Context.getCurrent(); - long n = a.size(); - var builder = Builder.getForDouble(FloatType.FLOAT_64, n, problemAggregator); - for (long i = 0; i < n; i++) { - if (a.isNothing(i)) { - builder.appendNulls(1); - } else { - builder.appendDouble(doDouble(a.getItemAsDouble(i), bNonNull, i, problemAggregator)); - } + protected Storage runDoubleLongZip( + ColumnDoubleStorage a, ColumnLongStorage b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.zipOverDoubleLongStorages( + a, + b, + s -> Builder.getForDouble(FloatType.FLOAT_64, s, problemAggregator), + true, + (index, value1, isNothing1, value2, isNothing2) -> + doDouble(value1, value2, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - context.safepoint(); - } + protected Storage runDoubleLongMap( + ColumnLongStorage a, Double b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverLongStorage( + a, + Builder.getForDouble(FloatType.FLOAT_64, a.getSize(), problemAggregator), + (builder, index, value, isNothing) -> + builder.appendDouble(doDouble(value, b, index, problemAggregator))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - return builder.seal(); + protected Storage runDoubleMap( + ColumnDoubleStorage a, Double b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverDoubleStorage( + a, + Builder.getForDouble(FloatType.FLOAT_64, a.getSize(), problemAggregator), + (builder, index, value, isNothing) -> + builder.appendDouble(doDouble(value, b, index, problemAggregator))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runLongZip( - AbstractLongStorage a, - AbstractLongStorage b, - MapOperationProblemAggregator problemAggregator) { - Context context = Context.getCurrent(); - long n = a.getSize(); - long m = Math.min(n, b.getSize()); - var builder = Builder.getForLong(INTEGER_RESULT_TYPE, n, problemAggregator); - for (long i = 0; i < n; i++) { - if (a.isNothing(i) || i >= m || b.isNothing(i)) { - builder.appendNulls(1); - } else { - Long r = doLong(a.getItemAsLong(i), b.getItemAsLong(i), i, problemAggregator); - if (r == null) { - builder.appendNulls(1); - } else { - builder.appendLong(r); - } - } - - context.safepoint(); - } + ColumnLongStorage a, ColumnLongStorage b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.zipOverLongStorages( + a, + b, + s -> Builder.getForLong(INTEGER_RESULT_TYPE, s, problemAggregator), + true, + (index, value1, isNothing1, value2, isNothing2) -> + doLong(value1, value2, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - return builder.seal(); + protected Storage runLongDoubleZip( + ColumnLongStorage a, ColumnDoubleStorage b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.zipOverLongDoubleStorages( + a, + b, + s -> Builder.getForDouble(FloatType.FLOAT_64, s, problemAggregator), + true, + (index, value1, isNothing1, value2, isNothing2) -> + doDouble(value1, value2, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runLongMap( - AbstractLongStorage a, Long b, MapOperationProblemAggregator problemAggregator) { - if (b == null) { - return LongStorage.makeEmpty(a.getSize(), INTEGER_RESULT_TYPE); - } - - long bNonNull = b; - Context context = Context.getCurrent(); - long n = a.getSize(); - var builder = Builder.getForLong(INTEGER_RESULT_TYPE, n, problemAggregator); - for (long i = 0; i < n; i++) { - if (a.isNothing(i)) { - builder.appendNulls(1); - } else { - Long r = doLong(a.getItemAsLong(i), bNonNull, i, problemAggregator); - if (r == null) { - builder.appendNulls(1); - } else { - builder.appendLong(r); - } - } - - context.safepoint(); - } - - return builder.seal(); + ColumnLongStorage a, Long b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverLongStorage( + a, + Builder.getForLong(INTEGER_RESULT_TYPE, a.getSize(), problemAggregator), + (builder, index, value, isNothing) -> + builder.append(doLong(value, b, index, problemAggregator))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runBigIntegerZip( - BigIntegerArrayAdapter a, - BigIntegerArrayAdapter b, + ColumnStorage a, + ColumnStorage b, MapOperationProblemAggregator problemAggregator) { - Context context = Context.getCurrent(); - long n = a.size(); - long m = Math.min(n, b.size()); - var builder = Builder.getForBigInteger(n, problemAggregator); - for (long i = 0; i < m; i++) { - BigInteger x = a.getItem(i); - BigInteger y = b.getItem(i); - if (x != null && y != null) { - builder.append(doBigInteger(x, y, i, problemAggregator)); - } else { - builder.appendNulls(1); - } - context.safepoint(); - } - - if (m < n) { - builder.appendNulls(Math.toIntExact(n - m)); - } + var result = + StorageIterators.zipOverStorages( + a, + b, + s -> Builder.getForBigInteger(s, problemAggregator), + true, + (index, x, y) -> doBigInteger(x, y, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - return builder.seal(); + protected Storage runBigIntegerLongMap( + ColumnLongStorage a, BigInteger b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverLongStorage( + a, + Builder.getForBigInteger(a.getSize(), problemAggregator), + (builder, index, value, isNothing) -> + builder.append( + doBigInteger(BigInteger.valueOf(value), b, index, problemAggregator))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runBigIntegerMap( - BigIntegerArrayAdapter a, BigInteger b, MapOperationProblemAggregator problemAggregator) { - Context context = Context.getCurrent(); - long n = a.size(); - var builder = Builder.getForBigInteger(n, problemAggregator); - for (long i = 0; i < n; i++) { - BigInteger x = a.getItem(i); - if (x == null || b == null) { - builder.appendNulls(1); - } else { - builder.append(doBigInteger(x, b, i, problemAggregator)); - } - - context.safepoint(); - } - - return builder.seal(); + ColumnStorage a, BigInteger b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.mapOverStorage( + a, + Builder.getForBigInteger(a.getSize(), problemAggregator), + (index, value) -> doBigInteger(value, b, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runBigDecimalZip( - BigDecimalArrayAdapter a, - BigDecimalArrayAdapter b, + ColumnStorage a, + ColumnStorage b, MapOperationProblemAggregator problemAggregator) { - Context context = Context.getCurrent(); - long n = a.size(); - long m = Math.min(a.size(), b.size()); - var builder = Builder.getForBigDecimal(n); - for (long i = 0; i < n; i++) { - BigDecimal x = a.getItem(i); - BigDecimal y = i >= m ? null : b.getItem(i); - if (x != null && y != null) { - builder.append(doBigDecimal(x, y, i, problemAggregator)); - } else { - builder.appendNulls(1); - } - context.safepoint(); - } - - return builder.seal(); + var result = + StorageIterators.zipOverStorages( + a, + b, + Builder::getForBigDecimal, + true, + (index, x, y) -> doBigDecimal(x, y, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runBigDecimalMap( - BigDecimalArrayAdapter a, BigDecimal b, MapOperationProblemAggregator problemAggregator) { - Context context = Context.getCurrent(); - long n = a.size(); - var builder = Builder.getForBigDecimal(n); - - for (long i = 0; i < n; i++) { - BigDecimal x = a.getItem(i); - if (x == null || b == null) { - builder.appendNulls(1); - } else { - builder.append(doBigDecimal(x, b, i, problemAggregator)); - } - - context.safepoint(); - } - - return builder.seal(); + ColumnStorage a, BigDecimal b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.mapOverStorage( + a, + Builder.getForBigDecimal(a.getSize()), + (index, value) -> doBigDecimal(value, b, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningBigDecimal.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningBigDecimal.java index 4dad49cb2829..93a434a43292 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningBigDecimal.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningBigDecimal.java @@ -1,60 +1,70 @@ package org.enso.table.data.column.operation.map.numeric.arithmetic; +import static org.enso.table.data.column.operation.map.numeric.arithmetic.NumericBinaryOpImplementation.asBigDecimal; + import java.math.BigDecimal; -import java.math.BigInteger; import org.enso.base.polyglot.NumericConverter; +import org.enso.table.data.column.builder.Builder; +import org.enso.table.data.column.operation.StorageIterators; +import org.enso.table.data.column.operation.map.BinaryMapOperation; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; -import org.enso.table.data.column.operation.map.numeric.helpers.BigDecimalArrayAdapter; -import org.enso.table.data.column.storage.Storage; +import org.enso.table.data.column.storage.*; import org.enso.table.data.column.storage.numeric.BigDecimalStorage; +import org.enso.table.data.column.storage.numeric.BigIntegerStorage; public abstract class NumericBinaryOpReturningBigDecimal< T extends Number, I extends Storage> - extends NumericBinaryOpImplementation { + extends BinaryMapOperation { public NumericBinaryOpReturningBigDecimal(String name) { super(name); } + private static ColumnStorage asBigDecimalStorage(Storage storage) { + return switch (storage) { + case ColumnDoubleStorage s -> asBigDecimal(s); + case ColumnLongStorage s -> asBigDecimal(s); + case BigDecimalStorage s -> s; + case BigIntegerStorage s -> asBigDecimal(s); + default -> throw NumericBinaryOpImplementation.newUnsupported(storage); + }; + } + @Override - public Storage runBinaryMap( + public Storage runBinaryMap( I storage, Object arg, MapOperationProblemAggregator problemAggregator) { if (arg == null) { return BigDecimalStorage.makeEmpty(storage.getSize()); } - BigDecimalArrayAdapter lhs = BigDecimalArrayAdapter.fromAnyStorage(storage); + var lhs = asBigDecimalStorage(storage); BigDecimal rhs = NumericConverter.coerceToBigDecimal(arg); - return runBigDecimalMap(lhs, rhs, problemAggregator); + var result = + StorageIterators.mapOverStorage( + lhs, + Builder.getForBigDecimal(lhs.getSize()), + (index, value) -> doBigDecimal(value, rhs, index, problemAggregator)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } @Override - public Storage runZip( + public Storage runZip( I storage, Storage arg, MapOperationProblemAggregator problemAggregator) { - BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromAnyStorage(storage); - BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromAnyStorage(arg); - return runBigDecimalZip(left, right, problemAggregator); - } + var lhs = asBigDecimalStorage(storage); + var rhs = asBigDecimalStorage(arg); - @Override - public Long doLong(long a, long b, long ix, MapOperationProblemAggregator problemAggregator) { - throw new IllegalStateException( - "Impossible: should not reach here - a NumericOpReturningBigDecimal should always use the" - + " doBigDecimal branch."); - } + var result = + StorageIterators.zipOverStorages( + lhs, + rhs, + s -> Builder.getForBigDecimal(s), + true, + (index, value1, value2) -> doBigDecimal(value1, value2, index, problemAggregator)); - @Override - public BigInteger doBigInteger( - BigInteger a, BigInteger b, long ix, MapOperationProblemAggregator problemAggregator) { - throw new IllegalStateException( - "Impossible: should not reach here - a NumericOpReturningBigDecimal should always use the" - + " doBigDecimal branch."); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } - @Override - public double doDouble( - double a, double b, long ix, MapOperationProblemAggregator problemAggregator) { - throw new IllegalStateException( - "Impossible: should not reach here - a NumericOpReturningBigDecimal should always use the" - + " doBigDecimal branch."); - } + public abstract BigDecimal doBigDecimal( + BigDecimal a, BigDecimal b, long ix, MapOperationProblemAggregator problemAggregator); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningDouble.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningDouble.java index 8639915cba8c..2d11c1225ca1 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningDouble.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningDouble.java @@ -1,19 +1,37 @@ package org.enso.table.data.column.operation.map.numeric.arithmetic; -import java.math.BigDecimal; import java.math.BigInteger; import org.enso.base.polyglot.NumericConverter; +import org.enso.table.data.column.builder.Builder; +import org.enso.table.data.column.operation.StorageIterators; +import org.enso.table.data.column.operation.map.BinaryMapOperation; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; -import org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter; +import org.enso.table.data.column.storage.ColumnDoubleStorage; +import org.enso.table.data.column.storage.ColumnLongStorage; +import org.enso.table.data.column.storage.ColumnStorage; import org.enso.table.data.column.storage.Storage; +import org.enso.table.data.column.storage.numeric.BigDecimalStorage; +import org.enso.table.data.column.storage.numeric.BigIntegerStorage; import org.enso.table.data.column.storage.numeric.DoubleStorage; +import org.enso.table.data.column.storage.numeric.DoubleStorageFacade; +import org.enso.table.data.column.storage.type.FloatType; public abstract class NumericBinaryOpReturningDouble> - extends NumericBinaryOpImplementation { + extends BinaryMapOperation { + public NumericBinaryOpReturningDouble(String name) { super(name); } + private static ColumnDoubleStorage asDoubleStorage(Storage storage) { + return switch (storage) { + case ColumnDoubleStorage s -> s; + case BigDecimalStorage s -> DoubleStorageFacade.forBigDecimal(s); + case BigIntegerStorage s -> DoubleStorageFacade.forBigInteger(s); + default -> throw NumericBinaryOpImplementation.newUnsupported(storage); + }; + } + @Override public Storage runBinaryMap( I storage, Object arg, MapOperationProblemAggregator problemAggregator) { @@ -21,42 +39,81 @@ public Storage runBinaryMap( return DoubleStorage.makeEmpty(storage.getSize()); } - DoubleArrayAdapter lhs = DoubleArrayAdapter.fromAnyStorage(storage); double rhs = (arg instanceof BigInteger bigInteger) ? bigInteger.doubleValue() : NumericConverter.coerceToDouble(arg); - return runDoubleMap(lhs, rhs, problemAggregator); + + ColumnStorage result; + if (storage instanceof ColumnLongStorage longStorage) { + result = + StorageIterators.buildOverLongStorage( + longStorage, + Builder.getForDouble(FloatType.FLOAT_64, longStorage.getSize(), problemAggregator), + (builder, index, value, isNothing) -> + builder.appendDouble(doDouble(value, rhs, index, problemAggregator))); + } else { + var doubleStorage = asDoubleStorage(storage); + result = + StorageIterators.buildOverDoubleStorage( + doubleStorage, + Builder.getForDouble(FloatType.FLOAT_64, doubleStorage.getSize(), problemAggregator), + (builder, index, value, isNothing) -> + builder.appendDouble(doDouble(value, rhs, index, problemAggregator))); + } + + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } @Override public Storage runZip( I storage, Storage arg, MapOperationProblemAggregator problemAggregator) { - DoubleArrayAdapter lhs = DoubleArrayAdapter.fromAnyStorage(storage); - DoubleArrayAdapter rhs = DoubleArrayAdapter.fromAnyStorage(arg); - return runDoubleZip(lhs, rhs, problemAggregator); - } + ColumnStorage result; + if (storage instanceof ColumnLongStorage lhs) { + if (arg instanceof ColumnLongStorage rhs) { + result = + StorageIterators.zipOverLongStorages( + lhs, + rhs, + s -> Builder.getForDouble(FloatType.FLOAT_64, s, problemAggregator), + true, + (index, value1, isNothing1, value2, isNothing2) -> + doDouble(value1, value2, index, problemAggregator)); + } else { + result = + StorageIterators.zipOverLongDoubleStorages( + lhs, + asDoubleStorage(arg), + s -> Builder.getForDouble(FloatType.FLOAT_64, s, problemAggregator), + true, + (index, value1, isNothing1, value2, isNothing2) -> + doDouble(value1, value2, index, problemAggregator)); + } + } else if (arg instanceof ColumnLongStorage rhs) { + result = + StorageIterators.zipOverDoubleLongStorages( + asDoubleStorage(storage), + rhs, + s -> Builder.getForDouble(FloatType.FLOAT_64, s, problemAggregator), + true, + (index, value1, isNothing1, value2, isNothing2) -> + doDouble(value1, value2, index, problemAggregator)); + } else { + result = + StorageIterators.zipOverDoubleStorages( + asDoubleStorage(storage), + asDoubleStorage(arg), + s -> Builder.getForDouble(FloatType.FLOAT_64, s, problemAggregator), + true, + (index, value1, isNothing1, value2, isNothing2) -> + doDouble(value1, value2, index, problemAggregator)); + } - @Override - public Long doLong(long a, long b, long ix, MapOperationProblemAggregator problemAggregator) { - throw new IllegalStateException( - "Impossible: should not reach here - a NumericOpReturningDouble should always use the" - + " doDouble branch."); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } - @Override - public BigInteger doBigInteger( - BigInteger a, BigInteger b, long ix, MapOperationProblemAggregator problemAggregator) { - throw new IllegalStateException( - "Impossible: should not reach here - a NumericOpReturningDouble should always use the" - + " doDouble branch."); - } - - @Override - public BigDecimal doBigDecimal( - BigDecimal a, BigDecimal b, long ix, MapOperationProblemAggregator problemAggregator) { - throw new IllegalStateException( - "Impossible: should not reach here - a NumericOpReturningDouble should always use the" - + " doDouble branch."); - } + protected abstract double doDouble( + double a, double b, long ix, MapOperationProblemAggregator problemAggregator); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/EqualsComparison.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/EqualsComparison.java index 2d306c00b9b3..ef58d9049c23 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/EqualsComparison.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/EqualsComparison.java @@ -3,7 +3,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; -import org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter; +import org.enso.table.data.column.storage.ColumnDoubleStorage; import org.enso.table.data.column.storage.Storage; public class EqualsComparison> @@ -19,18 +19,18 @@ protected boolean doDouble(double a, double b) { @Override protected Storage runDoubleMap( - DoubleArrayAdapter lhs, double rhs, MapOperationProblemAggregator problemAggregator) { + ColumnDoubleStorage a, double b, MapOperationProblemAggregator problemAggregator) { problemAggregator.reportFloatingPointEquality(-1); - return super.runDoubleMap(lhs, rhs, problemAggregator); + return super.runDoubleMap(a, b, problemAggregator); } @Override protected Storage runDoubleZip( - DoubleArrayAdapter lhs, - DoubleArrayAdapter rhs, + ColumnDoubleStorage a, + ColumnDoubleStorage b, MapOperationProblemAggregator problemAggregator) { problemAggregator.reportFloatingPointEquality(-1); - return super.runDoubleZip(lhs, rhs, problemAggregator); + return super.runDoubleZip(a, b, problemAggregator); } @Override diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/NumericComparison.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/NumericComparison.java index 7f78ec323647..a78056f72e36 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/NumericComparison.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/NumericComparison.java @@ -1,23 +1,24 @@ package org.enso.table.data.column.operation.map.numeric.comparisons; +import static org.enso.table.data.column.operation.map.numeric.arithmetic.NumericBinaryOpImplementation.asBigDecimal; +import static org.enso.table.data.column.operation.map.numeric.arithmetic.NumericBinaryOpImplementation.asBigInteger; + import java.math.BigDecimal; import java.math.BigInteger; import org.enso.base.CompareException; import org.enso.base.polyglot.NumericConverter; import org.enso.table.data.column.builder.Builder; +import org.enso.table.data.column.operation.StorageIterators; import org.enso.table.data.column.operation.map.BinaryMapOperation; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; -import org.enso.table.data.column.operation.map.numeric.helpers.BigDecimalArrayAdapter; -import org.enso.table.data.column.operation.map.numeric.helpers.BigIntegerArrayAdapter; -import org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter; import org.enso.table.data.column.storage.BoolStorage; +import org.enso.table.data.column.storage.ColumnDoubleStorage; +import org.enso.table.data.column.storage.ColumnLongStorage; +import org.enso.table.data.column.storage.ColumnStorage; import org.enso.table.data.column.storage.Storage; -import org.enso.table.data.column.storage.numeric.AbstractLongStorage; import org.enso.table.data.column.storage.numeric.BigDecimalStorage; import org.enso.table.data.column.storage.numeric.BigIntegerStorage; -import org.enso.table.data.column.storage.numeric.DoubleStorage; -import org.enso.table.data.column.storage.type.AnyObjectType; -import org.graalvm.polyglot.Context; +import org.enso.table.data.column.storage.numeric.DoubleStorageFacade; public abstract class NumericComparison> extends BinaryMapOperation { @@ -34,6 +35,10 @@ protected boolean onOtherType(Object a, Object b) { throw new CompareException(a, b); } + static IllegalStateException newUnsupported(Object arg) { + return new IllegalStateException("Unsupported storage: " + arg.getClass().getCanonicalName()); + } + public NumericComparison(String name) { super(name); } @@ -43,366 +48,282 @@ public Storage runBinaryMap( I storage, Object arg, MapOperationProblemAggregator problemAggregator) { if (arg == null) { return BoolStorage.makeEmpty(storage.getSize()); - } else if (arg instanceof BigInteger bigInteger) { + } + + if (arg instanceof BigInteger bigInteger) { return switch (storage) { - case AbstractLongStorage s -> runBigIntegerMap( - BigIntegerArrayAdapter.fromStorage(s), bigInteger, problemAggregator); - case BigIntegerStorage s -> runBigIntegerMap( - BigIntegerArrayAdapter.fromStorage(s), bigInteger, problemAggregator); case BigDecimalStorage s -> runBigDecimalMap( - BigDecimalArrayAdapter.fromBigDecimalStorage(s), - new BigDecimal(bigInteger), - problemAggregator); - case DoubleStorage s -> runDoubleMap(s, bigInteger.doubleValue(), problemAggregator); - default -> throw new IllegalStateException( - "Unsupported lhs storage: " + storage.getClass().getCanonicalName()); + s, new BigDecimal(bigInteger), problemAggregator); + case BigIntegerStorage s -> runBigIntegerMap(s, bigInteger, problemAggregator); + case ColumnDoubleStorage s -> runDoubleMap(s, bigInteger.doubleValue(), problemAggregator); + case ColumnLongStorage s -> runBigIntegerLongMap(s, bigInteger, problemAggregator); + default -> throw newUnsupported(storage); }; } else if (arg instanceof BigDecimal bigDecimal) { return switch (storage) { - case AbstractLongStorage s -> runBigDecimalMap( - BigDecimalArrayAdapter.fromStorage(s), bigDecimal, problemAggregator); + case BigDecimalStorage s -> runBigDecimalMap(s, bigDecimal, problemAggregator); case BigIntegerStorage s -> runBigDecimalMap( - BigDecimalArrayAdapter.fromBigIntegerStorage(s), bigDecimal, problemAggregator); - case BigDecimalStorage s -> runBigDecimalMap( - BigDecimalArrayAdapter.fromBigDecimalStorage(s), bigDecimal, problemAggregator); - case DoubleStorage s -> runBigDecimalMap( - BigDecimalArrayAdapter.fromStorage(s), bigDecimal, problemAggregator); - default -> throw new IllegalStateException( - "Unsupported lhs storage: " + storage.getClass().getCanonicalName()); + asBigDecimal(s), bigDecimal, problemAggregator); + case ColumnDoubleStorage s -> runBigDecimalMap( + asBigDecimal(s), bigDecimal, problemAggregator); + case ColumnLongStorage s -> runBigDecimalMap( + asBigDecimal(s), bigDecimal, problemAggregator); + default -> throw newUnsupported(storage); }; } else if (NumericConverter.isCoercibleToLong(arg)) { - long rhs = NumericConverter.coerceToLong(arg); + long argAsLong = NumericConverter.coerceToLong(arg); return switch (storage) { - case AbstractLongStorage s -> runLongMap(s, rhs, problemAggregator); - case BigIntegerStorage s -> runBigIntegerMap( - BigIntegerArrayAdapter.fromStorage(s), BigInteger.valueOf(rhs), problemAggregator); case BigDecimalStorage s -> runBigDecimalMap( - BigDecimalArrayAdapter.fromBigDecimalStorage(s), - BigDecimal.valueOf(rhs), - problemAggregator); - case DoubleStorage s -> runDoubleMap(s, (double) rhs, problemAggregator); - default -> throw new IllegalStateException( - "Unsupported lhs storage: " + storage.getClass().getCanonicalName()); + s, BigDecimal.valueOf(argAsLong), problemAggregator); + case BigIntegerStorage s -> runBigIntegerMap( + s, BigInteger.valueOf(argAsLong), problemAggregator); + case ColumnDoubleStorage s -> runDoubleMap(s, (double) argAsLong, problemAggregator); + case ColumnLongStorage s -> runLongMap(s, argAsLong, problemAggregator); + default -> throw newUnsupported(storage); }; } else if (NumericConverter.isCoercibleToDouble(arg)) { - double rhs = NumericConverter.coerceToDouble(arg); + double argAsDouble = NumericConverter.coerceToDouble(arg); return switch (storage) { case BigDecimalStorage s -> runBigDecimalMap( - BigDecimalArrayAdapter.fromBigDecimalStorage(s), - BigDecimal.valueOf(rhs), - problemAggregator); - default -> { - DoubleArrayAdapter lhs = DoubleArrayAdapter.fromAnyStorage(storage); - yield runDoubleMap(lhs, rhs, problemAggregator); - } + s, BigDecimal.valueOf(argAsDouble), problemAggregator); + case BigIntegerStorage s -> runDoubleMap( + DoubleStorageFacade.forBigInteger(s), argAsDouble, problemAggregator); + case ColumnDoubleStorage s -> runDoubleMap(s, argAsDouble, problemAggregator); + case ColumnLongStorage s -> runDoubleLongMap(s, argAsDouble, problemAggregator); + default -> throw newUnsupported(storage); }; } else { - long n = storage.getSize(); - var builder = Builder.getForBoolean(n); - Context context = Context.getCurrent(); - for (int i = 0; i < n; ++i) { - Object item = storage.getItemBoxed(i); - if (item == null) { - builder.appendNulls(1); - } else { - boolean r = onOtherType(item, arg); - builder.appendBoolean(r); - } - - context.safepoint(); - } - - return builder.seal(); + var result = + StorageIterators.buildOverStorage( + (Storage) storage, + Builder.getForBoolean(storage.getSize()), + (builder, index, value) -> builder.appendBoolean(onOtherType(value, arg))); + return (Storage) result; } } protected Storage runLongMap( - AbstractLongStorage lhs, long rhs, MapOperationProblemAggregator problemAggregator) { - long n = lhs.getSize(); - Context context = Context.getCurrent(); - var builder = Builder.getForBoolean(n); - for (long i = 0; i < n; ++i) { - if (lhs.isNothing(i)) { - builder.appendNulls(1); - } else { - long item = lhs.getItemAsLong(i); - boolean r = doLong(item, rhs); - builder.appendBoolean(r); - } - - context.safepoint(); - } + ColumnLongStorage a, long b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverLongStorage( + a, + Builder.getForBoolean(a.getSize()), + (builder, index, value, isNothing) -> builder.appendBoolean(doLong(value, b))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - return builder.seal(); + protected Storage runDoubleLongMap( + ColumnLongStorage a, Double b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverLongStorage( + a, + Builder.getForBoolean(a.getSize()), + (builder, index, value, isNothing) -> builder.append(doDouble((double) value, b))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runDoubleMap( - DoubleArrayAdapter lhs, double rhs, MapOperationProblemAggregator problemAggregator) { - long n = lhs.size(); - var builder = Builder.getForBoolean(n); - Context context = Context.getCurrent(); - for (long i = 0; i < n; ++i) { - if (lhs.isNothing(i)) { - builder.appendNulls(1); - } else { - double item = lhs.getItemAsDouble(i); - boolean r = doDouble(item, rhs); - builder.appendBoolean(r); - } - - context.safepoint(); - } + ColumnDoubleStorage a, double b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverDoubleStorage( + a, + Builder.getForBoolean(a.getSize()), + (builder, index, value, isNothing) -> builder.appendBoolean(doDouble(value, b))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - return builder.seal(); + protected Storage runBigIntegerLongMap( + ColumnLongStorage a, BigInteger b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverLongStorage( + a, + Builder.getForBoolean(a.getSize()), + (builder, index, value, isNothing) -> + builder.appendBoolean(doBigInteger(BigInteger.valueOf(value), b))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runBigIntegerMap( - BigIntegerArrayAdapter lhs, BigInteger rhs, MapOperationProblemAggregator problemAggregator) { - long n = lhs.size(); - var builder = Builder.getForBoolean(n); - Context context = Context.getCurrent(); - for (long i = 0; i < n; ++i) { - BigInteger item = lhs.getItem(i); - if (item == null) { - builder.appendNulls(1); - } else { - boolean r = doBigInteger(item, rhs); - builder.appendBoolean(r); - } - context.safepoint(); - } - return builder.seal(); + ColumnStorage a, BigInteger b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverStorage( + a, + Builder.getForBoolean(a.getSize()), + (builder, index, value) -> builder.appendBoolean(doBigInteger(value, b))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runBigDecimalMap( - BigDecimalArrayAdapter lhs, BigDecimal rhs, MapOperationProblemAggregator problemAggregator) { - long n = lhs.size(); - var builder = Builder.getForBoolean(n); - Context context = Context.getCurrent(); - for (int i = 0; i < n; ++i) { - BigDecimal item = lhs.getItem(i); - if (item == null) { - builder.appendNulls(1); - } else { - builder.append(doBigDecimal(item, rhs)); - } - - context.safepoint(); - } - - return builder.seal(); + ColumnStorage a, BigDecimal b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.buildOverStorage( + a, + Builder.getForBoolean(a.getSize()), + (builder, index, value) -> builder.appendBoolean(doBigDecimal(value, b))); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } @Override public Storage runZip( I storage, Storage arg, MapOperationProblemAggregator problemAggregator) { - return switch (storage) { - case DoubleStorage lhs -> switch (arg) { - case BigDecimalStorage rhs -> runBigDecimalZip( - BigDecimalArrayAdapter.fromStorage(lhs), - BigDecimalArrayAdapter.fromBigDecimalStorage(rhs), - problemAggregator); - default -> { - if (arg.getType() instanceof AnyObjectType) { - yield runMixedZip(lhs, arg, problemAggregator); - } else { - yield runDoubleZip(lhs, DoubleArrayAdapter.fromAnyStorage(arg), problemAggregator); - } - } + if (storage instanceof ColumnDoubleStorage lhs) { + return switch (arg) { + case BigDecimalStorage rhs -> runBigDecimalZip(asBigDecimal(lhs), rhs, problemAggregator); + case BigIntegerStorage rhs -> runDoubleZip( + lhs, DoubleStorageFacade.forBigInteger(rhs), problemAggregator); + case ColumnDoubleStorage rhs -> runDoubleZip(lhs, rhs, problemAggregator); + case ColumnLongStorage rhs -> runDoubleLongZip(lhs, rhs, problemAggregator); + default -> runMixedZip(storage, arg, problemAggregator); }; - - case AbstractLongStorage lhs -> switch (arg) { - case AbstractLongStorage rhs -> runLongZip(lhs, rhs, problemAggregator); - case BigIntegerStorage rhs -> { - BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs); - BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs); - yield runBigIntegerZip(left, right, problemAggregator); - } - case BigDecimalStorage rhs -> runBigDecimalZip( - BigDecimalArrayAdapter.fromStorage(lhs), - BigDecimalArrayAdapter.fromBigDecimalStorage(rhs), - problemAggregator); - case DoubleStorage rhs -> runDoubleZip( - DoubleArrayAdapter.fromStorage(lhs), rhs, problemAggregator); - default -> runMixedZip(lhs, arg, problemAggregator); + } else if (storage instanceof ColumnLongStorage lhs) { + return switch (arg) { + case BigDecimalStorage rhs -> runBigDecimalZip(asBigDecimal(lhs), rhs, problemAggregator); + case BigIntegerStorage rhs -> runBigIntegerZip(asBigInteger(lhs), rhs, problemAggregator); + case ColumnDoubleStorage rhs -> runLongDoubleZip(lhs, rhs, problemAggregator); + case ColumnLongStorage rhs -> runLongZip(lhs, rhs, problemAggregator); + default -> runMixedZip(storage, arg, problemAggregator); }; - - case BigIntegerStorage lhs -> switch (arg) { - case AbstractLongStorage rhs -> { - BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs); - BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs); - yield runBigIntegerZip(left, right, problemAggregator); - } - case BigIntegerStorage rhs -> { - BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs); - BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs); - yield runBigIntegerZip(left, right, problemAggregator); - } - case BigDecimalStorage rhs -> runBigDecimalZip( - BigDecimalArrayAdapter.fromBigIntegerStorage(lhs), - BigDecimalArrayAdapter.fromBigDecimalStorage(rhs), - problemAggregator); - case DoubleStorage rhs -> runDoubleZip( - DoubleArrayAdapter.fromBigIntegerStorage(lhs), rhs, problemAggregator); - default -> runMixedZip(lhs, arg, problemAggregator); + } else if (storage instanceof BigIntegerStorage lhs) { + return switch (arg) { + case BigDecimalStorage rhs -> runBigDecimalZip(asBigDecimal(lhs), rhs, problemAggregator); + case BigIntegerStorage rhs -> runBigIntegerZip(lhs, rhs, problemAggregator); + case ColumnDoubleStorage rhs -> runDoubleZip( + DoubleStorageFacade.forBigInteger(lhs), rhs, problemAggregator); + case ColumnLongStorage rhs -> runBigIntegerZip(lhs, asBigInteger(rhs), problemAggregator); + default -> runMixedZip(storage, arg, problemAggregator); }; - - case BigDecimalStorage lhs -> { - if (arg instanceof AbstractLongStorage - || arg instanceof BigIntegerStorage - || arg instanceof BigDecimalStorage - || arg instanceof DoubleStorage) { - BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromAnyStorage(lhs); - BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromAnyStorage(arg); - yield runBigDecimalZip(left, right, problemAggregator); - } else { - yield runMixedZip(lhs, arg, problemAggregator); - } - } - - default -> throw new IllegalStateException( - "Unsupported lhs storage: " + storage.getClass().getCanonicalName()); - }; + } else if (storage instanceof BigDecimalStorage lhs) { + return switch (arg) { + case BigDecimalStorage rhs -> runBigDecimalZip(lhs, rhs, problemAggregator); + case BigIntegerStorage rhs -> runBigDecimalZip(lhs, asBigDecimal(rhs), problemAggregator); + case ColumnDoubleStorage rhs -> runBigDecimalZip(lhs, asBigDecimal(rhs), problemAggregator); + case ColumnLongStorage rhs -> runBigDecimalZip(lhs, asBigDecimal(rhs), problemAggregator); + default -> runMixedZip(storage, arg, problemAggregator); + }; + } else { + throw newUnsupported(storage); + } } protected Storage runLongZip( - AbstractLongStorage lhs, - AbstractLongStorage rhs, - MapOperationProblemAggregator problemAggregator) { - long n = lhs.getSize(); - long m = Math.min(n, rhs.getSize()); - var builder = Builder.getForBoolean(n); - Context context = Context.getCurrent(); - for (long i = 0; i < n; ++i) { - if (lhs.isNothing(i) || (i >= m || rhs.isNothing(i))) { - builder.appendNulls(1); - } else { - long x = lhs.getItemAsLong(i); - long y = rhs.getItemAsLong(i); - boolean r = doLong(x, y); - builder.appendBoolean(r); - } - - context.safepoint(); - } + ColumnLongStorage a, ColumnLongStorage b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.zipOverLongStorages( + a, + b, + Builder::getForBoolean, + true, + (index, x, xIsNothing, y, yIsNothing) -> doLong(x, y)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - return builder.seal(); + protected Storage runLongDoubleZip( + ColumnLongStorage a, ColumnDoubleStorage b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.zipOverLongDoubleStorages( + a, + b, + Builder::getForBoolean, + true, + (index, x, xIsNothing, y, yIsNothing) -> doDouble(x, y)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runDoubleZip( - DoubleArrayAdapter lhs, - DoubleArrayAdapter rhs, + ColumnDoubleStorage a, + ColumnDoubleStorage b, MapOperationProblemAggregator problemAggregator) { - long n = lhs.size(); - long m = Math.min(n, rhs.size()); - var builder = Builder.getForBoolean(n); - Context context = Context.getCurrent(); - for (long i = 0; i < n; ++i) { - if (lhs.isNothing(i) || (i >= m || rhs.isNothing(i))) { - builder.appendNulls(1); - } else { - double x = lhs.getItemAsDouble(i); - double y = rhs.getItemAsDouble(i); - boolean r = doDouble(x, y); - builder.appendBoolean(r); - } - - context.safepoint(); - } + var result = + StorageIterators.zipOverDoubleStorages( + a, + b, + Builder::getForBoolean, + true, + (index, x, xIsNothing, y, yIsNothing) -> doDouble(x, y)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; + } - return builder.seal(); + protected Storage runDoubleLongZip( + ColumnDoubleStorage a, ColumnLongStorage b, MapOperationProblemAggregator problemAggregator) { + var result = + StorageIterators.zipOverDoubleLongStorages( + a, + b, + Builder::getForBoolean, + true, + (index, x, xIsNothing, y, yIsNothing) -> doDouble(x, y)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runBigIntegerZip( - BigIntegerArrayAdapter lhs, - BigIntegerArrayAdapter rhs, + ColumnStorage a, + ColumnStorage b, MapOperationProblemAggregator problemAggregator) { - long n = lhs.size(); - long m = Math.min(n, rhs.size()); - var builder = Builder.getForBoolean(n); - Context context = Context.getCurrent(); - for (long i = 0; i < n; ++i) { - BigInteger x = lhs.getItem(i); - BigInteger y = i >= m ? null : rhs.getItem(i); - if (x == null || y == null) { - builder.appendNulls(1); - } else { - boolean r = doBigInteger(x, y); - builder.appendBoolean(r); - } - - context.safepoint(); - } - - return builder.seal(); + var result = + StorageIterators.zipOverStorages( + a, b, Builder::getForBoolean, true, (index, x, y) -> doBigInteger(x, y)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runBigDecimalZip( - BigDecimalArrayAdapter lhs, - BigDecimalArrayAdapter rhs, + ColumnStorage a, + ColumnStorage b, MapOperationProblemAggregator problemAggregator) { - long n = lhs.size(); - long m = Math.min(n, rhs.size()); - var builder = Builder.getForBoolean(n); - Context context = Context.getCurrent(); - for (int i = 0; i < n; ++i) { - BigDecimal x = lhs.getItem(i); - BigDecimal y = i >= m ? null : rhs.getItem(i); - if (x == null || y == null) { - builder.appendNulls(1); - } else { - builder.appendBoolean(doBigDecimal(x, y)); - } - - context.safepoint(); - } - - return builder.seal(); + var result = + StorageIterators.zipOverStorages( + a, b, Builder::getForBoolean, true, (index, x, y) -> doBigDecimal(x, y)); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } protected Storage runMixedZip( Storage lhs, Storage rhs, MapOperationProblemAggregator problemAggregator) { - long n = lhs.getSize(); - long m = Math.min(n, rhs.getSize()); - var builder = Builder.getForBoolean(n); - Context context = Context.getCurrent(); - for (long i = 0; i < n; ++i) { - Object x = lhs.getItemBoxed(i); - Object y = i >= m ? null : rhs.getItemBoxed(i); - if (x == null || y == null) { - builder.appendNulls(1); - } else { - boolean r; - // Any number is coercible to double, if the value is not coercible, it is not a supported - // number type. - if (NumericConverter.isCoercibleToDouble(x) && NumericConverter.isCoercibleToDouble(y)) { - - // If any of the values is decimal like, then decimal type is used for comparison. - if (NumericConverter.isFloatLike(x) || NumericConverter.isFloatLike(y)) { - double a = NumericConverter.coerceToDouble(x); - double b = NumericConverter.coerceToDouble(y); - r = doDouble(a, b); - } else { - if (x instanceof BigInteger || y instanceof BigInteger) { - BigInteger a = NumericConverter.coerceToBigInteger(x); - BigInteger b = NumericConverter.coerceToBigInteger(y); - r = doBigInteger(a, b); - } else { - long a = NumericConverter.coerceToLong(x); - long b = NumericConverter.coerceToLong(y); - r = doLong(a, b); - } - } - } else { - r = onOtherType(x, y); - } - - builder.appendBoolean(r); - } - - context.safepoint(); - } - - return builder.seal(); + var result = + StorageIterators.zipOverStorages( + lhs, + rhs, + Builder::getForBoolean, + true, + (index, x, y) -> { + boolean r; + // Any number is coercible to double, if the value is not coercible, it is not a + // supported + // number type. + if (NumericConverter.isCoercibleToDouble(x) + && NumericConverter.isCoercibleToDouble(y)) { + // If any of the values is decimal like, then decimal type is used for comparison. + if (NumericConverter.isFloatLike(x) || NumericConverter.isFloatLike(y)) { + double a = NumericConverter.coerceToDouble(x); + double b = NumericConverter.coerceToDouble(y); + r = doDouble(a, b); + } else { + if (x instanceof BigInteger || y instanceof BigInteger) { + BigInteger a = NumericConverter.coerceToBigInteger(x); + BigInteger b = NumericConverter.coerceToBigInteger(y); + r = doBigInteger(a, b); + } else { + long a = NumericConverter.coerceToLong(x); + long b = NumericConverter.coerceToLong(y); + r = doLong(a, b); + } + } + } else { + r = onOtherType(x, y); + } + return r; + }); + // ToDo: Merge Storage and ColumnStorage + return (Storage) result; } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigDecimalArrayAdapter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigDecimalArrayAdapter.java deleted file mode 100644 index acf4c6a5dca3..000000000000 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigDecimalArrayAdapter.java +++ /dev/null @@ -1,144 +0,0 @@ -package org.enso.table.data.column.operation.map.numeric.helpers; - -import java.math.BigDecimal; -import java.math.BigInteger; -import org.enso.table.data.column.builder.Builder; -import org.enso.table.data.column.storage.ColumnDoubleStorage; -import org.enso.table.data.column.storage.ColumnLongStorage; -import org.enso.table.data.column.storage.ColumnStorage; -import org.enso.table.data.column.storage.Storage; -import org.enso.table.data.column.storage.numeric.BigDecimalStorage; -import org.enso.table.data.column.storage.numeric.BigIntegerStorage; - -public interface BigDecimalArrayAdapter { - BigDecimal getItem(long i); - - long size(); - - default Storage intoStorage() { - long n = size(); - var builder = Builder.getForBigDecimal(n); - for (long i = 0; i < n; i++) { - builder.append(getItem(i)); - } - return builder.seal(); - } - - static BigDecimalArrayAdapter fromBigDecimalStorage(ColumnStorage storage) { - return new BigDecimalStorageAsBigDecimal(storage); - } - - static BigDecimalArrayAdapter fromBigIntegerStorage(ColumnStorage storage) { - return new BigIntegerStorageAsBigDecimal(storage); - } - - static BigDecimalArrayAdapter fromStorage(ColumnLongStorage storage) { - return new LongStorageAsBigDecimal(storage); - } - - static BigDecimalArrayAdapter fromStorage(ColumnDoubleStorage storage) { - return new DoubleStorageAsBigDecimal(storage); - } - - static BigDecimalArrayAdapter fromAnyStorage(ColumnStorage storage) { - return switch (storage) { - case ColumnDoubleStorage s -> fromStorage(s); - case ColumnLongStorage s -> fromStorage(s); - case BigIntegerStorage s -> new BigIntegerStorageAsBigDecimal(s); - case BigDecimalStorage s -> new BigDecimalStorageAsBigDecimal(s); - default -> throw new IllegalStateException( - "Unsupported storage: " + storage.getClass().getCanonicalName()); - }; - } - - class BigDecimalStorageAsBigDecimal implements BigDecimalArrayAdapter { - private final ColumnStorage storage; - - private BigDecimalStorageAsBigDecimal(ColumnStorage storage) { - this.storage = storage; - } - - @Override - public BigDecimal getItem(long i) { - return storage.getItemBoxed(i); - } - - @Override - public long size() { - return storage.getSize(); - } - - @Override - public Storage intoStorage() { - if (storage instanceof Storage specialized) { - return specialized; - } else { - return BigDecimalArrayAdapter.super.intoStorage(); - } - } - } - - class BigIntegerStorageAsBigDecimal implements BigDecimalArrayAdapter { - private final ColumnStorage storage; - - private BigIntegerStorageAsBigDecimal(ColumnStorage storage) { - this.storage = storage; - } - - @Override - public BigDecimal getItem(long i) { - return new BigDecimal(storage.getItemBoxed(i)); - } - - @Override - public long size() { - return storage.getSize(); - } - } - - class LongStorageAsBigDecimal implements BigDecimalArrayAdapter { - private final ColumnLongStorage storage; - - private LongStorageAsBigDecimal(ColumnLongStorage storage) { - this.storage = storage; - } - - @Override - public BigDecimal getItem(long i) { - if (storage.isNothing(i)) { - return null; - } else { - long x = storage.getItemAsLong(i); - return BigDecimal.valueOf(x); - } - } - - @Override - public long size() { - return storage.getSize(); - } - } - - class DoubleStorageAsBigDecimal implements BigDecimalArrayAdapter { - private final ColumnDoubleStorage storage; - - private DoubleStorageAsBigDecimal(ColumnDoubleStorage storage) { - this.storage = storage; - } - - @Override - public BigDecimal getItem(long i) { - if (storage.isNothing(i)) { - return null; - } else { - double x = storage.getItemAsDouble(i); - return BigDecimal.valueOf(x); - } - } - - @Override - public long size() { - return storage.getSize(); - } - } -} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigIntegerArrayAdapter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigIntegerArrayAdapter.java deleted file mode 100644 index dfcd857b4ec3..000000000000 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigIntegerArrayAdapter.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.enso.table.data.column.operation.map.numeric.helpers; - -import java.math.BigInteger; -import org.enso.table.data.column.builder.Builder; -import org.enso.table.data.column.storage.ColumnLongStorage; -import org.enso.table.data.column.storage.Storage; -import org.enso.table.problems.BlackholeProblemAggregator; - -public interface BigIntegerArrayAdapter { - BigInteger getItem(long i); - - long size(); - - default Storage intoStorage() { - long n = size(); - var builder = Builder.getForBigInteger(n, BlackholeProblemAggregator.INSTANCE); - for (long i = 0; i < n; i++) { - builder.append(getItem(i)); - } - return builder.seal(); - } - - static BigIntegerArrayAdapter fromStorage(Storage storage) { - return new BigIntegerStorageAsBigInteger(storage); - } - - static BigIntegerArrayAdapter fromStorage(ColumnLongStorage storage) { - return new LongStorageAsBigInteger(storage); - } - - class BigIntegerStorageAsBigInteger implements BigIntegerArrayAdapter { - private final Storage storage; - - private BigIntegerStorageAsBigInteger(Storage storage) { - this.storage = storage; - } - - @Override - public BigInteger getItem(long i) { - return storage.getItemBoxed(i); - } - - @Override - public long size() { - return storage.getSize(); - } - - @Override - public Storage intoStorage() { - return storage; - } - } - - class LongStorageAsBigInteger implements BigIntegerArrayAdapter { - private final ColumnLongStorage storage; - - private LongStorageAsBigInteger(ColumnLongStorage storage) { - this.storage = storage; - } - - @Override - public BigInteger getItem(long i) { - if (storage.isNothing(i)) { - return null; - } else { - long x = storage.getItemAsLong(i); - return BigInteger.valueOf(x); - } - } - - @Override - public long size() { - return storage.getSize(); - } - } -} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/DoubleArrayAdapter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/DoubleArrayAdapter.java deleted file mode 100644 index 091666120985..000000000000 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/DoubleArrayAdapter.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.enso.table.data.column.operation.map.numeric.helpers; - -import java.math.BigDecimal; -import java.math.BigInteger; -import org.enso.table.data.column.builder.Builder; -import org.enso.table.data.column.storage.ColumnLongStorage; -import org.enso.table.data.column.storage.Storage; -import org.enso.table.data.column.storage.numeric.BigDecimalStorage; -import org.enso.table.data.column.storage.numeric.BigIntegerStorage; -import org.enso.table.data.column.storage.numeric.DoubleStorage; -import org.enso.table.data.column.storage.type.FloatType; -import org.enso.table.problems.BlackholeProblemAggregator; - -public interface DoubleArrayAdapter { - double getItemAsDouble(long i); - - boolean isNothing(long i); - - long size(); - - default Storage intoStorage() { - long n = size(); - var builder = Builder.getForDouble(FloatType.FLOAT_64, n, BlackholeProblemAggregator.INSTANCE); - for (long i = 0; i < n; i++) { - if (isNothing(i)) { - builder.appendNulls(1); - } else { - builder.appendDouble(getItemAsDouble(i)); - } - } - return builder.seal(); - } - - static DoubleArrayAdapter fromBigIntegerStorage(Storage storage) { - return new BigIntegerStorageAsDouble(storage); - } - - static DoubleArrayAdapter fromBigDecimalStorage(Storage storage) { - return new BigDecimalStorageAsDouble(storage); - } - - static DoubleArrayAdapter fromStorage(ColumnLongStorage storage) { - return new LongStorageAsDouble(storage); - } - - static DoubleArrayAdapter fromStorage(DoubleStorage storage) { - return storage; - } - - static DoubleArrayAdapter fromAnyStorage(Storage storage) { - return switch (storage) { - case DoubleStorage s -> fromStorage(s); - case ColumnLongStorage s -> fromStorage(s); - case BigIntegerStorage s -> fromBigIntegerStorage(s); - case BigDecimalStorage s -> fromBigDecimalStorage(s); - default -> throw new IllegalStateException( - "Unsupported storage: " + storage.getClass().getCanonicalName()); - }; - } - - class LongStorageAsDouble implements DoubleArrayAdapter { - private final ColumnLongStorage storage; - - private LongStorageAsDouble(ColumnLongStorage storage) { - this.storage = storage; - } - - @Override - public double getItemAsDouble(long i) { - long x = storage.getItemAsLong(i); - return (double) x; - } - - @Override - public boolean isNothing(long i) { - return storage.isNothing(i); - } - - @Override - public long size() { - return storage.getSize(); - } - } - - class BigIntegerStorageAsDouble implements DoubleArrayAdapter { - private final Storage storage; - - private BigIntegerStorageAsDouble(Storage storage) { - this.storage = storage; - } - - @Override - public double getItemAsDouble(long i) { - BigInteger x = storage.getItemBoxed(i); - return x.doubleValue(); - } - - @Override - public boolean isNothing(long i) { - return storage.isNothing(i); - } - - @Override - public long size() { - return storage.getSize(); - } - } - - class BigDecimalStorageAsDouble implements DoubleArrayAdapter { - private final Storage storage; - - private BigDecimalStorageAsDouble(Storage storage) { - this.storage = storage; - } - - @Override - public double getItemAsDouble(long i) { - BigDecimal x = storage.getItemBoxed(i); - return x.doubleValue(); - } - - @Override - public boolean isNothing(long i) { - return storage.isNothing(i); - } - - @Override - public long size() { - return storage.getSize(); - } - } -} 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 571c50f29299..987d2be0c995 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 @@ -2,6 +2,7 @@ import java.util.BitSet; import java.util.List; +import java.util.NoSuchElementException; import java.util.function.IntFunction; import org.enso.base.CompareException; import org.enso.base.polyglot.Polyglot_Utils; @@ -323,6 +324,62 @@ public Storage slice(List ranges) { return builder.seal(); } + @Override + public ColumnBooleanStorageIterator iterator() { + return new BoolStorageIterator(this); + } + + private static class BoolStorageIterator implements ColumnBooleanStorageIterator { + private final BoolStorage parent; + private int index = -1; + + public BoolStorageIterator(BoolStorage parent) { + this.parent = parent; + } + + @Override + public Boolean getItemBoxed() { + return parent.getItemBoxed(index); + } + + @Override + public boolean getItemAsBoolean() { + return !parent.isNothing(index) && parent.getItemAsBoolean(index); + } + + @Override + public boolean isNothing() { + return parent.isNothing(index); + } + + @Override + public boolean hasNext() { + return index + 1 < parent.getSize(); + } + + @Override + public Boolean next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return parent.getItemBoxed(++index); + } + + @Override + public long getIndex() { + return index; + } + + @Override + public boolean moveNext() { + if (!hasNext()) { + return false; + } + index++; + return true; + } + } + private static class BoolEq extends BinaryMapOperation { public BoolEq() { super(Maps.EQ); diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnBooleanStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnBooleanStorage.java index 111a8efbf0ee..ba5ffbf5da05 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnBooleanStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnBooleanStorage.java @@ -3,4 +3,7 @@ public interface ColumnBooleanStorage extends ColumnStorage { /** Gets the value at a given index. Throws ValueIsNothingException if the index is nothing. */ boolean getItemAsBoolean(long index) throws ValueIsNothingException; + + @Override + ColumnBooleanStorageIterator iterator(); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnBooleanStorageIterator.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnBooleanStorageIterator.java new file mode 100644 index 000000000000..5c9030b4da3f --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnBooleanStorageIterator.java @@ -0,0 +1,6 @@ +package org.enso.table.data.column.storage; + +public interface ColumnBooleanStorageIterator extends ColumnStorageIterator { + /** Gets the current item as a boolean. Note if the item isNothing value is undefined. */ + boolean getItemAsBoolean(); +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnDoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnDoubleStorage.java index 10122ed65aff..989c3f57420b 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnDoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnDoubleStorage.java @@ -3,4 +3,7 @@ public interface ColumnDoubleStorage extends ColumnStorage { /** Gets the value at a given index. Throws ValueIsNothingException if the index is nothing. */ double getItemAsDouble(long index) throws ValueIsNothingException; + + @Override + ColumnDoubleStorageIterator iterator(); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnDoubleStorageIterator.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnDoubleStorageIterator.java new file mode 100644 index 000000000000..39b4c27a3623 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnDoubleStorageIterator.java @@ -0,0 +1,58 @@ +package org.enso.table.data.column.storage; + +import org.graalvm.polyglot.Context; + +public interface ColumnDoubleStorageIterator extends ColumnStorageIterator { + /** Gets the current item as a double. Note if the item isNothing value is undefined. */ + double getItemAsDouble(); + + @FunctionalInterface + interface DoubleDoubleZipper { + void accept(long idx, double value1, boolean isNothing1, double value2, boolean isNothing2); + } + + @FunctionalInterface + interface DoubleLongZipper { + void accept(long idx, double value1, boolean isNothing1, long value2, boolean isNothing2); + } + + /** Zips this iterator with another iterator. */ + default void zip(ColumnDoubleStorage otherStorage, DoubleDoubleZipper zipper) { + var other = otherStorage.iterator(); + Context context = Context.getCurrent(); + + boolean hasValue1 = moveNext(); + boolean hasValue2 = other.moveNext(); + long idx = 0; + while (hasValue1 || hasValue2) { + boolean isNothing1 = !hasValue1 || isNothing(); + double value1 = isNothing1 ? Double.NaN : getItemAsDouble(); + boolean isNothing2 = !hasValue2 || other.isNothing(); + double value2 = isNothing2 ? Double.NaN : other.getItemAsDouble(); + zipper.accept(idx++, value1, isNothing1, value2, isNothing2); + context.safepoint(); + hasValue1 = hasValue1 && moveNext(); + hasValue2 = hasValue2 && other.moveNext(); + } + } + + /** Zips this iterator with another iterator. */ + default void zip(ColumnLongStorage otherStorage, DoubleLongZipper zipper) { + var other = otherStorage.iterator(); + Context context = Context.getCurrent(); + + boolean hasValue1 = moveNext(); + boolean hasValue2 = other.moveNext(); + long idx = 0; + while (hasValue1 || hasValue2) { + boolean isNothing1 = !hasValue1 || isNothing(); + double value1 = isNothing1 ? Double.NaN : getItemAsDouble(); + boolean isNothing2 = !hasValue2 || other.isNothing(); + long value2 = isNothing2 ? 0 : other.getItemAsLong(); + zipper.accept(idx++, value1, isNothing1, value2, isNothing2); + context.safepoint(); + hasValue1 = hasValue1 && moveNext(); + hasValue2 = hasValue2 && other.moveNext(); + } + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnLongStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnLongStorage.java index 42fe865b0377..f3c7b67a50ac 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnLongStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnLongStorage.java @@ -3,4 +3,7 @@ public interface ColumnLongStorage extends ColumnStorage { /** Gets the value at a given index. Throws ValueIsNothingException if the index is nothing. */ long getItemAsLong(long index) throws ValueIsNothingException; + + @Override + ColumnLongStorageIterator iterator(); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnLongStorageIterator.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnLongStorageIterator.java new file mode 100644 index 000000000000..e6e78a55656a --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnLongStorageIterator.java @@ -0,0 +1,58 @@ +package org.enso.table.data.column.storage; + +import org.graalvm.polyglot.Context; + +public interface ColumnLongStorageIterator extends ColumnStorageIterator { + /** Gets the current item as a long. Note if the item isNothing value is undefined. */ + long getItemAsLong(); + + @FunctionalInterface + interface LongLongZipper { + void accept(long idx, long value1, boolean isNothing1, long value2, boolean isNothing2); + } + + @FunctionalInterface + interface LongDoubleZipper { + void accept(long idx, long value1, boolean isNothing1, double value2, boolean isNothing2); + } + + /** Zips this iterator with a Double storage. */ + default void zip(ColumnDoubleStorage otherStorage, LongDoubleZipper zipper) { + var other = otherStorage.iterator(); + Context context = Context.getCurrent(); + + boolean hasValue1 = moveNext(); + boolean hasValue2 = other.moveNext(); + long idx = 0; + while (hasValue1 || hasValue2) { + boolean isNothing1 = !hasValue1 || isNothing(); + long value1 = isNothing1 ? 0 : getItemAsLong(); + boolean isNothing2 = !hasValue2 || other.isNothing(); + double value2 = isNothing2 ? 0 : other.getItemAsDouble(); + zipper.accept(idx++, value1, isNothing1, value2, isNothing2); + context.safepoint(); + hasValue1 = hasValue1 && moveNext(); + hasValue2 = hasValue2 && other.moveNext(); + } + } + + /** Zips this iterator with another Long storage. */ + default void zip(ColumnLongStorage otherStorage, LongLongZipper zipper) { + var other = otherStorage.iterator(); + Context context = Context.getCurrent(); + + boolean hasValue1 = moveNext(); + boolean hasValue2 = other.moveNext(); + long idx = 0; + while (hasValue1 || hasValue2) { + boolean isNothing1 = !hasValue1 || isNothing(); + long value1 = isNothing1 ? 0 : getItemAsLong(); + boolean isNothing2 = !hasValue2 || other.isNothing(); + long value2 = isNothing2 ? 0 : other.getItemAsLong(); + zipper.accept(idx++, value1, isNothing1, value2, isNothing2); + context.safepoint(); + hasValue1 = hasValue1 && moveNext(); + hasValue2 = hasValue2 && other.moveNext(); + } + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnStorage.java index 09cc34737ed2..cb057dbbe3c3 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnStorage.java @@ -3,7 +3,7 @@ import org.enso.table.data.column.storage.type.StorageType; /** Basic interface of a column storage. */ -public interface ColumnStorage { +public interface ColumnStorage extends Iterable { /* Gets the size of the storage. */ long getSize(); @@ -20,4 +20,7 @@ public interface ColumnStorage { /* Gets the value at a given index. */ T getItemBoxed(long index); + + @Override + ColumnStorageIterator iterator(); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnStorageFacade.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnStorageFacade.java new file mode 100644 index 000000000000..7a55b511ac0b --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnStorageFacade.java @@ -0,0 +1,41 @@ +package org.enso.table.data.column.storage; + +import java.util.function.Function; +import org.enso.table.data.column.storage.type.StorageType; + +/** A facade for a column storage that converts the stored type to another type. */ +public final class ColumnStorageFacade implements ColumnStorage { + private final ColumnStorage parent; + private final Function converter; + + public ColumnStorageFacade(ColumnStorage parent, Function converter) { + this.parent = parent; + this.converter = converter; + } + + @Override + public long getSize() { + return parent.getSize(); + } + + @Override + public StorageType getType() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public boolean isNothing(long index) { + return parent.isNothing(index); + } + + @Override + public T getItemBoxed(long index) { + S item = parent.getItemBoxed(index); + return item == null ? null : converter.apply(item); + } + + @Override + public ColumnStorageIterator iterator() { + return new Storage.StorageIterator<>(this); + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnStorageIterator.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnStorageIterator.java new file mode 100644 index 000000000000..c0cb3cf1d496 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/ColumnStorageIterator.java @@ -0,0 +1,18 @@ +package org.enso.table.data.column.storage; + +import java.util.Iterator; + +public interface ColumnStorageIterator extends Iterator { + // Gets the current item. + T getItemBoxed(); + + // Checks whether the value at idx is Nothing. + boolean isNothing(); + + // Gets the current index; + long getIndex(); + + // Moves to the next item. + // Returns true if not finished, false otherwise. + boolean moveNext(); +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/SpecializedStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/SpecializedStorage.java index a39e08b36c4f..292fb02cca79 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/SpecializedStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/SpecializedStorage.java @@ -2,6 +2,7 @@ import java.util.BitSet; import java.util.List; +import java.util.NoSuchElementException; import org.enso.table.data.column.operation.CountNothing; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.operation.map.MapOperationStorage; @@ -189,4 +190,55 @@ public SpecializedStorage castIfSameType(SpecializedStorage storage) { return null; } } + + @Override + public ColumnStorageIterator iterator() { + return new SpecializedStorageIterator<>(data); + } + + private static class SpecializedStorageIterator implements ColumnStorageIterator { + private final T[] data; + private int index = -1; + + public SpecializedStorageIterator(T[] data) { + this.data = data; + } + + @Override + public T getItemBoxed() { + return data[index]; + } + + @Override + public boolean isNothing() { + return data[index] == null; + } + + @Override + public boolean hasNext() { + return index + 1 < data.length; + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return data[++index]; + } + + @Override + public long getIndex() { + return index; + } + + @Override + public boolean moveNext() { + if (!hasNext()) { + return false; + } + index++; + return true; + } + } } 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 41797930cd02..99a8c5143939 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 @@ -3,6 +3,7 @@ import java.util.BitSet; import java.util.HashMap; import java.util.List; +import java.util.NoSuchElementException; import java.util.function.BiFunction; import org.enso.base.polyglot.Polyglot_Utils; import org.enso.table.data.column.builder.Builder; @@ -437,4 +438,55 @@ public static Storage fromRepeatedItem( return builder.seal(); } + + @Override + public ColumnStorageIterator iterator() { + return new StorageIterator<>(this); + } + + public static class StorageIterator implements ColumnStorageIterator { + protected final ColumnStorage parent; + protected long index = -1; + + public StorageIterator(ColumnStorage parent) { + this.parent = parent; + } + + @Override + public T getItemBoxed() { + return parent.getItemBoxed(index); + } + + @Override + public boolean isNothing() { + return parent.isNothing(index); + } + + @Override + public boolean hasNext() { + return index + 1 < parent.getSize(); + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return parent.getItemBoxed(++index); + } + + @Override + public long getIndex() { + return index; + } + + @Override + public boolean moveNext() { + if (!hasNext()) { + return false; + } + index++; + return true; + } + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java index e27c73a26e80..a2ebf8e9907c 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java @@ -20,10 +20,7 @@ import org.enso.table.data.column.operation.map.numeric.comparisons.LessComparison; import org.enso.table.data.column.operation.map.numeric.comparisons.LessOrEqualComparison; import org.enso.table.data.column.operation.map.numeric.isin.LongIsInOp; -import org.enso.table.data.column.storage.BoolStorage; -import org.enso.table.data.column.storage.ColumnLongStorage; -import org.enso.table.data.column.storage.Storage; -import org.enso.table.data.column.storage.ValueIsNothingException; +import org.enso.table.data.column.storage.*; import org.enso.table.data.column.storage.type.IntegerType; import org.enso.table.data.column.storage.type.StorageType; import org.enso.table.data.mask.OrderMask; @@ -273,7 +270,7 @@ public Storage appendNulls(int count) { int size = (int) parent.getSize(); return new ComputedNullableLongStorage(size + count) { @Override - protected Long computeItem(int idx) { + protected Long computeItem(long idx) { if (idx < size) { return parent.getItemBoxed(idx); } else { @@ -282,4 +279,23 @@ protected Long computeItem(int idx) { } }; } + + @Override + public ColumnLongStorageIterator iterator() { + return new BaseLongStorageIterator(this); + } + + /** Basic iterator for long storages. */ + public static class BaseLongStorageIterator extends StorageIterator + implements ColumnLongStorageIterator { + public BaseLongStorageIterator(ColumnLongStorage parent) { + super(parent); + } + + @Override + public long getItemAsLong() { + Long l = getItemBoxed(); + return l == null ? 0 : l; + } + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/BigIntegerStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/BigIntegerStorage.java index 638351d2f51a..a6cf416e89cb 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/BigIntegerStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/BigIntegerStorage.java @@ -139,7 +139,7 @@ private StorageType findSmallestIntegerTypeThatFits() { ComputedNullableLongStorage longAdapter = new ComputedNullableLongStorage((int) getSize()) { @Override - protected Long computeItem(int idx) { + protected Long computeItem(long idx) { BigInteger bigInteger = parent.getItemBoxed(idx); if (bigInteger == null) { return null; diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/ComputedLongStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/ComputedLongStorage.java index 1e489b5395ce..23978eb23080 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/ComputedLongStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/ComputedLongStorage.java @@ -14,7 +14,7 @@ public abstract class ComputedLongStorage extends AbstractLongStorage implements ColumnStorageWithNothingMap { private static final BitSet EMPTY = new BitSet(); - protected abstract long computeItem(int idx); + protected abstract long computeItem(long idx); protected ComputedLongStorage(int size) { super(size, IntegerType.INT_64); @@ -25,7 +25,7 @@ public long getItemAsLong(long index) throws ValueIsNothingException { if (index < 0 || index >= getSize()) { throw new IndexOutOfBoundsException(index); } - return computeItem((int) index); + return computeItem(index); } @Override diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/ComputedNullableLongStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/ComputedNullableLongStorage.java index 69cc52436460..ff1e661f50b1 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/ComputedNullableLongStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/ComputedNullableLongStorage.java @@ -14,7 +14,7 @@ */ public abstract class ComputedNullableLongStorage extends AbstractLongStorage implements ColumnStorageWithNothingMap { - protected abstract Long computeItem(int idx); + protected abstract Long computeItem(long idx); private BitSet isNothing; diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java index 583f5fe78157..82b026c1bf5d 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java @@ -3,6 +3,7 @@ import java.math.BigInteger; import java.util.BitSet; import java.util.List; +import java.util.NoSuchElementException; import org.enso.table.data.column.builder.Builder; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.operation.map.MapOperationStorage; @@ -20,13 +21,8 @@ import org.enso.table.data.column.operation.map.numeric.comparisons.GreaterOrEqualComparison; import org.enso.table.data.column.operation.map.numeric.comparisons.LessComparison; import org.enso.table.data.column.operation.map.numeric.comparisons.LessOrEqualComparison; -import org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter; import org.enso.table.data.column.operation.map.numeric.isin.DoubleIsInOp; -import org.enso.table.data.column.storage.BoolStorage; -import org.enso.table.data.column.storage.ColumnDoubleStorage; -import org.enso.table.data.column.storage.ColumnStorageWithNothingMap; -import org.enso.table.data.column.storage.Storage; -import org.enso.table.data.column.storage.ValueIsNothingException; +import org.enso.table.data.column.storage.*; import org.enso.table.data.column.storage.type.FloatType; import org.enso.table.data.column.storage.type.IntegerType; import org.enso.table.data.column.storage.type.StorageType; @@ -40,9 +36,9 @@ /** A column containing floating point numbers. */ public final class DoubleStorage extends Storage - implements DoubleArrayAdapter, ColumnStorageWithNothingMap, ColumnDoubleStorage { - private final double[] data; - private final BitSet isNothing; + implements ColumnDoubleStorage, ColumnStorageWithNothingMap { + final double[] data; + final BitSet isNothing; private final int size; private static final MapOperationStorage ops = buildOps(); @@ -101,21 +97,6 @@ public boolean isNothing(long idx) { return isNothing.get((int) idx); } - @Override - public long size() { - return getSize(); - } - - /** Used by the DoubleBuilder in appendBulkStorage. */ - public double[] getRawData() { - return data; - } - - @Override - public DoubleStorage intoStorage() { - return this; - } - @Override public boolean isBinaryOpVectorized(String op) { return ops.isSupportedBinary(op); @@ -397,7 +378,7 @@ private StorageType findSmallestIntegerTypeThatFits() { ComputedNullableLongStorage longAdapter = new ComputedNullableLongStorage(size) { @Override - protected Long computeItem(int idx) { + protected Long computeItem(long idx) { if (parent.isNothing(idx)) { return null; } @@ -412,4 +393,152 @@ protected Long computeItem(int idx) { // And rely on its shrinking logic. return longAdapter.inferPreciseTypeShrunk(); } + + /** Allow access to the underlying data array for copying. */ + public double[] getArray() { + return data; + } + + @Override + public ColumnDoubleStorageIterator iterator() { + return new DoubleStorageIterator(data, isNothing, (int) getSize()); + } + + private static class DoubleStorageIterator implements ColumnDoubleStorageIterator { + private final double[] data; + private final BitSet isNothing; + private final int size; + private int index = -1; + + public DoubleStorageIterator(double[] data, BitSet isNothing, int size) { + this.data = data; + this.isNothing = isNothing; + this.size = size; + } + + @Override + public Double getItemBoxed() { + return isNothing.get(index) ? null : data[index]; + } + + @Override + public double getItemAsDouble() { + return data[index]; + } + + @Override + public boolean isNothing() { + return isNothing.get(index); + } + + @Override + public boolean hasNext() { + return index + 1 < size; + } + + @Override + public Double next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + index++; + return getItemBoxed(); + } + + @Override + public long getIndex() { + return index; + } + + @Override + public boolean moveNext() { + if (!hasNext()) { + return false; + } + index++; + return true; + } + + @Override + public void zip(ColumnDoubleStorage otherStorage, DoubleDoubleZipper zipper) { + Context context = Context.getCurrent(); + var otherSize = otherStorage.getSize(); + var toCount = Math.max(size, otherSize); + + if (otherStorage instanceof DoubleStorage doubleStorage) { + for (int i = 0; i < toCount; i++) { + boolean isNothing1 = i >= size || isNothing.get(i); + double value1 = isNothing1 ? Double.NaN : data[i]; + boolean isNothing2 = i >= otherSize || doubleStorage.isNothing.get(i); + double value2 = isNothing2 ? Double.NaN : doubleStorage.data[i]; + zipper.accept(i, value1, isNothing1, value2, isNothing2); + context.safepoint(); + } + } else { + int minSize = (int) Math.min(size, otherSize); + for (int i = 0; i < minSize; i++) { + boolean isNothing1 = i >= size || isNothing.get(i); + double value1 = isNothing1 ? 0 : data[i]; + boolean isNothing2 = otherStorage.isNothing(i); + if (isNothing2) { + zipper.accept(i, value1, isNothing1, Double.NaN, true); + } else { + zipper.accept(i, value1, isNothing1, otherStorage.getItemAsDouble(i), false); + } + context.safepoint(); + } + + for (long i = minSize; i < toCount; i++) { + var isNothing2 = otherStorage.isNothing(i); + if (isNothing2) { + zipper.accept(i, 0, true, Double.NaN, true); + } else { + zipper.accept(i, 0, true, otherStorage.getItemAsDouble(i), false); + } + context.safepoint(); + } + } + } + + @Override + public void zip(ColumnLongStorage otherStorage, DoubleLongZipper zipper) { + Context context = Context.getCurrent(); + var otherSize = otherStorage.getSize(); + var toCount = Math.max(size, otherSize); + + if (otherStorage instanceof LongStorage longStorage) { + for (int i = 0; i < toCount; i++) { + boolean isNothing1 = i >= size || isNothing.get(i); + double value1 = isNothing1 ? Double.NaN : data[i]; + boolean isNothing2 = i >= otherSize || longStorage.isNothing.get(i); + long value2 = isNothing2 ? 0 : longStorage.data[i]; + zipper.accept(i, value1, isNothing1, value2, isNothing2); + context.safepoint(); + } + } else { + int minSize = (int) Math.min(size, otherSize); + for (int i = 0; i < minSize; i++) { + boolean isNothing1 = i >= size || isNothing.get(i); + double value1 = isNothing1 ? 0 : data[i]; + var isNothing2 = otherStorage.isNothing(i); + if (isNothing2) { + zipper.accept(i, value1, isNothing1, 0, true); + } else { + zipper.accept(i, value1, isNothing1, otherStorage.getItemAsLong(i), false); + } + context.safepoint(); + } + + for (long i = minSize; i < toCount; i++) { + var isNothing2 = otherStorage.isNothing(i); + if (isNothing2) { + zipper.accept(i, 0, true, 0, true); + } else { + zipper.accept(i, 0, true, otherStorage.getItemAsLong(i), false); + } + context.safepoint(); + } + } + } + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorageFacade.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorageFacade.java new file mode 100644 index 000000000000..938e340483ee --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorageFacade.java @@ -0,0 +1,75 @@ +package org.enso.table.data.column.storage.numeric; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.function.ToDoubleFunction; +import org.enso.table.data.column.storage.*; +import org.enso.table.data.column.storage.type.FloatType; +import org.enso.table.data.column.storage.type.StorageType; + +/** A facade for a column storage that converts the stored type to a double. */ +public final class DoubleStorageFacade implements ColumnDoubleStorage { + private final ColumnStorage parent; + private final ToDoubleFunction converter; + + public DoubleStorageFacade(ColumnStorage parent, ToDoubleFunction converter) { + this.parent = parent; + this.converter = converter; + } + + public static ColumnDoubleStorage forBigInteger(ColumnStorage parent) { + return new DoubleStorageFacade<>(parent, BigInteger::doubleValue); + } + + public static ColumnDoubleStorage forBigDecimal(ColumnStorage parent) { + return new DoubleStorageFacade<>(parent, BigDecimal::doubleValue); + } + + @Override + public double getItemAsDouble(long index) throws ValueIsNothingException { + if (isNothing(index)) { + throw new ValueIsNothingException(index); + } + T item = parent.getItemBoxed(index); + return converter.applyAsDouble(item); + } + + @Override + public long getSize() { + return parent.getSize(); + } + + @Override + public StorageType getType() { + return FloatType.FLOAT_64; + } + + @Override + public boolean isNothing(long index) { + return parent.isNothing(index); + } + + @Override + public Double getItemBoxed(long index) { + T item = parent.getItemBoxed(index); + return item == null ? null : converter.applyAsDouble(item); + } + + @Override + public ColumnDoubleStorageIterator iterator() { + return new BaseDoubleStorageIterator(this); + } + + private static class BaseDoubleStorageIterator extends Storage.StorageIterator + implements ColumnDoubleStorageIterator { + public BaseDoubleStorageIterator(ColumnDoubleStorage parent) { + super(parent); + } + + @Override + public double getItemAsDouble() { + Double d = getItemBoxed(); + return d == null ? Double.NaN : d; + } + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongConstantStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongConstantStorage.java index e26621654d65..bdf0dd02883f 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongConstantStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongConstantStorage.java @@ -9,7 +9,7 @@ public LongConstantStorage(long constant, int size) { } @Override - protected long computeItem(int idx) { + protected long computeItem(long idx) { return constant; } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongRangeStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongRangeStorage.java index 3c4135dcb797..54601fa3c6b0 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongRangeStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongRangeStorage.java @@ -23,7 +23,7 @@ private void verifyBounds() throws ArithmeticException { } @Override - protected long computeItem(int idx) { + protected long computeItem(long idx) { return start + idx * step; } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongStorage.java index 62ac152012b1..bd02010790a1 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongStorage.java @@ -3,11 +3,10 @@ import java.math.BigInteger; import java.util.BitSet; import java.util.List; +import java.util.NoSuchElementException; import org.enso.base.polyglot.NumericConverter; import org.enso.table.data.column.builder.Builder; -import org.enso.table.data.column.storage.ColumnStorageWithNothingMap; -import org.enso.table.data.column.storage.Storage; -import org.enso.table.data.column.storage.ValueIsNothingException; +import org.enso.table.data.column.storage.*; import org.enso.table.data.column.storage.type.FloatType; import org.enso.table.data.column.storage.type.IntegerType; import org.enso.table.data.column.storage.type.StorageType; @@ -22,8 +21,8 @@ public final class LongStorage extends AbstractLongStorage implements ColumnStor // TODO [RW] at some point we will want to add separate storage classes for byte, short and int, // for more compact storage and more efficient handling of smaller integers; for now we will be // handling this just by checking the bounds - private final long[] data; - private final BitSet isNothing; + final long[] data; + final BitSet isNothing; /** * @param data the underlying data @@ -49,15 +48,9 @@ public LongStorage(long[] data, IntegerType type) { this(data, data.length, new BitSet(), type); } - /** - * @param idx an index - * @return the data item contained at the given index. - */ - public long getItemAsLong(long idx) { - if (isNothing(idx)) { - throw new ValueIsNothingException(idx); - } - return data[Math.toIntExact(idx)]; + @Override + public long getItemAsLong(long index) { + return data[(int) index]; } @Override @@ -132,10 +125,6 @@ public Storage fillMissing( return super.fillMissing(arg, commonType, problemAggregator); } - public long[] getRawData() { - return data; - } - @Override public LongStorage slice(int offset, int limit) { int size = (int) getSize(); @@ -198,4 +187,152 @@ public LongStorage widen(IntegerType widerType) { assert widerType.fits(getType()); return new LongStorage(data, (int) getSize(), getIsNothingMap(), widerType); } + + /** Allow access to the underlying data array for copying. */ + public long[] getArray() { + return data; + } + + @Override + public ColumnLongStorageIterator iterator() { + return new LongStorageIterator(data, isNothing, (int) getSize()); + } + + private static class LongStorageIterator implements ColumnLongStorageIterator { + private final long[] data; + private final BitSet isNothing; + private final int size; + private int index = -1; + + public LongStorageIterator(long[] data, BitSet isNothing, int size) { + this.data = data; + this.isNothing = isNothing; + this.size = size; + } + + @Override + public Long getItemBoxed() { + return isNothing.get(index) ? null : data[index]; + } + + @Override + public long getItemAsLong() { + return data[index]; + } + + @Override + public boolean isNothing() { + return isNothing.get(index); + } + + @Override + public boolean hasNext() { + return index + 1 < size; + } + + @Override + public Long next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + index++; + return getItemBoxed(); + } + + @Override + public long getIndex() { + return index; + } + + @Override + public boolean moveNext() { + if (!hasNext()) { + return false; + } + index++; + return true; + } + + @Override + public void zip(ColumnDoubleStorage otherStorage, LongDoubleZipper zipper) { + Context context = Context.getCurrent(); + var otherSize = otherStorage.getSize(); + var toCount = Math.max(size, otherSize); + + if (otherStorage instanceof DoubleStorage doubleStorage) { + for (int i = 0; i < toCount; i++) { + boolean isNothing1 = i >= size || isNothing.get(i); + long value1 = isNothing1 ? 0 : data[i]; + boolean isNothing2 = i >= otherSize || doubleStorage.isNothing.get(i); + double value2 = isNothing2 ? Double.NaN : doubleStorage.data[i]; + zipper.accept(i, value1, isNothing1, value2, isNothing2); + context.safepoint(); + } + } else { + int minSize = (int) Math.min(size, otherSize); + for (int i = 0; i < minSize; i++) { + boolean isNothing1 = i >= size || isNothing.get(i); + long value1 = isNothing1 ? 0 : data[i]; + var isNothing2 = otherStorage.isNothing(i); + if (isNothing2) { + zipper.accept(i, value1, isNothing1, Double.NaN, true); + } else { + zipper.accept(i, value1, isNothing1, otherStorage.getItemAsDouble(i), false); + } + context.safepoint(); + } + + for (long i = minSize; i < toCount; i++) { + var isNothing2 = otherStorage.isNothing(i); + if (isNothing2) { + zipper.accept(i, 0, true, Double.NaN, true); + } else { + zipper.accept(i, 0, true, otherStorage.getItemAsDouble(i), false); + } + context.safepoint(); + } + } + } + + @Override + public void zip(ColumnLongStorage otherStorage, LongLongZipper zipper) { + Context context = Context.getCurrent(); + var otherSize = otherStorage.getSize(); + var toCount = Math.max(size, otherSize); + + if (otherStorage instanceof LongStorage longStorage) { + for (int i = 0; i < toCount; i++) { + boolean isNothing1 = i >= size || isNothing.get(i); + long value1 = isNothing1 ? 0 : data[i]; + boolean isNothing2 = i >= otherSize || longStorage.isNothing.get(i); + long value2 = isNothing2 ? 0 : longStorage.data[i]; + zipper.accept(i, value1, isNothing1, value2, isNothing2); + context.safepoint(); + } + } else { + int minSize = (int) Math.min(size, otherSize); + for (int i = 0; i < minSize; i++) { + boolean isNothing1 = i >= size || isNothing.get(i); + long value1 = isNothing1 ? 0 : data[i]; + var isNothing2 = otherStorage.isNothing(i); + if (isNothing2) { + zipper.accept(i, value1, isNothing1, 0, true); + } else { + zipper.accept(i, value1, isNothing1, otherStorage.getItemAsLong(i), false); + } + context.safepoint(); + } + + for (long i = minSize; i < toCount; i++) { + var isNothing2 = otherStorage.isNothing(i); + if (isNothing2) { + zipper.accept(i, 0, true, 0, true); + } else { + zipper.accept(i, 0, true, otherStorage.getItemAsLong(i), false); + } + context.safepoint(); + } + } + } + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongStorageFacade.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongStorageFacade.java new file mode 100644 index 000000000000..57b27f9d49ee --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/LongStorageFacade.java @@ -0,0 +1,55 @@ +package org.enso.table.data.column.storage.numeric; + +import java.util.function.ToLongFunction; +import org.enso.table.data.column.storage.ColumnLongStorage; +import org.enso.table.data.column.storage.ColumnLongStorageIterator; +import org.enso.table.data.column.storage.ColumnStorage; +import org.enso.table.data.column.storage.ValueIsNothingException; +import org.enso.table.data.column.storage.type.IntegerType; +import org.enso.table.data.column.storage.type.StorageType; + +/** A facade for a column storage that converts the stored type to a long. */ +public class LongStorageFacade implements ColumnLongStorage { + private final ColumnStorage parent; + private final ToLongFunction converter; + + public LongStorageFacade(ColumnStorage parent, ToLongFunction converter) { + this.parent = parent; + this.converter = converter; + } + + @Override + public long getItemAsLong(long index) throws ValueIsNothingException { + if (isNothing(index)) { + throw new ValueIsNothingException(index); + } + T item = parent.getItemBoxed(index); + return converter.applyAsLong(item); + } + + @Override + public long getSize() { + return parent.getSize(); + } + + @Override + public StorageType getType() { + return IntegerType.INT_64; + } + + @Override + public boolean isNothing(long index) { + return parent.isNothing(index); + } + + @Override + public Long getItemBoxed(long index) { + T item = parent.getItemBoxed(index); + return item == null ? null : converter.applyAsLong(item); + } + + @Override + public ColumnLongStorageIterator iterator() { + return new AbstractLongStorage.BaseLongStorageIterator(this); + } +}