Skip to content

Commit c659e78

Browse files
dfa1claude
andcommitted
test(compute): pin the dict-double filter aggregate lane
Review follow-up: the aggregate lane's double-domain pool lowering (DictDoubleArray shapeOf/matchTable) was only covered transitively via the sum lane — every dict filter the aggregate oracle built was a DictLongArray. One pinned case drives the lane through a double dict filter directly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 1299dbe commit c659e78

1 file changed

Lines changed: 31 additions & 0 deletions

File tree

reader/src/test/java/io/github/dfa1/vortex/reader/ComputeFilteredAggregateTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.github.dfa1.vortex.reader.array.BoolArray;
77
import io.github.dfa1.vortex.reader.array.ByteArray;
88
import io.github.dfa1.vortex.reader.array.ChunkedByteArray;
9+
import io.github.dfa1.vortex.reader.array.DictDoubleArray;
910
import io.github.dfa1.vortex.reader.array.DictLongArray;
1011
import io.github.dfa1.vortex.reader.array.MaskedArray;
1112
import io.github.dfa1.vortex.reader.array.MaterializedBoolArray;
@@ -303,6 +304,26 @@ void dictFilterFloatAggregateBoxesFloatExtremes() {
303304
assertThat(result.max()).isInstanceOf(Float.class).isEqualTo(4.0f);
304305
}
305306

307+
@Test
308+
void dictDoubleFilterDrivesTheLane() {
309+
// Given a double-valued dict filter (a DictDoubleArray pool) — the aggregate lane's
310+
// double-domain pool lowering, which the long-dict randomized sweep never reaches
311+
Array filter = dictDoubleColumn(new double[]{1.5, 2.5}, new int[]{1, 0, 1, 1});
312+
Chunk chunk = chunk(4, Map.of(
313+
"f0", filter,
314+
"v", longArray(new Reference(new long[]{1, 2, 4, 8}, null), false)));
315+
316+
// When the kernel folds over the rows where the dict value is > 2.0 (code 1: rows 0, 2, 3)
317+
FilteredAggregate result = Compute.filteredAggregate(chunk, RowFilter.gt("f0", 2.0), "v");
318+
319+
// Then the selected rows fold exactly as the value-level evaluation would
320+
assertThat(result.selectedRows()).isEqualTo(3L);
321+
assertThat(result.aggNonNullCount()).isEqualTo(3L);
322+
assertThat(result.sum()).isEqualTo(1L + 4L + 8L);
323+
assertThat(result.min()).isEqualTo(1L);
324+
assertThat(result.max()).isEqualTo(8L);
325+
}
326+
306327
@Test
307328
void andOfOneDictLeafUnwrapsToTheLane() {
308329
// Given a one-element AND around a dict comparison leaf — RowFilter.and(...) wraps a single
@@ -495,6 +516,16 @@ private static Array dictColumn(long[] pool, int[] codes) {
495516
return DictLongArray.of(DType.I64, codes.length, poolArray, byteArray(codes, 0, codes.length));
496517
}
497518

519+
/// Builds a flat-coded double-valued dict column for the double-domain dict-lane case.
520+
private static Array dictDoubleColumn(double[] pool, int[] codes) {
521+
MemorySegment poolSeg = ARENA.allocate(Math.max(8L, pool.length * 8L), 8);
522+
for (int i = 0; i < pool.length; i++) {
523+
poolSeg.setAtIndex(PTypeIO.LE_DOUBLE, i, pool[i]);
524+
}
525+
MaterializedDoubleArray poolArray = new MaterializedDoubleArray(DType.F64, pool.length, poolSeg);
526+
return DictDoubleArray.of(DType.F64, codes.length, poolArray, byteArray(codes, 0, codes.length));
527+
}
528+
498529
private static MaterializedByteArray byteArray(int[] codes, int from, int to) {
499530
MemorySegment segment = ARENA.allocate(Math.max(1L, (long) to - from));
500531
for (int i = from; i < to; i++) {

0 commit comments

Comments
 (0)