use crate::constant_fit::{compute_residual_error, fit_constant_fixed};
use crate::q16_linear::{fit_linear_fixed, Q16_SHIFT};
pub fn should_use_linear(data: &[i32]) -> bool {
if data.len() < 3 {
return false;
}
let constant = fit_constant_fixed(data);
let (slope, intercept) = fit_linear_fixed(data);
let error_constant = compute_residual_error(data, 0, constant);
let error_linear = compute_residual_error(data, slope, intercept);
error_linear < error_constant / 2
}
#[inline]
#[must_use]
#[allow(clippy::suspicious_operation_groupings)]
pub fn fit_quadratic_fixed(data: &[i32]) -> (i32, i32, i32) {
let n = data.len();
if n < 3 {
if n == 2 {
let (s, i) = fit_linear_fixed(data);
return (0, s, i);
}
if n == 1 {
let val = unsafe { *data.get_unchecked(0) };
return (0, 0, val.wrapping_shl(Q16_SHIFT as u32));
}
return (0, 0, 0);
}
let n64 = n as i64;
let n128 = n as i128;
let sx = (n64 * (n64 - 1)) / 2;
let sx2 = (n64 * (n64 - 1) * (2 * n64 - 1)) / 6;
let sx3 = ((n128 * (n128 - 1)) / 2) * ((n128 * (n128 - 1)) / 2);
let sx3_i64 = sx3 as i64;
let sx4 = (n128 * (n128 - 1) * (2 * n128 - 1) * (3 * n128 * n128 - 3 * n128 - 1)) / 30;
let sx4_i64 = sx4 as i64;
let mut sy: i64 = 0;
let mut sxy: i64 = 0;
let mut sx2y: i64 = 0;
let ptr = data.as_ptr();
for i in 0..n {
let y = unsafe { *ptr.add(i) } as i64;
let x = i as i64;
sy += y;
sxy += x * y;
sx2y += x * x * y;
}
let det = {
n64 as i128 * (sx2 as i128 * sx4_i64 as i128 - sx3_i64 as i128 * sx3_i64 as i128)
- sx as i128 * (sx as i128 * sx4_i64 as i128 - sx3_i64 as i128 * sx2 as i128)
+ sx2 as i128 * (sx as i128 * sx3_i64 as i128 - sx2 as i128 * sx2 as i128)
};
if det == 0 {
let (s, i) = fit_linear_fixed(data);
return (0, s, i);
}
let det_a = {
n64 as i128 * (sx2 as i128 * sx2y as i128 - sx3_i64 as i128 * sxy as i128)
- sx as i128 * (sx as i128 * sx2y as i128 - sx3_i64 as i128 * sy as i128)
+ sx2 as i128 * (sx as i128 * sxy as i128 - sx2 as i128 * sy as i128)
};
let a = ((det_a << Q16_SHIFT as i128) / det) as i32;
let det_b = {
n64 as i128 * (sxy as i128 * sx4_i64 as i128 - sx2y as i128 * sx3_i64 as i128)
- sy as i128 * (sx as i128 * sx4_i64 as i128 - sx2 as i128 * sx3_i64 as i128)
+ sx2 as i128 * (sx as i128 * sx2y as i128 - sx2 as i128 * sxy as i128)
};
let b = ((det_b << Q16_SHIFT as i128) / det) as i32;
let det_c = {
sy as i128 * (sx2 as i128 * sx4_i64 as i128 - sx3_i64 as i128 * sx3_i64 as i128)
- sxy as i128 * (sx as i128 * sx4_i64 as i128 - sx3_i64 as i128 * sx2 as i128)
+ sx2y as i128 * (sx as i128 * sx3_i64 as i128 - sx2 as i128 * sx2 as i128)
};
let c = ((det_c << Q16_SHIFT as i128) / det) as i32;
(a, b, c)
}
#[inline(always)]
#[must_use]
pub const fn evaluate_quadratic_fixed(a: i32, b: i32, c: i32, x: i32) -> i32 {
let x64 = x as i64;
let ax2 = (a as i64) * x64 * x64;
let bx = (b as i64) * x64;
(ax2 + bx) as i32 + c
}
#[inline]
#[must_use]
#[allow(clippy::needless_range_loop)]
pub fn fit_cubic_fixed(data: &[i32]) -> (i32, i32, i32, i32) {
let n = data.len();
if n < 4 {
let (a, b, c) = fit_quadratic_fixed(data);
return (0, a, b, c);
}
let n64 = n as i64;
let mut sy: i128 = 0;
let mut sxy: i128 = 0;
let mut sx2y: i128 = 0;
let mut sx3y: i128 = 0;
let mut sxk = [0i128; 7]; sxk[0] = n64 as i128;
let ptr = data.as_ptr();
for i in 0..n {
let y = unsafe { *ptr.add(i) } as i128;
let x = i as i128;
let x2 = x * x;
let x3 = x2 * x;
sy += y;
sxy += x * y;
sx2y += x2 * y;
sx3y += x3 * y;
let mut xk = x;
for slot in &mut sxk[1..] {
*slot += xk;
xk *= x;
}
}
let mut m = [[0i128; 5]; 4]; for (i, row) in m.iter_mut().enumerate() {
row[..4].copy_from_slice(&sxk[i..i + 4]);
}
m[0][4] = sy;
m[1][4] = sxy;
m[2][4] = sx2y;
m[3][4] = sx3y;
for col in 0..4 {
let mut max_row = col;
let mut max_val = m[col][col].abs();
for (row_idx, row_data) in m.iter().enumerate().skip(col + 1) {
if row_data[col].abs() > max_val {
max_val = row_data[col].abs();
max_row = row_idx;
}
}
if max_val == 0 {
let (a2, b2, c2) = fit_quadratic_fixed(data);
return (0, a2, b2, c2);
}
m.swap(col, max_row);
let pivot = m[col][col];
for row in (col + 1)..4 {
let factor = m[row][col];
if factor == 0 {
continue;
}
for j in col..5 {
m[row][j] = m[row][j] * pivot - m[col][j] * factor;
}
}
}
let mut sol = [0i128; 4]; for i in (0..4).rev() {
let mut sum = m[i][4] << Q16_SHIFT;
for j in (i + 1)..4 {
sum -= m[i][j] * sol[j];
}
if m[i][i] != 0 {
sol[i] = sum / m[i][i];
}
}
(sol[3] as i32, sol[2] as i32, sol[1] as i32, sol[0] as i32)
}
#[inline(always)]
#[must_use]
pub const fn evaluate_cubic_fixed(a: i32, b: i32, c: i32, d: i32, x: i32) -> i32 {
let x64 = x as i64;
let x2 = x64 * x64;
let x3 = x2 * x64;
let ax3 = (a as i64) * x3;
let bx2 = (b as i64) * x2;
let cx = (c as i64) * x64;
(ax3 + bx2 + cx) as i32 + d
}