#![expect(clippy::cast_possible_truncation)]
pub use ::alp::ALPRDFloat;
pub use ::alp::RDEncoder;
use ::alp::alp_rd_apply_patches;
use ::alp::alp_rd_combine_codes_inplace;
use ::alp::alp_rd_combine_inplace;
use ::alp::alp_rd_dict_decode_inplace;
use ::alp::bit_width;
pub use array::*;
use vortex_array::ArrayView;
use vortex_array::ExecutionCtx;
use vortex_array::IntoArray;
use vortex_array::arrays::Primitive;
use vortex_array::arrays::PrimitiveArray;
use vortex_array::dtype::DType;
use vortex_array::dtype::NativePType;
use vortex_array::match_each_integer_ptype;
use vortex_array::patches::Patches;
use vortex_array::validity::Validity;
use vortex_buffer::Buffer;
use vortex_buffer::BufferMut;
use vortex_error::VortexExpect;
use vortex_error::VortexResult;
use vortex_error::vortex_panic;
use vortex_fastlanes::bitpack_compress::bitpack_encode_unchecked;
use vortex_session::VortexSession;
use crate::match_each_alp_float_ptype;
mod array;
mod compute;
mod kernel;
mod ops;
mod rules;
mod slice;
pub(crate) fn initialize(session: &VortexSession) {
kernel::initialize(session);
}
pub trait RDEncoderExt {
fn encode(&self, array: ArrayView<'_, Primitive>) -> ALPRDArray;
}
impl RDEncoderExt for RDEncoder {
fn encode(&self, array: ArrayView<'_, Primitive>) -> ALPRDArray {
match_each_alp_float_ptype!(array.ptype(), |P| { encode_generic::<P>(self, array) })
}
}
fn encode_generic<T>(encoder: &RDEncoder, array: ArrayView<'_, Primitive>) -> ALPRDArray
where
T: ALPRDFloat + NativePType,
T::UINT: NativePType,
{
let (left_parts, right_parts, exceptions_pos, exceptions) =
encoder.split_parts(array.as_slice::<T>());
let len = left_parts.len();
let primitive_left = PrimitiveArray::new(
Buffer::from(left_parts),
array
.validity()
.vortex_expect("ALP RD validity should be derivable"),
);
let packed_left = unsafe {
bitpack_encode_unchecked(primitive_left, encoder.left_bit_width())
.vortex_expect("bitpack_encode_unchecked should succeed for left parts")
.into_array()
};
let primitive_right = PrimitiveArray::new(Buffer::from(right_parts), Validity::NonNullable);
let packed_right = unsafe {
bitpack_encode_unchecked(primitive_right, encoder.right_bit_width())
.vortex_expect("bitpack_encode_unchecked should succeed for right parts")
.into_array()
};
let exceptions = (!exceptions_pos.is_empty()).then(|| {
let max_exc_pos = exceptions_pos.last().copied().unwrap_or_default();
let bw = bit_width(max_exc_pos);
let exc_pos_array =
PrimitiveArray::new(Buffer::from(exceptions_pos), Validity::NonNullable);
let packed_pos = unsafe {
bitpack_encode_unchecked(exc_pos_array, bw)
.vortex_expect("bitpack_encode_unchecked should succeed for exception positions")
.into_array()
};
Patches::new(
len,
0,
packed_pos,
Buffer::from(exceptions).into_array(),
None,
)
.vortex_expect("Patches construction in encode")
});
ALPRD::try_new(
DType::Primitive(T::PTYPE, packed_left.dtype().nullability()),
packed_left,
Buffer::<u16>::copy_from(encoder.codes()),
packed_right,
encoder.right_bit_width(),
exceptions,
)
.vortex_expect("ALPRDArray construction in encode")
}
pub fn alp_rd_decode<T: ALPRDFloat + NativePType>(
mut left_parts: BufferMut<u16>,
left_parts_dict: &[u16],
right_bit_width: u8,
mut right_parts: BufferMut<T::UINT>,
left_parts_patches: Option<Patches>,
ctx: &mut ExecutionCtx,
) -> VortexResult<Buffer<T>>
where
T::UINT: NativePType,
{
if left_parts.len() != right_parts.len() {
vortex_panic!("alp_rd_decode: left_parts.len != right_parts.len");
}
if let Some(patches) = left_parts_patches {
alp_rd_dict_decode_inplace(left_parts.as_mut_slice(), left_parts_dict);
let indices = patches.indices().clone().execute::<PrimitiveArray>(ctx)?;
let patch_values = patches.values().clone().execute::<PrimitiveArray>(ctx)?;
match_each_integer_ptype!(indices.ptype(), |I| {
alp_rd_apply_patches(
left_parts.as_mut_slice(),
indices.as_slice::<I>(),
patch_values.as_slice::<u16>(),
patches.offset(),
)
});
alp_rd_combine_inplace::<T>(
right_parts.as_mut_slice(),
left_parts.as_ref(),
right_bit_width,
);
} else {
alp_rd_combine_codes_inplace::<T>(
right_parts.as_mut_slice(),
left_parts.as_ref(),
left_parts_dict,
right_bit_width,
);
}
Ok(unsafe { right_parts.transmute::<T>() }.freeze())
}
#[cfg(test)]
mod tests {
use vortex_array::arrays::PrimitiveArray;
use super::ALPRDArrayExt;
use super::RDEncoder;
use super::RDEncoderExt;
#[test]
fn rd_encoder_uses_deterministic_codes_for_tied_prefixes() {
let values: Vec<_> = (1_000_i64..2_000)
.map(|row_id| ((row_id * 53) % 36_000) as f64 / 100.0 - 180.0)
.collect();
let array = PrimitiveArray::from_iter(values.iter().copied());
let encoded = RDEncoder::new(&values).encode(array.as_view());
assert_eq!(encoded.right_bit_width(), 53);
assert_eq!(
encoded.left_parts_dictionary().as_slice(),
&[514, 1538, 515, 1539, 513, 1537, 512, 1536]
);
}
}