1#![expect(clippy::cast_possible_truncation)]
7
8pub use ::alp::ALPRDFloat;
9pub use ::alp::RDEncoder;
10use ::alp::alp_rd_apply_patches;
11use ::alp::alp_rd_combine_codes_inplace;
12use ::alp::alp_rd_combine_inplace;
13use ::alp::alp_rd_dict_decode_inplace;
14use ::alp::bit_width;
15pub use array::*;
16use vortex_array::ArrayView;
17use vortex_array::ExecutionCtx;
18use vortex_array::IntoArray;
19use vortex_array::arrays::Primitive;
20use vortex_array::arrays::PrimitiveArray;
21use vortex_array::dtype::DType;
22use vortex_array::dtype::NativePType;
23use vortex_array::match_each_integer_ptype;
24use vortex_array::patches::Patches;
25use vortex_array::validity::Validity;
26use vortex_buffer::Buffer;
27use vortex_buffer::BufferMut;
28use vortex_error::VortexExpect;
29use vortex_error::VortexResult;
30use vortex_error::vortex_panic;
31use vortex_fastlanes::bitpack_compress::bitpack_encode_unchecked;
32use vortex_session::VortexSession;
33
34use crate::match_each_alp_float_ptype;
35
36mod array;
37mod compute;
38mod kernel;
39mod ops;
40mod rules;
41mod slice;
42
43pub(crate) fn initialize(session: &VortexSession) {
44 kernel::initialize(session);
45}
46
47pub trait RDEncoderExt {
49 fn encode(&self, array: ArrayView<'_, Primitive>) -> ALPRDArray;
54}
55
56impl RDEncoderExt for RDEncoder {
57 fn encode(&self, array: ArrayView<'_, Primitive>) -> ALPRDArray {
58 match_each_alp_float_ptype!(array.ptype(), |P| { encode_generic::<P>(self, array) })
59 }
60}
61
62fn encode_generic<T>(encoder: &RDEncoder, array: ArrayView<'_, Primitive>) -> ALPRDArray
63where
64 T: ALPRDFloat + NativePType,
65 T::UINT: NativePType,
66{
67 let (left_parts, right_parts, exceptions_pos, exceptions) =
68 encoder.split_parts(array.as_slice::<T>());
69 let len = left_parts.len();
70
71 let primitive_left = PrimitiveArray::new(
73 Buffer::from(left_parts),
74 array
75 .validity()
76 .vortex_expect("ALP RD validity should be derivable"),
77 );
78 let packed_left = unsafe {
80 bitpack_encode_unchecked(primitive_left, encoder.left_bit_width())
81 .vortex_expect("bitpack_encode_unchecked should succeed for left parts")
82 .into_array()
83 };
84
85 let primitive_right = PrimitiveArray::new(Buffer::from(right_parts), Validity::NonNullable);
86 let packed_right = unsafe {
88 bitpack_encode_unchecked(primitive_right, encoder.right_bit_width())
89 .vortex_expect("bitpack_encode_unchecked should succeed for right parts")
90 .into_array()
91 };
92
93 let exceptions = (!exceptions_pos.is_empty()).then(|| {
95 let max_exc_pos = exceptions_pos.last().copied().unwrap_or_default();
96 let bw = bit_width(max_exc_pos);
97
98 let exc_pos_array =
99 PrimitiveArray::new(Buffer::from(exceptions_pos), Validity::NonNullable);
100 let packed_pos = unsafe {
102 bitpack_encode_unchecked(exc_pos_array, bw)
103 .vortex_expect("bitpack_encode_unchecked should succeed for exception positions")
104 .into_array()
105 };
106
107 Patches::new(
108 len,
109 0,
110 packed_pos,
111 Buffer::from(exceptions).into_array(),
112 None,
114 )
115 .vortex_expect("Patches construction in encode")
116 });
117
118 ALPRD::try_new(
119 DType::Primitive(T::PTYPE, packed_left.dtype().nullability()),
120 packed_left,
121 Buffer::<u16>::copy_from(encoder.codes()),
122 packed_right,
123 encoder.right_bit_width(),
124 exceptions,
125 )
126 .vortex_expect("ALPRDArray construction in encode")
127}
128
129pub fn alp_rd_decode<T: ALPRDFloat + NativePType>(
138 mut left_parts: BufferMut<u16>,
139 left_parts_dict: &[u16],
140 right_bit_width: u8,
141 mut right_parts: BufferMut<T::UINT>,
142 left_parts_patches: Option<Patches>,
143 ctx: &mut ExecutionCtx,
144) -> VortexResult<Buffer<T>>
145where
146 T::UINT: NativePType,
147{
148 if left_parts.len() != right_parts.len() {
149 vortex_panic!("alp_rd_decode: left_parts.len != right_parts.len");
150 }
151
152 if let Some(patches) = left_parts_patches {
153 alp_rd_dict_decode_inplace(left_parts.as_mut_slice(), left_parts_dict);
157
158 let indices = patches.indices().clone().execute::<PrimitiveArray>(ctx)?;
159 let patch_values = patches.values().clone().execute::<PrimitiveArray>(ctx)?;
160 match_each_integer_ptype!(indices.ptype(), |I| {
161 alp_rd_apply_patches(
162 left_parts.as_mut_slice(),
163 indices.as_slice::<I>(),
164 patch_values.as_slice::<u16>(),
165 patches.offset(),
166 )
167 });
168
169 alp_rd_combine_inplace::<T>(
170 right_parts.as_mut_slice(),
171 left_parts.as_ref(),
172 right_bit_width,
173 );
174 } else {
175 alp_rd_combine_codes_inplace::<T>(
178 right_parts.as_mut_slice(),
179 left_parts.as_ref(),
180 left_parts_dict,
181 right_bit_width,
182 );
183 }
184
185 Ok(unsafe { right_parts.transmute::<T>() }.freeze())
187}
188
189#[cfg(test)]
190mod tests {
191 use vortex_array::arrays::PrimitiveArray;
192
193 use super::ALPRDArrayExt;
194 use super::RDEncoder;
195 use super::RDEncoderExt;
196
197 #[test]
198 fn rd_encoder_uses_deterministic_codes_for_tied_prefixes() {
199 let values: Vec<_> = (1_000_i64..2_000)
200 .map(|row_id| ((row_id * 53) % 36_000) as f64 / 100.0 - 180.0)
201 .collect();
202 let array = PrimitiveArray::from_iter(values.iter().copied());
203
204 let encoded = RDEncoder::new(&values).encode(array.as_view());
205
206 assert_eq!(encoded.right_bit_width(), 53);
207 assert_eq!(
208 encoded.left_parts_dictionary().as_slice(),
209 &[514, 1538, 515, 1539, 513, 1537, 512, 1536]
210 );
211 }
212}