pub const Q16_SHIFT: i32 = 16;
pub const Q16_ONE: i32 = 1 << Q16_SHIFT;
#[inline(always)]
#[must_use]
pub fn fit_linear_fixed(data: &[i32]) -> (i32, i32) {
let n = data.len();
if n < 2 {
if n == 1 {
let val = unsafe { *data.get_unchecked(0) };
return (0, val.wrapping_shl(Q16_SHIFT as u32));
}
return (0, 0);
}
let n64 = n as i64;
let sum_x = (n64 * (n64 - 1)) >> 1;
let mut sum_y: i64 = 0;
let mut sum_xy: i64 = 0;
let ptr = data.as_ptr();
let mut i = 0;
while i + 4 <= n {
unsafe {
let y0 = *ptr.add(i) as i64;
let y1 = *ptr.add(i + 1) as i64;
let y2 = *ptr.add(i + 2) as i64;
let y3 = *ptr.add(i + 3) as i64;
let x0 = i as i64;
let local_sum_y = y0 + y1 + y2 + y3;
sum_y = sum_y.wrapping_add(local_sum_y);
let weighted_y = y1 + (y2 << 1) + y3 * 3; sum_xy = sum_xy.wrapping_add(x0 * local_sum_y + weighted_y);
}
i += 4;
}
while i < n {
unsafe {
let y = *ptr.add(i) as i64;
let x = i as i64;
sum_y = sum_y.wrapping_add(y);
sum_xy = sum_xy.wrapping_add(x * y);
}
i += 1;
}
let n_sq = n64 as i128 * n64 as i128;
let denominator = ((n_sq * (n_sq - 1)) / 12) as i64;
if denominator == 0 {
return (0, (sum_y / n64) as i32);
}
let slope_num = (n64 * sum_xy).wrapping_sub(sum_x * sum_y);
let slope = (slope_num << Q16_SHIFT) / denominator;
let sum_y_fixed = sum_y << Q16_SHIFT;
let slope_term = slope.wrapping_mul(sum_x);
let intercept = (sum_y_fixed.wrapping_sub(slope_term)) / n64;
(slope as i32, intercept as i32)
}
#[inline(always)]
#[must_use]
pub const fn evaluate_linear_fixed(slope: i32, intercept: i32, x: i32) -> i32 {
let mx = (slope as i64).wrapping_mul(x as i64);
(mx as i32).wrapping_add(intercept)
}
#[inline(always)]
#[must_use]
pub const fn q16_to_int(q: i32) -> i32 {
q >> Q16_SHIFT
}
#[inline(always)]
#[must_use]
pub const fn int_to_q16(i: i32) -> i32 {
i << Q16_SHIFT
}
#[cfg(feature = "std")]
#[inline(always)]
#[must_use]
pub fn q16_to_f32(q: i32) -> f32 {
const INV_Q16_ONE: f32 = 1.0 / (1i32 << 16) as f32;
q as f32 * INV_Q16_ONE
}