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 unsafe {
110 Patches::new_unchecked(
111 len,
112 0,
113 packed_pos,
114 Buffer::from(exceptions).into_array(),
115 None,
117 None,
118 )
119 }
120 });
121
122 ALPRD::try_new(
123 DType::Primitive(T::PTYPE, packed_left.dtype().nullability()),
124 packed_left,
125 Buffer::<u16>::copy_from(encoder.codes()),
126 packed_right,
127 encoder.right_bit_width(),
128 exceptions,
129 )
130 .vortex_expect("ALPRDArray construction in encode")
131}
132
133pub fn alp_rd_decode<T: ALPRDFloat + NativePType>(
142 mut left_parts: BufferMut<u16>,
143 left_parts_dict: &[u16],
144 right_bit_width: u8,
145 mut right_parts: BufferMut<T::UINT>,
146 left_parts_patches: Option<Patches>,
147 ctx: &mut ExecutionCtx,
148) -> VortexResult<Buffer<T>>
149where
150 T::UINT: NativePType,
151{
152 if left_parts.len() != right_parts.len() {
153 vortex_panic!("alp_rd_decode: left_parts.len != right_parts.len");
154 }
155
156 if let Some(patches) = left_parts_patches {
157 alp_rd_dict_decode_inplace(left_parts.as_mut_slice(), left_parts_dict);
161
162 let indices = patches.indices().clone().execute::<PrimitiveArray>(ctx)?;
163 let patch_values = patches.values().clone().execute::<PrimitiveArray>(ctx)?;
164 match_each_integer_ptype!(indices.ptype(), |I| {
165 alp_rd_apply_patches(
166 left_parts.as_mut_slice(),
167 indices.as_slice::<I>(),
168 patch_values.as_slice::<u16>(),
169 patches.offset(),
170 )
171 });
172
173 alp_rd_combine_inplace::<T>(
174 right_parts.as_mut_slice(),
175 left_parts.as_ref(),
176 right_bit_width,
177 );
178 } else {
179 alp_rd_combine_codes_inplace::<T>(
182 right_parts.as_mut_slice(),
183 left_parts.as_ref(),
184 left_parts_dict,
185 right_bit_width,
186 );
187 }
188
189 Ok(unsafe { right_parts.transmute::<T>() }.freeze())
191}
192
193#[cfg(test)]
194mod tests {
195 use vortex_array::arrays::PrimitiveArray;
196
197 use super::ALPRDArrayExt;
198 use super::RDEncoder;
199 use super::RDEncoderExt;
200
201 #[test]
202 fn rd_encoder_uses_deterministic_codes_for_tied_prefixes() {
203 let values: Vec<_> = (1_000_i64..2_000)
204 .map(|row_id| ((row_id * 53) % 36_000) as f64 / 100.0 - 180.0)
205 .collect();
206 let array = PrimitiveArray::from_iter(values.iter().copied());
207
208 let encoded = RDEncoder::new(&values).encode(array.as_view());
209
210 assert_eq!(encoded.right_bit_width(), 53);
211 assert_eq!(
212 encoded.left_parts_dictionary().as_slice(),
213 &[514, 1538, 515, 1539, 513, 1537, 512, 1536]
214 );
215 }
216}