use crate::constants::AlpFloat;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BestParams {
pub exp: u8,
pub fac: u8,
}
pub const SAMPLES_COUNT: usize = 32;
#[inline(always)]
pub fn is_impossible<F: AlpFloat>(n: F) -> bool {
n.is_impossible()
}
#[inline(always)]
pub fn try_encode_fast<F: AlpFloat>(
val: F,
exp_factor: F,
fac_int: i64,
frac_exp: F,
) -> Option<F::Int> {
val.try_encode_fast(exp_factor, fac_int, frac_exp)
}
#[inline(always)]
pub fn try_encode_value<F: AlpFloat>(val: F, exp: u8, fac: u8) -> Option<F::Int> {
if exp > F::MAX_EXPONENT || fac > exp || fac > F::MAX_FAC {
return None;
}
let exp_factor = F::exp_factor(exp, fac);
let fac_int = F::fac_int(fac);
let frac_exp = F::frac_exp(exp);
val.try_encode_fast(exp_factor, fac_int, frac_exp)
}
pub fn find_best_params<F: AlpFloat>(data: &[F]) -> BestParams {
if data.is_empty() {
return BestParams { exp: 0, fac: 0 };
}
let mut samples = [F::ZERO; SAMPLES_COUNT];
let sample_len = data.len().min(SAMPLES_COUNT);
if data.len() <= SAMPLES_COUNT {
samples[..sample_len].copy_from_slice(data);
} else {
let step = data.len() / SAMPLES_COUNT;
for (i, slot) in samples.iter_mut().enumerate() {
*slot = unsafe { *data.get_unchecked(i * step) };
}
}
let active_samples = &samples[..sample_len];
let first = active_samples[0];
if active_samples.iter().all(|&v| v == first) {
for exp in 0..=F::MAX_EXPONENT {
let frac_exp = F::frac_exp(exp);
let exp_factor = F::exp_factor(exp, 0);
let fac_int = F::fac_int(0);
if F::try_encode_fast(first, exp_factor, fac_int, frac_exp).is_some() {
return BestParams { exp, fac: 0 };
}
}
return BestParams { exp: 0, fac: 0 };
}
let mut best_cost = usize::MAX;
let mut best_params = BestParams { exp: 0, fac: 0 };
for exp in 0..=F::MAX_EXPONENT {
let max_fac = exp.min(F::MAX_FAC);
let frac_exp = F::frac_exp(exp);
for fac in 0..=max_fac {
let exp_factor = F::exp_factor(exp, fac);
let fac_int = F::fac_int(fac);
let mut exceptions = 0usize;
let mut min_val = F::MAX_INT;
let mut max_val = F::MIN_INT;
for &val in active_samples {
if let Some(enc) = F::try_encode_fast(val, exp_factor, fac_int, frac_exp) {
min_val = min_val.min(enc);
max_val = max_val.max(enc);
} else {
exceptions += 1;
if exceptions * F::EXCEPTION_PENALTY >= best_cost {
break;
}
}
}
if exceptions == sample_len || exceptions * F::EXCEPTION_PENALTY >= best_cost {
continue;
}
let max_offset = if min_val <= max_val {
F::calc_range(min_val, max_val)
} else {
0
};
let bit_width = F::bits_needed(max_offset) as usize;
let total_cost = bit_width * sample_len + exceptions * F::EXCEPTION_PENALTY;
if total_cost < best_cost {
best_cost = total_cost;
best_params = BestParams { exp, fac };
if total_cost == 0 {
return best_params;
}
if exceptions == 0 && bit_width <= 2 {
return best_params;
}
}
}
}
best_params
}