@@ -18,6 +18,10 @@ class LazyRleArrayTest {
1818
1919 private static final DType I64 = new DType .Primitive (PType .I64 , false );
2020 private static final DType I32 = new DType .Primitive (PType .I32 , false );
21+ private static final DType U8 = new DType .Primitive (PType .U8 , false );
22+ private static final DType I8 = new DType .Primitive (PType .I8 , false );
23+ private static final DType U16 = new DType .Primitive (PType .U16 , false );
24+ private static final DType I16 = new DType .Primitive (PType .I16 , false );
2125
2226 @ Nested
2327 class LongDispatch {
@@ -99,6 +103,54 @@ void foldSumsCorrectly() {
99103 // 1024 * 7 + 2 * 11 = 7168 + 22 = 7190
100104 assertThat (sum ).isEqualTo (7190L );
101105 }
106+
107+ @ Test
108+ void getLongLookupIndexedClampAndEmpty () {
109+ // Given — indexed chunk; index[2] out of range clamps to last value
110+ int [] idx = new int [1024 ];
111+ idx [0 ] = 0 ;
112+ idx [1 ] = 1 ;
113+ idx [2 ] = 9 ;
114+ var x = new LazyRleLongArray (I64 , 3 , new long []{10L , 20L , 30L }, idx ,
115+ new long []{0L }, 0L , 3L , 1 , 0 );
116+ assertThat (x .getLong (0 )).isEqualTo (10L );
117+ assertThat (x .getLong (1 )).isEqualTo (20L );
118+ assertThat (x .getLong (2 )).isEqualTo (30L ); // clamped
119+
120+ // empty chunk → zero
121+ var e = new LazyRleLongArray (I64 , 2 , new long [0 ], new int [1024 ],
122+ new long []{0L }, 0L , 0L , 1 , 0 );
123+ assertThat (e .getLong (0 )).isZero ();
124+ }
125+
126+ @ Test
127+ void foldStopsWhenLengthSatisfiedBeforeChunksExhausted () {
128+ // Given — 2 chunks declared but length only spans 5 rows of chunk 0;
129+ // the fold loop must exit on `emitted < n`, not on chunk exhaustion
130+ var sut = new LazyRleLongArray (I64 , 5 , new long []{1L , 2L }, new int [2048 ],
131+ new long []{0L , 1L }, 0L , 2L , 2 , 0 );
132+ assertThat (sut .fold (0L , java .lang .Long ::sum )).isEqualTo (5L );
133+ }
134+
135+ @ Test
136+ void forEachIndexedClampAndEmptyChunk () {
137+ // indexed forEach with out-of-range index → clamps to last value
138+ int [] idx = new int [1024 ];
139+ idx [0 ] = 0 ;
140+ idx [1 ] = 9 ;
141+ var x = new LazyRleLongArray (I64 , 2 , new long []{10L , 20L }, idx ,
142+ new long []{0L }, 0L , 2L , 1 , 0 );
143+ var seen = new ArrayList <java .lang .Long >();
144+ x .forEachLong (seen ::add );
145+ assertThat (seen ).containsExactly (10L , 20L );
146+
147+ // empty chunk via forEach → zeros
148+ var e = new LazyRleLongArray (I64 , 2 , new long [0 ], new int [1024 ],
149+ new long []{0L }, 0L , 0L , 1 , 0 );
150+ var zeros = new ArrayList <java .lang .Long >();
151+ e .forEachLong (zeros ::add );
152+ assertThat (zeros ).containsExactly (0L , 0L );
153+ }
102154 }
103155
104156 @ Nested
@@ -119,5 +171,162 @@ void intLookupAndForEach() {
119171 assertThat (sut .getInt (1 )).isEqualTo (200 );
120172 assertThat (sut .getInt (2 )).isEqualTo (100 );
121173 }
174+
175+ @ Test
176+ void forEachFoldClampAndEmptyChunk () {
177+ // Given — values [1,2,3]; index[3] out of range → clamps to last (3)
178+ int [] indices = new int [1024 ];
179+ indices [0 ] = 0 ;
180+ indices [1 ] = 1 ;
181+ indices [2 ] = 2 ;
182+ indices [3 ] = 99 ;
183+ var sut = new LazyRleIntArray (I32 , 4 , new int []{1 , 2 , 3 }, indices ,
184+ new long []{0L }, 0L , 3L , 1 , 0 );
185+
186+ assertThat (sut .getInt (0 )).isEqualTo (1 );
187+ assertThat (sut .getInt (3 )).isEqualTo (3 ); // getInt lookup clamp path
188+ var seen = new ArrayList <java .lang .Integer >();
189+ sut .forEachInt (seen ::add );
190+ assertThat (seen ).containsExactly (1 , 2 , 3 , 3 );
191+ assertThat (sut .fold (0 , Integer ::sum )).isEqualTo (9 );
192+
193+ // empty chunk (0 distinct values) → zero, via getInt, forEach and fold
194+ var empty = new LazyRleIntArray (I32 , 2 , new int [0 ], new int [1024 ],
195+ new long []{0L }, 0L , 0L , 1 , 0 );
196+ assertThat (empty .getInt (0 )).isZero ();
197+ var zeros = new ArrayList <java .lang .Integer >();
198+ empty .forEachInt (zeros ::add );
199+ assertThat (zeros ).containsExactly (0 , 0 );
200+ assertThat (empty .fold (0 , Integer ::sum )).isZero ();
201+ }
202+
203+ @ Test
204+ void getIntConstantRunFastPath () {
205+ // single distinct value → getInt hits the numChunkValues == 1 lookup branch
206+ var sut = new LazyRleIntArray (I32 , 3 , new int []{42 }, new int [1024 ],
207+ new long []{0L }, 0L , 1L , 1 , 0 );
208+ assertThat (sut .getInt (0 )).isEqualTo (42 );
209+ assertThat (sut .getInt (2 )).isEqualTo (42 );
210+ }
211+
212+ @ Test
213+ void foldStopsWhenLengthSatisfiedBeforeChunksExhausted () {
214+ // 2 chunks declared, length spans only 5 rows of chunk 0 → loop exits on emitted < n
215+ var sut = new LazyRleIntArray (I32 , 5 , new int []{1 , 2 }, new int [2048 ],
216+ new long []{0L , 1L }, 0L , 2L , 2 , 0 );
217+ assertThat (sut .fold (0 , Integer ::sum )).isEqualTo (5 );
218+ }
219+ }
220+
221+ @ Nested
222+ class ByteDispatch {
223+
224+ @ Test
225+ void lookupConstantEmptyIndexedClamp () {
226+ // constant run (1 distinct)
227+ var c = new LazyRleByteArray (U8 , 3 , new byte []{7 }, new int [1024 ],
228+ new long []{0L }, 0L , 1L , 1 , 0 , true );
229+ assertThat (c .getByte (0 )).isEqualTo ((byte ) 7 );
230+ assertThat (c .fold (0L , Long ::sum )).isEqualTo (21L );
231+
232+ // empty chunk (0 distinct) → zero
233+ var e = new LazyRleByteArray (U8 , 2 , new byte [0 ], new int [1024 ],
234+ new long []{0L }, 0L , 0L , 1 , 0 , true );
235+ assertThat (e .getByte (0 )).isZero ();
236+ assertThat (e .fold (0L , Long ::sum )).isZero ();
237+
238+ // indexed with out-of-range index → clamps to last value
239+ int [] idx = new int [1024 ];
240+ idx [0 ] = 0 ;
241+ idx [1 ] = 1 ;
242+ idx [2 ] = 2 ;
243+ idx [3 ] = 99 ;
244+ var x = new LazyRleByteArray (U8 , 4 , new byte []{1 , 2 , 3 }, idx ,
245+ new long []{0L }, 0L , 3L , 1 , 0 , true );
246+ assertThat (x .getByte (0 )).isEqualTo ((byte ) 1 );
247+ assertThat (x .getByte (3 )).isEqualTo ((byte ) 3 ); // clamped
248+ assertThat (x .fold (0L , Long ::sum )).isEqualTo (9L );
249+ }
250+
251+ @ Test
252+ void getIntWidensSignedAndUnsigned () {
253+ int [] idx = new int [1024 ];
254+ var u = new LazyRleByteArray (U8 , 1 , new byte []{(byte ) 0xF0 }, idx ,
255+ new long []{0L }, 0L , 1L , 1 , 0 , true );
256+ assertThat (u .getInt (0 )).isEqualTo (240 );
257+ var s = new LazyRleByteArray (I8 , 1 , new byte []{(byte ) 0xF0 }, idx ,
258+ new long []{0L }, 0L , 1L , 1 , 0 , false );
259+ assertThat (s .getInt (0 )).isEqualTo (-16 );
260+ }
261+
262+ @ Test
263+ void multiChunkFoldSignedWiden () {
264+ // two constant chunks: 1024×1 + 2×2; signed widening path in foldChunk
265+ var sut = new LazyRleByteArray (I8 , 1026 , new byte []{1 , 2 }, new int [2048 ],
266+ new long []{0L , 1L }, 0L , 2L , 2 , 0 , false );
267+ assertThat (sut .getByte (1024 )).isEqualTo ((byte ) 2 );
268+ assertThat (sut .fold (0L , Long ::sum )).isEqualTo (1024L + 4L );
269+ }
270+
271+ @ Test
272+ void foldStopsWhenLengthSatisfiedBeforeChunksExhausted () {
273+ var sut = new LazyRleByteArray (U8 , 5 , new byte []{1 , 2 }, new int [2048 ],
274+ new long []{0L , 1L }, 0L , 2L , 2 , 0 , true );
275+ assertThat (sut .fold (0L , Long ::sum )).isEqualTo (5L );
276+ }
277+ }
278+
279+ @ Nested
280+ class ShortDispatch {
281+
282+ @ Test
283+ void lookupConstantEmptyIndexedClamp () {
284+ var c = new LazyRleShortArray (U16 , 3 , new short []{7 }, new int [1024 ],
285+ new long []{0L }, 0L , 1L , 1 , 0 , true );
286+ assertThat (c .getShort (0 )).isEqualTo ((short ) 7 );
287+ assertThat (c .fold (0L , Long ::sum )).isEqualTo (21L );
288+
289+ var e = new LazyRleShortArray (U16 , 2 , new short [0 ], new int [1024 ],
290+ new long []{0L }, 0L , 0L , 1 , 0 , true );
291+ assertThat (e .getShort (0 )).isZero ();
292+ assertThat (e .fold (0L , Long ::sum )).isZero ();
293+
294+ int [] idx = new int [1024 ];
295+ idx [0 ] = 0 ;
296+ idx [1 ] = 1 ;
297+ idx [2 ] = 2 ;
298+ idx [3 ] = 99 ;
299+ var x = new LazyRleShortArray (U16 , 4 , new short []{1 , 2 , 3 }, idx ,
300+ new long []{0L }, 0L , 3L , 1 , 0 , true );
301+ assertThat (x .getShort (0 )).isEqualTo ((short ) 1 );
302+ assertThat (x .getShort (3 )).isEqualTo ((short ) 3 ); // clamped
303+ assertThat (x .fold (0L , Long ::sum )).isEqualTo (9L );
304+ }
305+
306+ @ Test
307+ void getIntWidensSignedAndUnsigned () {
308+ int [] idx = new int [1024 ];
309+ var u = new LazyRleShortArray (U16 , 1 , new short []{(short ) 0xFF00 }, idx ,
310+ new long []{0L }, 0L , 1L , 1 , 0 , true );
311+ assertThat (u .getInt (0 )).isEqualTo (0xFF00 );
312+ var s = new LazyRleShortArray (I16 , 1 , new short []{(short ) 0xFF00 }, idx ,
313+ new long []{0L }, 0L , 1L , 1 , 0 , false );
314+ assertThat (s .getInt (0 )).isEqualTo ((int ) (short ) 0xFF00 );
315+ }
316+
317+ @ Test
318+ void multiChunkFoldSignedWiden () {
319+ var sut = new LazyRleShortArray (I16 , 1026 , new short []{1 , 2 }, new int [2048 ],
320+ new long []{0L , 1L }, 0L , 2L , 2 , 0 , false );
321+ assertThat (sut .getShort (1024 )).isEqualTo ((short ) 2 );
322+ assertThat (sut .fold (0L , Long ::sum )).isEqualTo (1024L + 4L );
323+ }
324+
325+ @ Test
326+ void foldStopsWhenLengthSatisfiedBeforeChunksExhausted () {
327+ var sut = new LazyRleShortArray (U16 , 5 , new short []{1 , 2 }, new int [2048 ],
328+ new long []{0L , 1L }, 0L , 2L , 2 , 0 , true );
329+ assertThat (sut .fold (0L , Long ::sum )).isEqualTo (5L );
330+ }
122331 }
123332}
0 commit comments