1use ::alp::ENCODE_CHUNK_SIZE;
5use itertools::Itertools;
6use vortex_array::ArrayRef;
7use vortex_array::ArrayView;
8use vortex_array::ExecutionCtx;
9use vortex_array::IntoArray;
10use vortex_array::arrays::Primitive;
11use vortex_array::arrays::PrimitiveArray;
12use vortex_array::dtype::NativePType;
13use vortex_array::dtype::PType;
14use vortex_array::patches::Patches;
15use vortex_array::validity::Validity;
16use vortex_buffer::Buffer;
17use vortex_buffer::BufferMut;
18use vortex_error::VortexResult;
19use vortex_error::vortex_bail;
20use vortex_mask::Mask;
21
22use crate::ALP;
23use crate::Exponents;
24use crate::alp::ALPArray;
25use crate::alp::ALPFloat;
26
27#[macro_export]
28macro_rules! match_each_alp_float_ptype {
29 ($self:expr, | $enc:ident | $body:block) => {{
30 use vortex_array::dtype::PType;
31 use vortex_error::vortex_panic;
32 let ptype = $self;
33 match ptype {
34 PType::F32 => {
35 type $enc = f32;
36 $body
37 }
38 PType::F64 => {
39 type $enc = f64;
40 $body
41 }
42 _ => vortex_panic!("ALP can only encode f32 and f64, got {}", ptype),
43 }
44 }};
45}
46
47pub fn alp_encode(
48 parray: ArrayView<'_, Primitive>,
49 exponents: Option<Exponents>,
50 ctx: &mut ExecutionCtx,
51) -> VortexResult<ALPArray> {
52 let (exponents, encoded, patches) = match parray.ptype() {
53 PType::F32 => alp_encode_components_typed::<f32>(parray, exponents, ctx)?,
54 PType::F64 => alp_encode_components_typed::<f64>(parray, exponents, ctx)?,
55 _ => vortex_bail!("ALP can only encode f32 and f64"),
56 };
57
58 unsafe { Ok(ALP::new_unchecked(encoded, exponents, patches)) }
60}
61
62#[expect(
63 clippy::cast_possible_truncation,
64 reason = "u64 index cast to usize is safe for reasonable array sizes"
65)]
66fn alp_encode_components_typed<T>(
67 values: ArrayView<'_, Primitive>,
68 exponents: Option<Exponents>,
69 ctx: &mut ExecutionCtx,
70) -> VortexResult<(Exponents, ArrayRef, Option<Patches>)>
71where
72 T: ALPFloat + NativePType,
73 T::ALPInt: NativePType,
74{
75 let values_slice = values.as_slice::<T>();
76
77 let mut encoded = BufferMut::<T::ALPInt>::with_capacity(values_slice.len());
80
81 let mut exceptional_positions = Vec::with_capacity(values_slice.len() / 32);
83 let mut exceptional_values = Vec::with_capacity(values_slice.len() / 32);
84
85 let mut chunk_offsets = Vec::with_capacity(values_slice.len().div_ceil(ENCODE_CHUNK_SIZE));
87 let exponents = ::alp::encode_into(
88 values_slice,
89 exponents,
90 &mut encoded.spare_capacity_mut()[..values_slice.len()],
91 &mut exceptional_positions,
92 &mut exceptional_values,
93 &mut chunk_offsets,
94 );
95 unsafe { encoded.set_len(values_slice.len()) };
97
98 let encoded_array = PrimitiveArray::new(encoded.freeze(), values.validity()?).into_array();
99
100 let validity = values
101 .array()
102 .validity()?
103 .execute_mask(values.array().len(), ctx)?;
104 let (valid_exceptional_positions, valid_exceptional_values): (Vec<u64>, Vec<T>) = match validity
107 {
108 Mask::AllTrue(_) => (exceptional_positions, exceptional_values),
109 Mask::AllFalse(_) => {
110 (Vec::new(), Vec::new())
112 }
113 Mask::Values(is_valid) => exceptional_positions
114 .into_iter()
115 .zip_eq(exceptional_values)
116 .filter(|(index, _)| {
117 let is_valid = is_valid.value(*index as usize);
118 if !is_valid {
119 let patch_chunk = *index as usize / ENCODE_CHUNK_SIZE;
120 for chunk_idx in (patch_chunk + 1)..chunk_offsets.len() {
121 chunk_offsets[chunk_idx] -= 1;
122 }
123 }
124 is_valid
125 })
126 .unzip(),
127 };
128 let patches = if valid_exceptional_positions.is_empty() {
129 None
130 } else {
131 let patches_validity = if values.dtype().is_nullable() {
132 Validity::AllValid
133 } else {
134 Validity::NonNullable
135 };
136 let valid_exceptional_values =
137 PrimitiveArray::new(Buffer::from(valid_exceptional_values), patches_validity)
138 .into_array();
139
140 Some(Patches::new(
141 values_slice.len(),
142 0,
143 Buffer::from(valid_exceptional_positions).into_array(),
144 valid_exceptional_values,
145 Some(Buffer::from(chunk_offsets).into_array()),
146 )?)
147 };
148 Ok((exponents, encoded_array, patches))
149}
150
151#[cfg(test)]
152mod tests {
153 use core::f32;
154 use core::f64;
155 use std::sync::LazyLock;
156
157 use f64::consts::E;
158 use f64::consts::PI;
159 use vortex_array::VortexSessionExecute;
160 use vortex_array::assert_arrays_eq;
161 use vortex_array::dtype::NativePType;
162 use vortex_array::validity::Validity;
163 use vortex_buffer::Buffer;
164 use vortex_buffer::buffer;
165 use vortex_session::VortexSession;
166
167 use super::*;
168 use crate::alp::array::ALPArrayExt;
169 use crate::alp::array::ALPArraySlotsExt;
170 use crate::decompress_into_array;
171
172 static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
173 let session = vortex_array::array_session();
174 crate::initialize(&session);
175 session
176 });
177
178 #[test]
179 fn test_compress() {
180 let array = PrimitiveArray::new(buffer![1.234f32; 1025], Validity::NonNullable);
181 let encoded =
182 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
183 assert!(encoded.patches().is_none());
184 let expected_encoded = PrimitiveArray::from_iter(vec![1234i32; 1025]);
185 assert_arrays_eq!(
186 encoded.encoded(),
187 expected_encoded,
188 &mut SESSION.create_execution_ctx()
189 );
190 assert_eq!(encoded.exponents(), Exponents { e: 9, f: 6 });
191
192 let decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
193 assert_arrays_eq!(decoded, array, &mut SESSION.create_execution_ctx());
194 }
195
196 #[test]
197 fn test_nullable_compress() {
198 let array = PrimitiveArray::from_option_iter([None, Some(1.234f32), None]);
199 let encoded =
200 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
201 assert!(encoded.patches().is_none());
202 let expected_encoded = PrimitiveArray::from_option_iter([None, Some(1234i32), None]);
203 assert_arrays_eq!(
204 encoded.encoded(),
205 expected_encoded,
206 &mut SESSION.create_execution_ctx()
207 );
208 assert_eq!(encoded.exponents(), Exponents { e: 9, f: 6 });
209
210 let decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
211 let expected = PrimitiveArray::from_option_iter(vec![None, Some(1.234f32), None]);
212 assert_arrays_eq!(decoded, expected, &mut SESSION.create_execution_ctx());
213 }
214
215 #[test]
216 #[expect(clippy::approx_constant)] fn test_patched_compress() {
218 let values = buffer![1.234f64, 2.718, PI, 4.0];
219 let array = PrimitiveArray::new(values.clone(), Validity::NonNullable);
220 let encoded =
221 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
222 assert!(encoded.patches().is_some());
223 let expected_encoded = PrimitiveArray::from_iter(vec![1234i64, 2718, 1234, 4000]);
224 assert_arrays_eq!(
225 encoded.encoded(),
226 expected_encoded,
227 &mut SESSION.create_execution_ctx()
228 );
229 assert_eq!(encoded.exponents(), Exponents { e: 16, f: 13 });
230
231 let decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
232 let expected_decoded = PrimitiveArray::new(values, Validity::NonNullable);
233 assert_arrays_eq!(
234 decoded,
235 expected_decoded,
236 &mut SESSION.create_execution_ctx()
237 );
238 }
239
240 #[test]
241 #[expect(clippy::approx_constant)] fn test_compress_ignores_invalid_exceptional_values() {
243 let values = buffer![1.234f64, 2.718, PI, 4.0];
244 let array = PrimitiveArray::new(values, Validity::from_iter([true, true, false, true]));
245 let encoded =
246 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
247 assert!(encoded.patches().is_none());
248 let expected_encoded =
249 PrimitiveArray::from_option_iter(buffer![Some(1234i64), Some(2718), None, Some(4000)]);
250 assert_arrays_eq!(
251 encoded.encoded(),
252 expected_encoded,
253 &mut SESSION.create_execution_ctx()
254 );
255 assert_eq!(encoded.exponents(), Exponents { e: 16, f: 13 });
256
257 let decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
258 assert_arrays_eq!(decoded, array, &mut SESSION.create_execution_ctx());
259 }
260
261 #[test]
262 #[expect(clippy::approx_constant)] fn test_nullable_patched_scalar_at() {
264 let array = PrimitiveArray::from_option_iter([
265 Some(1.234f64),
266 Some(2.718),
267 Some(PI),
268 Some(4.0),
269 None,
270 ]);
271 let encoded =
272 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
273 assert!(encoded.patches().is_some());
274
275 assert_eq!(encoded.exponents(), Exponents { e: 16, f: 13 });
276
277 assert_arrays_eq!(encoded, array, &mut SESSION.create_execution_ctx());
278
279 let _decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
280 }
281
282 #[test]
283 fn roundtrips_close_fractional() {
284 let original = PrimitiveArray::from_iter([195.26274f32, 195.27837, -48.815685]);
285 let alp_arr = alp_encode(
286 original.as_view(),
287 None,
288 &mut SESSION.create_execution_ctx(),
289 )
290 .unwrap();
291 assert_arrays_eq!(alp_arr, original, &mut SESSION.create_execution_ctx());
292 }
293
294 #[test]
295 fn roundtrips_all_null() {
296 let mut ctx = SESSION.create_execution_ctx();
297 let original =
298 PrimitiveArray::new(buffer![195.26274f64, PI, -48.815685], Validity::AllInvalid);
299 let alp_arr = alp_encode(original.as_view(), None, &mut ctx).unwrap();
300 let decompressed = alp_arr
301 .into_array()
302 .execute::<PrimitiveArray>(&mut ctx)
303 .unwrap();
304
305 assert_eq!(
306 [195.26274, 195.26274, 195.26274],
308 decompressed.as_slice::<f64>()
309 );
310
311 assert_arrays_eq!(decompressed, original, &mut ctx);
312 }
313
314 #[test]
315 fn non_finite_numbers() {
316 let mut ctx = SESSION.create_execution_ctx();
317 let original = PrimitiveArray::new(
318 buffer![0.0f32, -0.0, f32::NAN, f32::NEG_INFINITY, f32::INFINITY],
319 Validity::NonNullable,
320 );
321 let encoded = alp_encode(original.as_view(), None, &mut ctx).unwrap();
322 let decoded = encoded
323 .as_array()
324 .clone()
325 .execute::<PrimitiveArray>(&mut ctx)
326 .unwrap();
327 for idx in 0..original.len() {
328 let decoded_val = decoded.as_slice::<f32>()[idx];
329 let original_val = original.as_slice::<f32>()[idx];
330 assert!(
331 NativePType::is_eq(decoded_val, original_val),
332 "Expected {original_val} but got {decoded_val}"
333 );
334 }
335 }
336
337 #[test]
338 fn test_chunk_offsets() {
339 let mut ctx = SESSION.create_execution_ctx();
340 let mut values = vec![1.0f64; 3072];
341
342 values[1023] = PI;
343 values[1024] = E;
344 values[1025] = PI;
345
346 let array = PrimitiveArray::new(Buffer::from(values), Validity::NonNullable);
347 let encoded = alp_encode(array.as_view(), None, &mut ctx).unwrap();
348 let patches = encoded.patches().unwrap();
349
350 let chunk_offsets = patches
351 .chunk_offsets()
352 .clone()
353 .unwrap()
354 .execute::<PrimitiveArray>(&mut ctx)
355 .unwrap();
356 let expected_offsets = PrimitiveArray::from_iter(vec![0u64, 1, 3]);
357 assert_arrays_eq!(chunk_offsets, expected_offsets, &mut ctx);
358
359 let patch_indices = patches
360 .indices()
361 .clone()
362 .execute::<PrimitiveArray>(&mut ctx)
363 .unwrap();
364 let expected_indices = PrimitiveArray::from_iter(vec![1023u64, 1024, 1025]);
365 assert_arrays_eq!(patch_indices, expected_indices, &mut ctx);
366
367 let patch_values = patches
368 .values()
369 .clone()
370 .execute::<PrimitiveArray>(&mut ctx)
371 .unwrap();
372 let expected_values = PrimitiveArray::from_iter(vec![PI, E, PI]);
373 assert_arrays_eq!(patch_values, expected_values, &mut ctx);
374 }
375
376 #[test]
377 fn test_chunk_offsets_no_patches_in_middle() {
378 let mut ctx = SESSION.create_execution_ctx();
379 let mut values = vec![1.0f64; 3072];
380 values[0] = PI;
381 values[2048] = E;
382
383 let array = PrimitiveArray::new(Buffer::from(values), Validity::NonNullable);
384 let encoded = alp_encode(array.as_view(), None, &mut ctx).unwrap();
385 let patches = encoded.patches().unwrap();
386
387 let chunk_offsets = patches
388 .chunk_offsets()
389 .clone()
390 .unwrap()
391 .execute::<PrimitiveArray>(&mut ctx)
392 .unwrap();
393 let expected_offsets = PrimitiveArray::from_iter(vec![0u64, 1, 1]);
394 assert_arrays_eq!(chunk_offsets, expected_offsets, &mut ctx);
395
396 let patch_indices = patches
397 .indices()
398 .clone()
399 .execute::<PrimitiveArray>(&mut ctx)
400 .unwrap();
401 let expected_indices = PrimitiveArray::from_iter(vec![0u64, 2048]);
402 assert_arrays_eq!(patch_indices, expected_indices, &mut ctx);
403
404 let patch_values = patches
405 .values()
406 .clone()
407 .execute::<PrimitiveArray>(&mut ctx)
408 .unwrap();
409 let expected_values = PrimitiveArray::from_iter(vec![PI, E]);
410 assert_arrays_eq!(patch_values, expected_values, &mut ctx);
411 }
412
413 #[test]
414 fn test_chunk_offsets_trailing_empty_chunks() {
415 let mut ctx = SESSION.create_execution_ctx();
416 let mut values = vec![1.0f64; 3072];
417 values[0] = PI;
418
419 let array = PrimitiveArray::new(Buffer::from(values), Validity::NonNullable);
420 let encoded = alp_encode(array.as_view(), None, &mut ctx).unwrap();
421 let patches = encoded.patches().unwrap();
422
423 let chunk_offsets = patches
424 .chunk_offsets()
425 .clone()
426 .unwrap()
427 .execute::<PrimitiveArray>(&mut ctx)
428 .unwrap();
429 let expected_offsets = PrimitiveArray::from_iter(vec![0u64, 1, 1]);
430 assert_arrays_eq!(chunk_offsets, expected_offsets, &mut ctx);
431
432 let patch_indices = patches
433 .indices()
434 .clone()
435 .execute::<PrimitiveArray>(&mut ctx)
436 .unwrap();
437 let expected_indices = PrimitiveArray::from_iter(vec![0u64]);
438 assert_arrays_eq!(patch_indices, expected_indices, &mut ctx);
439
440 let patch_values = patches
441 .values()
442 .clone()
443 .execute::<PrimitiveArray>(&mut ctx)
444 .unwrap();
445 let expected_values = PrimitiveArray::from_iter(vec![PI]);
446 assert_arrays_eq!(patch_values, expected_values, &mut ctx);
447 }
448
449 #[test]
450 fn test_chunk_offsets_single_chunk() {
451 let mut ctx = SESSION.create_execution_ctx();
452 let mut values = vec![1.0f64; 512];
453 values[0] = PI;
454 values[100] = E;
455
456 let array = PrimitiveArray::new(Buffer::from(values), Validity::NonNullable);
457 let encoded = alp_encode(array.as_view(), None, &mut ctx).unwrap();
458 let patches = encoded.patches().unwrap();
459
460 let chunk_offsets = patches
461 .chunk_offsets()
462 .clone()
463 .unwrap()
464 .execute::<PrimitiveArray>(&mut ctx)
465 .unwrap();
466 let expected_offsets = PrimitiveArray::from_iter(vec![0u64]);
467 assert_arrays_eq!(chunk_offsets, expected_offsets, &mut ctx);
468
469 let patch_indices = patches
470 .indices()
471 .clone()
472 .execute::<PrimitiveArray>(&mut ctx)
473 .unwrap();
474 let expected_indices = PrimitiveArray::from_iter(vec![0u64, 100]);
475 assert_arrays_eq!(patch_indices, expected_indices, &mut ctx);
476
477 let patch_values = patches
478 .values()
479 .clone()
480 .execute::<PrimitiveArray>(&mut ctx)
481 .unwrap();
482 let expected_values = PrimitiveArray::from_iter(vec![PI, E]);
483 assert_arrays_eq!(patch_values, expected_values, &mut ctx);
484 }
485
486 #[test]
487 fn test_slice_half_chunk_f32_roundtrip() {
488 let values = vec![1.234f32; 1024];
490 let original = PrimitiveArray::new(Buffer::from(values), Validity::NonNullable);
491 let encoded = alp_encode(
492 original.as_view(),
493 None,
494 &mut SESSION.create_execution_ctx(),
495 )
496 .unwrap();
497
498 let sliced_alp = encoded.slice(512..1024).unwrap();
499
500 let expected_slice = original.slice(512..1024).unwrap();
501 assert_arrays_eq!(
502 sliced_alp,
503 expected_slice,
504 &mut SESSION.create_execution_ctx()
505 );
506 }
507
508 #[test]
509 fn test_slice_half_chunk_f64_roundtrip() {
510 let values = vec![5.678f64; 1024];
511 let original = PrimitiveArray::new(Buffer::from(values), Validity::NonNullable);
512 let encoded = alp_encode(
513 original.as_view(),
514 None,
515 &mut SESSION.create_execution_ctx(),
516 )
517 .unwrap();
518
519 let sliced_alp = encoded.slice(512..1024).unwrap();
520
521 let expected_slice = original.slice(512..1024).unwrap();
522 assert_arrays_eq!(
523 sliced_alp,
524 expected_slice,
525 &mut SESSION.create_execution_ctx()
526 );
527 }
528
529 #[test]
530 fn test_slice_half_chunk_with_patches_roundtrip() {
531 let mut values = vec![1.0f64; 1024];
532 values[100] = PI;
533 values[200] = E;
534 values[600] = 42.42;
535
536 let original = PrimitiveArray::new(Buffer::from(values), Validity::NonNullable);
537 let encoded = alp_encode(
538 original.as_view(),
539 None,
540 &mut SESSION.create_execution_ctx(),
541 )
542 .unwrap();
543
544 let sliced_alp = encoded.slice(512..1024).unwrap();
545
546 let expected_slice = original.slice(512..1024).unwrap();
547 assert_arrays_eq!(
548 sliced_alp,
549 expected_slice,
550 &mut SESSION.create_execution_ctx()
551 );
552 assert!(encoded.patches().is_some());
553 }
554
555 #[test]
556 fn test_slice_across_chunks_with_patches_roundtrip() {
557 let mut values = vec![1.0f64; 2048];
558 values[100] = PI;
559 values[200] = E;
560 values[600] = 42.42;
561 values[800] = 42.42;
562 values[1000] = 42.42;
563 values[1023] = 42.42;
564
565 let original = PrimitiveArray::new(Buffer::from(values), Validity::NonNullable);
566 let encoded = alp_encode(
567 original.as_view(),
568 None,
569 &mut SESSION.create_execution_ctx(),
570 )
571 .unwrap();
572
573 let sliced_alp = encoded.slice(1023..1025).unwrap();
574
575 let expected_slice = original.slice(1023..1025).unwrap();
576 assert_arrays_eq!(
577 sliced_alp,
578 expected_slice,
579 &mut SESSION.create_execution_ctx()
580 );
581 assert!(encoded.patches().is_some());
582 }
583
584 #[test]
585 fn test_slice_half_chunk_nullable_roundtrip() {
586 let mut ctx = SESSION.create_execution_ctx();
587 let values = (0..1024)
588 .map(|i| if i % 3 == 0 { None } else { Some(2.5f32) })
589 .collect::<Vec<_>>();
590
591 let original = PrimitiveArray::from_option_iter(values);
592 let encoded = alp_encode(original.as_view(), None, &mut ctx).unwrap();
593
594 let sliced_alp = encoded.slice(512..1024).unwrap();
595 let decoded = sliced_alp.execute::<PrimitiveArray>(&mut ctx).unwrap();
596
597 let expected_slice = original.slice(512..1024).unwrap();
598 assert_arrays_eq!(decoded, expected_slice, &mut ctx);
599 }
600
601 #[test]
602 fn test_large_f32_array_uniform_values() {
603 let size = 10_000;
604 let array = PrimitiveArray::new(buffer![42.125f32; size], Validity::NonNullable);
605 let encoded =
606 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
607
608 assert!(encoded.patches().is_none());
609 let decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
610 assert_arrays_eq!(decoded, array, &mut SESSION.create_execution_ctx());
611 }
612
613 #[test]
614 fn test_large_f64_array_uniform_values() {
615 let size = 50_000;
616 let array = PrimitiveArray::new(buffer![123.456789f64; size], Validity::NonNullable);
617 let encoded =
618 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
619
620 assert!(encoded.patches().is_none());
621 let decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
622 assert_arrays_eq!(decoded, array, &mut SESSION.create_execution_ctx());
623 }
624
625 #[test]
626 fn test_large_f32_array_with_patches() {
627 let size = 5_000;
628 let mut values = vec![1.5f32; size];
629 values[100] = f32::consts::PI;
630 values[1500] = f32::consts::E;
631 values[3000] = f32::NEG_INFINITY;
632 values[4500] = f32::INFINITY;
633
634 let array = PrimitiveArray::new(Buffer::from(values), Validity::NonNullable);
635 let encoded =
636 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
637
638 assert!(encoded.patches().is_some());
639 let decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
640 assert_arrays_eq!(decoded, array, &mut SESSION.create_execution_ctx());
641 }
642
643 #[test]
644 fn test_large_f64_array_with_patches() {
645 let size = 8_000;
646 let mut values = vec![2.2184f64; size];
647 values[0] = PI;
648 values[1000] = E;
649 values[2000] = f64::NAN;
650 values[3000] = f64::INFINITY;
651 values[4000] = f64::NEG_INFINITY;
652 values[5000] = 0.0;
653 values[6000] = -0.0;
654 values[7000] = 999.999999999;
655
656 let array = PrimitiveArray::new(Buffer::from(values.clone()), Validity::NonNullable);
657 let encoded =
658 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
659
660 assert!(encoded.patches().is_some());
661 let decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
662
663 for idx in 0..size {
664 let decoded_val = decoded.as_slice::<f64>()[idx];
665 let original_val = values[idx];
666 assert!(
667 NativePType::is_eq(decoded_val, original_val),
668 "At index {idx}: Expected {original_val} but got {decoded_val}"
669 );
670 }
671 }
672
673 #[test]
674 fn test_large_nullable_array() {
675 let size = 12_000;
676 let values: Vec<Option<f32>> = (0..size)
677 .map(|i| {
678 if i % 7 == 0 {
679 None
680 } else {
681 Some((i as f32) * 0.1)
682 }
683 })
684 .collect();
685
686 let array = PrimitiveArray::from_option_iter(values);
687 let encoded =
688 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
689 let decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
690
691 assert_arrays_eq!(decoded, array, &mut SESSION.create_execution_ctx());
692 }
693
694 #[test]
695 fn test_large_mixed_validity_with_patches() {
696 let size = 6_000;
697 let mut values = vec![10.125f64; size];
698
699 values[500] = PI;
700 values[1500] = E;
701 values[2500] = f64::INFINITY;
702 values[3500] = f64::NEG_INFINITY;
703 values[4500] = f64::NAN;
704
705 let validity = Validity::from_iter((0..size).map(|i| !matches!(i, 500 | 2500)));
706
707 let array = PrimitiveArray::new(Buffer::from(values), validity);
708 let encoded =
709 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
710 let decoded = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
711
712 assert_arrays_eq!(decoded, array, &mut SESSION.create_execution_ctx());
713 }
714
715 #[test]
723 fn test_slice_mid_chunk_with_patches_in_trailing_chunk() {
724 let mut values = vec![1.0f64; 3072];
726 values[100] = PI;
728 values[500] = E;
729 values[1100] = PI;
731 values[1500] = E;
732 values[1900] = PI;
733 values[2100] = PI;
735 values[2500] = E;
736 values[2900] = PI;
737
738 let original = PrimitiveArray::new(Buffer::from(values), Validity::NonNullable);
739 let encoded = alp_encode(
740 original.as_view(),
741 None,
742 &mut SESSION.create_execution_ctx(),
743 )
744 .unwrap();
745 assert!(encoded.patches().is_some());
746
747 let sliced_alp = encoded.slice(0..2500).unwrap();
751 let expected = original.slice(0..2500).unwrap();
752 assert_arrays_eq!(sliced_alp, expected, &mut SESSION.create_execution_ctx());
753
754 let sliced_alp = encoded.slice(500..2500).unwrap();
756 let expected = original.slice(500..2500).unwrap();
757 assert_arrays_eq!(sliced_alp, expected, &mut SESSION.create_execution_ctx());
758 }
759}