use crate::{
bitpack::packed_byte_size,
encoder::exception::{Exception, exceptions_byte_size},
float::AlpFloat,
};
const MIN_PRUNE_BIT_WIDTH: u8 = 4;
const MAX_PRUNE_EXCEPTIONS: usize = 16;
const CANDIDATE_WIDTHS: [u8; 9] = [48, 32, 28, 24, 20, 16, 12, 8, 0];
pub(crate) fn try_prune_outliers<F: AlpFloat>(
slice: &[F],
encoded_ints: &mut [F::Int],
base: F::Int,
for_bit_width: u8,
exceptions: &mut Vec<Exception<F::RawBits>>,
is_large: bool,
) -> u8 {
if for_bit_width <= MIN_PRUNE_BIT_WIDTH || exceptions.len() >= MAX_PRUNE_EXCEPTIONS {
return for_bit_width;
}
let c_max = match CANDIDATE_WIDTHS
.iter()
.copied()
.find(|&w| w < for_bit_width)
{
Some(w) => w,
None => return for_bit_width,
};
let max_allowed_c = if c_max == 0 {
0u64
} else {
(1u64 << c_max) - 1
};
let budget = MAX_PRUNE_EXCEPTIONS.saturating_sub(exceptions.len());
let mut excess = 0usize;
for &val in encoded_ints.iter() {
let diff = F::int_diff_to_u64(val, base);
if diff > max_allowed_c {
excess += 1;
if excess > budget {
return for_bit_width;
}
}
}
let count = slice.len();
let current_packed_len = packed_byte_size(count, for_bit_width);
let current_cost = current_packed_len + exceptions_byte_size::<F>(exceptions.len(), is_large);
let mut hist = [0u16; 65];
for &val in encoded_ints.iter() {
let diff = F::int_diff_to_u64(val, base);
let bw = F::bits_needed(diff) as usize;
hist[bw] += 1;
}
let mut exc_count = [0usize; 65];
let mut running = 0usize;
for w in (0..=64).rev() {
exc_count[w] = running;
running += hist[w] as usize;
}
let mut best_target_bw = for_bit_width;
let mut min_cost = current_cost;
for &target_bw in &CANDIDATE_WIDTHS {
if target_bw >= for_bit_width {
continue;
}
let extra_exceptions = exc_count[target_bw as usize];
if extra_exceptions > MAX_PRUNE_EXCEPTIONS {
continue;
}
let new_total_exc = exceptions.len() + extra_exceptions;
let new_cost =
packed_byte_size(count, target_bw) + exceptions_byte_size::<F>(new_total_exc, is_large);
if new_cost < min_cost {
min_cost = new_cost;
best_target_bw = target_bw;
}
}
if best_target_bw < for_bit_width {
let max_allowed = if best_target_bw == 0 {
0u64
} else {
(1u64 << best_target_bw) - 1
};
for (pos, (&v, &val)) in slice.iter().zip(encoded_ints.iter()).enumerate() {
let diff = F::int_diff_to_u64(val, base);
if diff > max_allowed {
exceptions.push(Exception {
pos,
bits: v.to_raw_bits(),
});
}
}
exceptions.sort_unstable_by_key(|e| e.pos);
exceptions.dedup_by_key(|e| e.pos);
for exc in &*exceptions {
unsafe {
*encoded_ints.get_unchecked_mut(exc.pos) = base;
}
}
}
best_target_bw
}