1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! Constant fitting + residual error (`fit_constant_fixed` / `compute_residual_error`).
use crate::q16_linear::{evaluate_linear_fixed, int_to_q16, Q16_SHIFT};
pub const fn fit_constant_fixed(data: &[i32]) -> i32 {
let n = data.len();
if n == 0 {
return 0;
}
let ptr = data.as_ptr();
let mut sum: i64 = 0;
// Loop unrolling (4x)
let mut i = 0;
while i + 4 <= n {
// SAFETY: i+3 < n はループ条件で保証。ptr は data.as_ptr() で有効。
unsafe {
let v0 = *ptr.add(i) as i64;
let v1 = *ptr.add(i + 1) as i64;
let v2 = *ptr.add(i + 2) as i64;
let v3 = *ptr.add(i + 3) as i64;
sum = sum.wrapping_add(v0 + v1 + v2 + v3);
}
i += 4;
}
// Remainder
while i < n {
// SAFETY: i < n はループ条件で保証。
unsafe {
sum = sum.wrapping_add(*ptr.add(i) as i64);
}
i += 1;
}
let mean = (sum << Q16_SHIFT) / n as i64;
mean as i32
}
/// Compute residual error (sum of squared differences) - Optimized
///
/// Each prediction-vs-actual difference is right-shifted by 8 bits (`>> 8`)
/// before squaring to prevent i64 overflow when accumulating over large
/// datasets. The returned value is thus scaled by 2^{-16} relative to the
/// true Q16.16 squared error. Use this only for relative comparisons
/// (e.g., `should_use_linear`), not for absolute error reporting.
#[inline(always)]
#[must_use]
pub const fn compute_residual_error(data: &[i32], slope: i32, intercept: i32) -> i64 {
let n = data.len();
let ptr = data.as_ptr();
let mut error: i64 = 0;
let mut i = 0;
while i < n {
// SAFETY: i < n はループ条件で保証。ptr は data.as_ptr() で有効。
unsafe {
let y = *ptr.add(i);
let predicted = evaluate_linear_fixed(slope, intercept, i as i32);
let actual = int_to_q16(y);
let diff = (predicted as i64 - actual as i64) >> 8; // Scale down to prevent overflow
error = error.wrapping_add(diff.wrapping_mul(diff));
}
i += 1;
}
error
}