diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/UpdateByOperatorFactory.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/UpdateByOperatorFactory.java index 0c5bbccef08..615d74d0407 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/UpdateByOperatorFactory.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/UpdateByOperatorFactory.java @@ -856,6 +856,8 @@ private UpdateByOperator makeCumMinMaxOperator(MatchPair pair, TableDefinition t if (csType == byte.class || csType == Byte.class) { return new ByteCumMinMaxOperator(pair, isMax, NULL_BYTE); + } else if (csType == char.class || csType == Character.class) { + return new CharCumMinMaxOperator(pair, isMax); } else if (csType == short.class || csType == Short.class) { return new ShortCumMinMaxOperator(pair, isMax); } else if (csType == int.class || csType == Integer.class) { @@ -1033,7 +1035,7 @@ private UpdateByOperator makeRollingGroupOperator(@NotNull final MatchPair[] pai return new RollingGroupOperator(pairs, affectingColumns, rg.revWindowScale().timestampCol(), - prevWindowScaleUnits, fwdWindowScaleUnits, tableDef); + prevWindowScaleUnits, fwdWindowScaleUnits); } private UpdateByOperator makeRollingAvgOperator(@NotNull final MatchPair pair, @@ -1132,7 +1134,7 @@ private UpdateByOperator makeRollingMinMaxOperator(@NotNull MatchPair pair, } else if (csType == long.class || csType == Long.class || isTimeType(csType)) { return new LongRollingMinMaxOperator(pair, affectingColumns, rmm.revWindowScale().timestampCol(), - prevWindowScaleUnits, fwdWindowScaleUnits, rmm.isMax()); + prevWindowScaleUnits, fwdWindowScaleUnits, rmm.isMax(), csType); } else if (csType == float.class || csType == Float.class) { return new FloatRollingMinMaxOperator(pair, affectingColumns, rmm.revWindowScale().timestampCol(), diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/ByteCumMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/ByteCumMinMaxOperator.java index 2e46038ae6f..a28f9e4056d 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/ByteCumMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/ByteCumMinMaxOperator.java @@ -2,7 +2,7 @@ // Copyright (c) 2016-2025 Deephaven Data Labs and Patent Pending // // ****** AUTO-GENERATED CLASS - DO NOT EDIT MANUALLY -// ****** Edit ShortCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate +// ****** Edit CharCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate // // @formatter:off package io.deephaven.engine.table.impl.updateby.minmax; diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/CharCumMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/CharCumMinMaxOperator.java new file mode 100644 index 00000000000..96903d94b0b --- /dev/null +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/CharCumMinMaxOperator.java @@ -0,0 +1,82 @@ +// +// Copyright (c) 2016-2025 Deephaven Data Labs and Patent Pending +// +package io.deephaven.engine.table.impl.updateby.minmax; + +import io.deephaven.base.verify.Assert; +import io.deephaven.chunk.Chunk; +import io.deephaven.chunk.CharChunk; +import io.deephaven.chunk.attributes.Values; +import io.deephaven.engine.table.impl.MatchPair; +import io.deephaven.engine.table.impl.updateby.UpdateByOperator; +import io.deephaven.engine.table.impl.updateby.internal.BaseCharUpdateByOperator; +import org.jetbrains.annotations.NotNull; + +import static io.deephaven.util.QueryConstants.*; + +public class CharCumMinMaxOperator extends BaseCharUpdateByOperator { + private final boolean isMax; + + // region extra-fields + // endregion extra-fields + + protected class Context extends BaseCharUpdateByOperator.Context { + public CharChunk charValueChunk; + + protected Context(final int chunkSize) { + super(chunkSize); + } + + @Override + public void setValueChunks(@NotNull final Chunk[] valueChunks) { + charValueChunk = valueChunks[0].asCharChunk(); + } + + @Override + public void push(int pos, int count) { + Assert.eq(count, "push count", 1); + + final char val = charValueChunk.get(pos); + + if (curVal == NULL_CHAR) { + curVal = val; + } else if (val != NULL_CHAR) { + if ((isMax && val > curVal) || + (!isMax && val < curVal)) { + curVal = val; + } + } + } + } + + public CharCumMinMaxOperator( + @NotNull final MatchPair pair, + final boolean isMax + // region extra-constructor-args + // endregion extra-constructor-args + ) { + super(pair, new String[] {pair.rightColumn}); + this.isMax = isMax; + // region constructor + // endregion constructor + } + + @Override + public UpdateByOperator copy() { + return new CharCumMinMaxOperator( + pair, + isMax + // region extra-copy-args + // endregion extra-copy-args + ); + } + + @NotNull + @Override + public UpdateByOperator.Context makeUpdateContext(final int affectedChunkSize, final int influencerChunkSize) { + return new Context(affectedChunkSize); + } + + // region extra-methods + // endregion extra-methods +} diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/DoubleCumMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/DoubleCumMinMaxOperator.java index 74621ae90ce..f2328f24fea 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/DoubleCumMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/DoubleCumMinMaxOperator.java @@ -2,7 +2,7 @@ // Copyright (c) 2016-2025 Deephaven Data Labs and Patent Pending // // ****** AUTO-GENERATED CLASS - DO NOT EDIT MANUALLY -// ****** Edit ShortCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate +// ****** Edit CharCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate // // @formatter:off package io.deephaven.engine.table.impl.updateby.minmax; diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/FloatCumMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/FloatCumMinMaxOperator.java index 7a77cfd5a64..5b65d587e40 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/FloatCumMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/FloatCumMinMaxOperator.java @@ -2,7 +2,7 @@ // Copyright (c) 2016-2025 Deephaven Data Labs and Patent Pending // // ****** AUTO-GENERATED CLASS - DO NOT EDIT MANUALLY -// ****** Edit ShortCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate +// ****** Edit CharCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate // // @formatter:off package io.deephaven.engine.table.impl.updateby.minmax; diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/IntCumMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/IntCumMinMaxOperator.java index fe80322cfb0..3c1138f0387 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/IntCumMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/IntCumMinMaxOperator.java @@ -2,7 +2,7 @@ // Copyright (c) 2016-2025 Deephaven Data Labs and Patent Pending // // ****** AUTO-GENERATED CLASS - DO NOT EDIT MANUALLY -// ****** Edit ShortCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate +// ****** Edit CharCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate // // @formatter:off package io.deephaven.engine.table.impl.updateby.minmax; diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/LongCumMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/LongCumMinMaxOperator.java index 631db3153df..6e4ee7df19c 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/LongCumMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/LongCumMinMaxOperator.java @@ -2,7 +2,7 @@ // Copyright (c) 2016-2025 Deephaven Data Labs and Patent Pending // // ****** AUTO-GENERATED CLASS - DO NOT EDIT MANUALLY -// ****** Edit ShortCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate +// ****** Edit CharCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate // // @formatter:off package io.deephaven.engine.table.impl.updateby.minmax; diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/ShortCumMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/ShortCumMinMaxOperator.java index 47c60057ef9..36d0be14763 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/ShortCumMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/ShortCumMinMaxOperator.java @@ -1,6 +1,10 @@ // // Copyright (c) 2016-2025 Deephaven Data Labs and Patent Pending // +// ****** AUTO-GENERATED CLASS - DO NOT EDIT MANUALLY +// ****** Edit CharCumMinMaxOperator and run "./gradlew replicateUpdateBy" to regenerate +// +// @formatter:off package io.deephaven.engine.table.impl.updateby.minmax; import io.deephaven.base.verify.Assert; diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollinggroup/RollingGroupOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollinggroup/RollingGroupOperator.java index 0796b4853c6..44272887757 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollinggroup/RollingGroupOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollinggroup/RollingGroupOperator.java @@ -234,8 +234,7 @@ public RollingGroupOperator( @NotNull final String[] affectingColumns, @Nullable final String timestampColumnName, final long reverseWindowScaleUnits, - final long forwardWindowScaleUnits, - @NotNull final TableDefinition tableDef) { + final long forwardWindowScaleUnits) { super(pairs[0], affectingColumns, timestampColumnName, reverseWindowScaleUnits, forwardWindowScaleUnits, true); this.pairs = pairs; diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/ByteRollingMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/ByteRollingMinMaxOperator.java index 71bb41c3dae..ac24886d025 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/ByteRollingMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/ByteRollingMinMaxOperator.java @@ -168,4 +168,7 @@ public UpdateByOperator copy() { // endregion extra-copy-args ); } + + // region extra-methods + // endregion extra-methods } diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/CharRollingMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/CharRollingMinMaxOperator.java index 68e70c257f9..dd9d202959b 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/CharRollingMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/CharRollingMinMaxOperator.java @@ -164,4 +164,7 @@ public UpdateByOperator copy() { // endregion extra-copy-args ); } + + // region extra-methods + // endregion extra-methods } diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/DoubleRollingMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/DoubleRollingMinMaxOperator.java index 596114edcc9..41453e80062 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/DoubleRollingMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/DoubleRollingMinMaxOperator.java @@ -168,4 +168,7 @@ public UpdateByOperator copy() { // endregion extra-copy-args ); } + + // region extra-methods + // endregion extra-methods } diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/FloatRollingMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/FloatRollingMinMaxOperator.java index 5f60ed3ecea..2f5d0414294 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/FloatRollingMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/FloatRollingMinMaxOperator.java @@ -168,4 +168,7 @@ public UpdateByOperator copy() { // endregion extra-copy-args ); } + + // region extra-methods + // endregion extra-methods } diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/IntRollingMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/IntRollingMinMaxOperator.java index a49ce3382a2..5b60f469659 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/IntRollingMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/IntRollingMinMaxOperator.java @@ -168,4 +168,7 @@ public UpdateByOperator copy() { // endregion extra-copy-args ); } + + // region extra-methods + // endregion extra-methods } diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/LongRollingMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/LongRollingMinMaxOperator.java index 0f8fc39decf..e4d2cbb4ed5 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/LongRollingMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/LongRollingMinMaxOperator.java @@ -7,6 +7,13 @@ // @formatter:off package io.deephaven.engine.table.impl.updateby.rollingminmax; +import java.time.Instant; +import java.util.Map; +import java.util.Collections; + +import io.deephaven.engine.table.ColumnSource; +import io.deephaven.engine.table.impl.sources.ReinterpretUtils; + import io.deephaven.base.ringbuffer.AggregatingLongRingBuffer; import io.deephaven.base.verify.Assert; import io.deephaven.chunk.LongChunk; @@ -24,6 +31,7 @@ public class LongRollingMinMaxOperator extends BaseLongUpdateByOperator { private final boolean isMax; private static final int BUFFER_INITIAL_CAPACITY = 128; // region extra-fields + private final Class type; // endregion extra-fields protected class Context extends BaseLongUpdateByOperator.Context { @@ -147,11 +155,13 @@ public LongRollingMinMaxOperator( final long forwardWindowScaleUnits, final boolean isMax // region extra-constructor-args + ,@NotNull final Class type // endregion extra-constructor-args ) { super(pair, affectingColumns, timestampColumnName, reverseWindowScaleUnits, forwardWindowScaleUnits, true); this.isMax = isMax; // region constructor + this.type = type; // endregion constructor } @@ -165,7 +175,22 @@ public UpdateByOperator copy() { forwardWindowScaleUnits, isMax // region extra-copy-args + , type // endregion extra-copy-args ); } + + // region extra-methods + @NotNull + @Override + public Map> getOutputColumns() { + final ColumnSource actualOutput; + if(type == Instant.class) { + actualOutput = ReinterpretUtils.longToInstantSource(outputSource); + } else { + actualOutput = outputSource; + } + return Collections.singletonMap(pair.leftColumn, actualOutput); + } + // endregion extra-methods } diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/ShortRollingMinMaxOperator.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/ShortRollingMinMaxOperator.java index 7d724f201d1..c1d4c7cf9cc 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/ShortRollingMinMaxOperator.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/rollingminmax/ShortRollingMinMaxOperator.java @@ -168,4 +168,7 @@ public UpdateByOperator copy() { // endregion extra-copy-args ); } + + // region extra-methods + // endregion extra-methods } diff --git a/engine/table/src/test/java/io/deephaven/engine/table/impl/updateby/TestCumMinMax.java b/engine/table/src/test/java/io/deephaven/engine/table/impl/updateby/TestCumMinMax.java index b11930a94c1..1aec33c64a9 100644 --- a/engine/table/src/test/java/io/deephaven/engine/table/impl/updateby/TestCumMinMax.java +++ b/engine/table/src/test/java/io/deephaven/engine/table/impl/updateby/TestCumMinMax.java @@ -5,44 +5,78 @@ import io.deephaven.api.updateby.UpdateByOperation; import io.deephaven.engine.context.ExecutionContext; +import io.deephaven.engine.context.QueryScope; +import io.deephaven.engine.table.ColumnDefinition; import io.deephaven.engine.table.PartitionedTable; import io.deephaven.engine.table.Table; +import io.deephaven.engine.table.TableDefinition; import io.deephaven.engine.table.vectors.ColumnVectors; import io.deephaven.engine.testutil.ControlledUpdateGraph; import io.deephaven.engine.testutil.EvalNugget; import io.deephaven.engine.table.impl.QueryTable; import io.deephaven.engine.testutil.TstUtils; +import io.deephaven.engine.testutil.generator.CharGenerator; import io.deephaven.engine.testutil.generator.TestDataGenerator; +import io.deephaven.engine.util.TableTools; import io.deephaven.function.Numeric; import io.deephaven.test.types.OutOfBandTest; -import io.deephaven.util.type.ArrayTypeUtils; +import io.deephaven.time.DateTimeUtils; import org.jetbrains.annotations.NotNull; import org.junit.Test; import org.junit.experimental.categories.Category; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Arrays; import java.util.List; import java.util.Random; import static io.deephaven.engine.testutil.GenerateTableUpdates.generateAppends; import static io.deephaven.engine.testutil.testcase.RefreshingTableTestCase.simulateShiftAwareStep; +import static io.deephaven.util.QueryConstants.NULL_CHAR; import static org.junit.Assert.assertArrayEquals; @Category(OutOfBandTest.class) public class TestCumMinMax extends BaseUpdateByTest { + private final int STATIC_TABLE_SIZE = 10000; + + private final String[] cumMin = new String[] { + "byteColMin=byteCol", + "charColMin=charCol", + "shortColMin=shortCol", + "intColMin=intCol", + "longColMin=longCol", + "floatColMin=floatCol", + "doubleColMin=doubleCol", + "bigIntColMin=bigIntCol", + "bigDecimalColMin=bigDecimalCol" + }; + + private final String[] cumMax = new String[] { + "byteColMax=byteCol", + "charColMax=charCol", + "shortColMax=shortCol", + "intColMax=intCol", + "longColMax=longCol", + "floatColMax=floatCol", + "doubleColMax=doubleCol", + "bigIntColMax=bigIntCol", + "bigDecimalColMax=bigDecimalCol" + }; + // region Zero Key Tests @Test public void testStaticZeroKey() { - final QueryTable t = createTestTable(100000, false, false, false, 0x2134BCFA).t; + final QueryTable t = createTestTable(STATIC_TABLE_SIZE, false, false, false, 0x2134BCFA, + new String[] {"charCol"}, + new TestDataGenerator[] {new CharGenerator('A', 'z', 0.1)}).t; final Table result = t.updateBy(List.of( - UpdateByOperation.CumMin("byteColMin=byteCol", "shortColMin=shortCol", "intColMin=intCol", - "longColMin=longCol", "floatColMin=floatCol", "doubleColMin=doubleCol", - "bigIntColMin=bigIntCol", "bigDecimalColMin=bigDecimalCol"), - UpdateByOperation.CumMax("byteColMax=byteCol", "shortColMax=shortCol", "intColMax=intCol", - "longColMax=longCol", "floatColMax=floatCol", "doubleColMax=doubleCol", - "bigIntColMax=bigIntCol", "bigDecimalColMax=bigDecimalCol"))); + UpdateByOperation.CumMin(cumMin), + UpdateByOperation.CumMax(cumMax))); for (String col : t.getDefinition().getColumnNamesArray()) { if ("boolCol".equals(col)) { continue; @@ -54,16 +88,13 @@ public void testStaticZeroKey() { @Test public void testStaticZeroKeyAllNulls() { - final QueryTable t = createTestTableAllNull(100000, false, false, false, 0x31313131, - ArrayTypeUtils.EMPTY_STRING_ARRAY, new TestDataGenerator[0]).t; + final QueryTable t = createTestTable(STATIC_TABLE_SIZE, false, false, false, 0x2134BCFA, + new String[] {"charCol"}, + new TestDataGenerator[] {new CharGenerator('A', 'z', 0.1)}).t; final Table result = t.updateBy(List.of( - UpdateByOperation.CumMin("byteColMin=byteCol", "shortColMin=shortCol", "intColMin=intCol", - "longColMin=longCol", "floatColMin=floatCol", "doubleColMin=doubleCol", - "bigIntColMin=bigIntCol", "bigDecimalColMin=bigDecimalCol"), - UpdateByOperation.CumMax("byteColMax=byteCol", "shortColMax=shortCol", "intColMax=intCol", - "longColMax=longCol", "floatColMax=floatCol", "doubleColMax=doubleCol", - "bigIntColMax=bigIntCol", "bigDecimalColMax=bigDecimalCol"))); + UpdateByOperation.CumMin(cumMin), + UpdateByOperation.CumMax(cumMax))); for (String col : t.getDefinition().getColumnNamesArray()) { if ("boolCol".equals(col)) { continue; @@ -92,15 +123,13 @@ public void testStaticGroupedBucketed() { } private void doTestStaticBucketed(boolean grouped) { - final QueryTable t = createTestTable(100000, true, grouped, false, 0xACDB4321).t; + final QueryTable t = createTestTable(STATIC_TABLE_SIZE, true, grouped, false, 0xACDB4321, + new String[] {"charCol"}, + new TestDataGenerator[] {new CharGenerator('A', 'z', 0.1)}).t; final Table result = t.updateBy(List.of( - UpdateByOperation.CumMin("byteColMin=byteCol", "shortColMin=shortCol", "intColMin=intCol", - "longColMin=longCol", "floatColMin=floatCol", "doubleColMin=doubleCol", - "bigIntColMin=bigIntCol", "bigDecimalColMin=bigDecimalCol"), - UpdateByOperation.CumMax("byteColMax=byteCol", "shortColMax=shortCol", "intColMax=intCol", - "longColMax=longCol", "floatColMax=floatCol", "doubleColMax=doubleCol", - "bigIntColMax=bigIntCol", "bigDecimalColMax=bigDecimalCol")), + UpdateByOperation.CumMin(cumMin), + UpdateByOperation.CumMax(cumMax)), "Sym"); final PartitionedTable preOp = t.partitionBy("Sym"); @@ -147,7 +176,9 @@ public void testBucketedGeneral() { } private void doTestTicking(boolean bucketed, boolean appendOnly) { - final CreateResult result = createTestTable(10000, bucketed, false, true, 0x31313131); + final CreateResult result = createTestTable(10000, bucketed, false, true, 0x31313131, + new String[] {"charCol"}, + new TestDataGenerator[] {new CharGenerator('A', 'z', 0.1)}); final QueryTable t = result.t; if (appendOnly) { @@ -184,6 +215,129 @@ protected Table e() { } // endregion + @Test + public void testResultDataTypes() { + final Instant baseInstant = DateTimeUtils.parseInstant("2023-01-01T00:00:00 NY"); + final ZoneId zone = ZoneId.of("America/Los_Angeles"); + + QueryScope.addParam("baseInstant", baseInstant); + QueryScope.addParam("baseLDT", LocalDateTime.ofInstant(baseInstant, zone)); + QueryScope.addParam("baseZDT", baseInstant.atZone(zone)); + + final TableDefinition expectedDefinition = TableDefinition.of( + ColumnDefinition.ofByte("byteCol"), + ColumnDefinition.ofChar("charCol"), + ColumnDefinition.ofShort("shortCol"), + ColumnDefinition.ofInt("intCol"), + ColumnDefinition.ofLong("longCol"), + ColumnDefinition.ofFloat("floatCol"), + ColumnDefinition.ofDouble("doubleCol"), + ColumnDefinition.ofString("stringCol"), + ColumnDefinition.fromGenericType("instantCol", Instant.class), + ColumnDefinition.fromGenericType("ldtCol", LocalDateTime.class), + ColumnDefinition.fromGenericType("zdtCol", ZonedDateTime.class)); + + final String[] columnNames = expectedDefinition.getColumnNamesArray(); + + final String[] updateStrings = new String[] { + "byteCol=(byte)i", + "charCol=(char)(i + 64)", + "shortCol=(short)i", + "intCol=i", + "longCol=ii", + "floatCol=(float)ii", + "doubleCol=(double)ii", + "stringCol=String.valueOf(i)", + "instantCol=baseInstant.plusSeconds(i)", + "ldtCol=baseLDT.plusSeconds(i)", + "zdtCol=baseZDT.plusSeconds(i)", + }; + + // NOTE: boolean is not supported by RollingMinMaxSpec.applicableTo() + final Table source = TableTools.emptyTable(20).update(updateStrings); + + // Verify all the source columns are the expected types. + source.getDefinition().checkCompatibility(expectedDefinition); + + final Table expected = source.updateBy(UpdateByOperation.CumMin(columnNames)); + + // Verify all the result columns are the expected types. + expected.getDefinition().checkCompatibility(expectedDefinition); + } + + @Test + public void testProxy() { + final QueryTable t = createTestTable(STATIC_TABLE_SIZE, true, false, false, 0x31313131, + new String[] {"charCol"}, + new TestDataGenerator[] {new CharGenerator('A', 'z', 0.1)}).t; + + Table actual; + Table expected; + + PartitionedTable pt = t.partitionBy("Sym"); + actual = pt.proxy() + .updateBy(UpdateByOperation.CumMin()) + .target().merge().sort("Sym"); + expected = t.sort("Sym").updateBy(UpdateByOperation.CumMin(), "Sym"); + TstUtils.assertTableEquals(expected, actual); + + actual = pt.proxy() + .updateBy(UpdateByOperation.CumMax()) + .target().merge().sort("Sym"); + expected = t.sort("Sym").updateBy(UpdateByOperation.CumMax(), "Sym"); + TstUtils.assertTableEquals(expected, actual); + } + + public static char[] cumMin(char... values) { + if (values == null) { + return null; + } + + if (values.length == 0) { + return new char[0]; + } + + char[] result = new char[values.length]; + result[0] = values[0]; + + for (int i = 1; i < values.length; i++) { + if (result[i - 1] == NULL_CHAR) { + result[i] = values[i]; + } else if (values[i] == NULL_CHAR) { + result[i] = result[i - 1]; + } else { + result[i] = values[i] < result[i - 1] ? values[i] : result[i - 1]; + } + } + + return result; + } + + public static char[] cumMax(char... values) { + if (values == null) { + return null; + } + + if (values.length == 0) { + return new char[0]; + } + + char[] result = new char[values.length]; + result[0] = values[0]; + + for (int i = 1; i < values.length; i++) { + if (result[i - 1] == NULL_CHAR) { + result[i] = values[i]; + } else if (values[i] == NULL_CHAR) { + result[i] = result[i - 1]; + } else { + result[i] = values[i] > result[i - 1] ? values[i] : result[i - 1]; + } + } + + return result; + } + public static Object[] cumMin(Object... values) { if (values == null) { return null; @@ -237,6 +391,8 @@ public static Object[] cumMax(Object... values) { final void assertWithCumMin(@NotNull final Object expected, @NotNull final Object actual) { if (expected instanceof byte[]) { assertArrayEquals(Numeric.cummin((byte[]) expected), (byte[]) actual); + } else if (expected instanceof char[]) { + assertArrayEquals(cumMin((char[]) expected), (char[]) actual); } else if (expected instanceof short[]) { assertArrayEquals(Numeric.cummin((short[]) expected), (short[]) actual); } else if (expected instanceof int[]) { @@ -255,6 +411,8 @@ final void assertWithCumMin(@NotNull final Object expected, @NotNull final Objec final void assertWithCumMax(@NotNull final Object expected, @NotNull final Object actual) { if (expected instanceof byte[]) { assertArrayEquals(Numeric.cummax((byte[]) expected), (byte[]) actual); + } else if (expected instanceof char[]) { + assertArrayEquals(cumMax((char[]) expected), (char[]) actual); } else if (expected instanceof short[]) { assertArrayEquals(Numeric.cummax((short[]) expected), (short[]) actual); } else if (expected instanceof int[]) { diff --git a/engine/table/src/test/java/io/deephaven/engine/table/impl/updateby/TestRollingMinMax.java b/engine/table/src/test/java/io/deephaven/engine/table/impl/updateby/TestRollingMinMax.java index ab4a15638da..69cea51c45b 100644 --- a/engine/table/src/test/java/io/deephaven/engine/table/impl/updateby/TestRollingMinMax.java +++ b/engine/table/src/test/java/io/deephaven/engine/table/impl/updateby/TestRollingMinMax.java @@ -8,7 +8,11 @@ import io.deephaven.api.updateby.UpdateByOperation; import io.deephaven.base.verify.Assert; import io.deephaven.engine.context.ExecutionContext; +import io.deephaven.engine.context.QueryScope; +import io.deephaven.engine.table.ColumnDefinition; +import io.deephaven.engine.table.PartitionedTable; import io.deephaven.engine.table.Table; +import io.deephaven.engine.table.TableDefinition; import io.deephaven.engine.table.impl.QueryTable; import io.deephaven.engine.table.vectors.ColumnVectors; import io.deephaven.engine.testutil.ControlledUpdateGraph; @@ -30,7 +34,7 @@ import java.math.BigDecimal; import java.math.BigInteger; -import java.time.Duration; +import java.time.*; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -1243,4 +1247,80 @@ public void testZeroDatasets() { // We can assert equality to the input table because the data values are constant. TstUtils.assertTableEquals(t, expected, TableDiff.DiffItems.DoublesExact); } + + @Test + public void testResultDataTypes() { + final Instant baseInstant = DateTimeUtils.parseInstant("2023-01-01T00:00:00 NY"); + final ZoneId zone = ZoneId.of("America/Los_Angeles"); + + QueryScope.addParam("baseInstant", baseInstant); + QueryScope.addParam("baseLDT", LocalDateTime.ofInstant(baseInstant, zone)); + QueryScope.addParam("baseZDT", baseInstant.atZone(zone)); + + final TableDefinition expectedDefinition = TableDefinition.of( + ColumnDefinition.ofByte("byteCol"), + ColumnDefinition.ofChar("charCol"), + ColumnDefinition.ofShort("shortCol"), + ColumnDefinition.ofInt("intCol"), + ColumnDefinition.ofLong("longCol"), + ColumnDefinition.ofFloat("floatCol"), + ColumnDefinition.ofDouble("doubleCol"), + ColumnDefinition.ofString("stringCol"), + ColumnDefinition.fromGenericType("instantCol", Instant.class), + ColumnDefinition.fromGenericType("ldtCol", LocalDateTime.class), + ColumnDefinition.fromGenericType("zdtCol", ZonedDateTime.class)); + + final String[] columnNames = expectedDefinition.getColumnNamesArray(); + + final String[] updateStrings = new String[] { + "byteCol=(byte)i", + "charCol=(char)(i + 64)", + "shortCol=(short)i", + "intCol=i", + "longCol=ii", + "floatCol=(float)ii", + "doubleCol=(double)ii", + "stringCol=String.valueOf(i)", + "instantCol=baseInstant.plusSeconds(i)", + "ldtCol=baseLDT.plusSeconds(i)", + "zdtCol=baseZDT.plusSeconds(i)", + }; + + // NOTE: boolean is not supported by RollingMinMaxSpec.applicableTo() + final Table source = TableTools.emptyTable(20).update(updateStrings); + + // Verify all the source columns are the expected types. + source.getDefinition().checkCompatibility(expectedDefinition); + + final Table expected = source.updateBy(UpdateByOperation.RollingMax(5, columnNames)); + + // Verify all the result columns are the expected types. + expected.getDefinition().checkCompatibility(expectedDefinition); + } + + @Test + public void testProxy() { + final QueryTable t = createTestTable(STATIC_TABLE_SIZE, true, false, false, 0x31313131, + new String[] {"charCol"}, + new TestDataGenerator[] {new CharGenerator('A', 'z', 0.1)}).t; + + final int prevTicks = 100; + final int postTicks = 0; + + Table actual; + Table expected; + + PartitionedTable pt = t.partitionBy("Sym"); + actual = pt.proxy() + .updateBy(UpdateByOperation.RollingMin(prevTicks, postTicks)) + .target().merge().sort("Sym"); + expected = t.sort("Sym").updateBy(UpdateByOperation.RollingMin(prevTicks, postTicks), "Sym"); + TstUtils.assertTableEquals(expected, actual); + + actual = pt.proxy() + .updateBy(UpdateByOperation.RollingMax(prevTicks, postTicks)) + .target().merge().sort("Sym"); + expected = t.sort("Sym").updateBy(UpdateByOperation.RollingMax(prevTicks, postTicks), "Sym"); + TstUtils.assertTableEquals(expected, actual); + } } diff --git a/replication/static/src/main/java/io/deephaven/replicators/ReplicateUpdateBy.java b/replication/static/src/main/java/io/deephaven/replicators/ReplicateUpdateBy.java index 005a2752bd6..82bc752012d 100644 --- a/replication/static/src/main/java/io/deephaven/replicators/ReplicateUpdateBy.java +++ b/replication/static/src/main/java/io/deephaven/replicators/ReplicateUpdateBy.java @@ -87,9 +87,10 @@ public static void main(String[] args) throws IOException { ReplicatePrimitiveCode.floatToAllFloatingPoints(TASK, "engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/sum/FloatCumSumOperator.java"); - files = ReplicatePrimitiveCode.shortToAllNumericals(TASK, - "engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/ShortCumMinMaxOperator.java", - null); + + files = ReplicatePrimitiveCode.charToAllButBoolean(TASK, + "engine/table/src/main/java/io/deephaven/engine/table/impl/updateby/minmax/CharCumMinMaxOperator.java", + exemptions); for (final String f : files) { if (f.contains("Integer")) { fixupInteger(f); @@ -142,6 +143,9 @@ public static void main(String[] args) throws IOException { } else if (f.contains("Float") || f.contains("Double")) { fixupFloatDoubleMinMax(f); } + if (f.contains("Long")) { + augmentLongWithReinterps(f); + } } files = ReplicatePrimitiveCode.charToIntegers(TASK, diff --git a/table-api/src/main/java/io/deephaven/api/updateby/spec/CumMinMaxSpec.java b/table-api/src/main/java/io/deephaven/api/updateby/spec/CumMinMaxSpec.java index c4c0e691101..8fabdb4f572 100644 --- a/table-api/src/main/java/io/deephaven/api/updateby/spec/CumMinMaxSpec.java +++ b/table-api/src/main/java/io/deephaven/api/updateby/spec/CumMinMaxSpec.java @@ -25,7 +25,7 @@ public final boolean applicableTo(Class inputType) { return // is primitive or boxed numeric? applicableToNumeric(inputType) - + || inputType == char.class || inputType == Character.class // is comparable? || (Comparable.class.isAssignableFrom(inputType) && inputType != Boolean.class); }