#![allow(clippy::cast_precision_loss)]
use crate::view::{BitMaskView, F64VectorView};
#[must_use]
pub fn masked_sum(x: F64VectorView<'_>, mask: Option<BitMaskView<'_>>) -> f64 {
if mask.is_none() {
if let Some(slice) = x.as_slice() {
return slice.iter().sum();
}
}
crate::scalar::masked_sum(x, mask)
}
#[must_use]
pub fn masked_mean(x: F64VectorView<'_>, mask: Option<BitMaskView<'_>>) -> Option<f64> {
if mask.is_none() {
if let Some(slice) = x.as_slice() {
if slice.is_empty() {
return None;
}
let sum: f64 = slice.iter().sum();
return Some(sum / slice.len() as f64);
}
}
crate::scalar::masked_mean(x, mask)
}
#[must_use]
pub fn masked_variance(x: F64VectorView<'_>, mask: Option<BitMaskView<'_>>) -> Option<f64> {
if mask.is_none() {
if let Some(slice) = x.as_slice() {
if slice.is_empty() {
return None;
}
let n = slice.len() as f64;
let mean = slice.iter().sum::<f64>() / n;
let var = slice
.iter()
.map(|v| {
let d = v - mean;
d * d
})
.sum::<f64>()
/ n;
return Some(var);
}
}
crate::scalar::masked_variance(x, mask)
}
pub fn gather(src: F64VectorView<'_>, indices: &[usize], out: &mut [f64]) {
assert_eq!(out.len(), indices.len());
if let Some(slice) = src.as_slice() {
for (dst, &idx) in out.iter_mut().zip(indices.iter()) {
*dst = slice[idx];
}
return;
}
crate::scalar::gather(src, indices, out);
}
pub fn copy_vec(src: F64VectorView<'_>, dst: &mut [f64]) {
assert_eq!(dst.len(), src.len());
if let Some(slice) = src.as_slice() {
dst.copy_from_slice(slice);
return;
}
crate::scalar::copy_vec(src, dst);
}
#[must_use]
pub fn masked_covariance(
x: F64VectorView<'_>,
y: F64VectorView<'_>,
mask: Option<BitMaskView<'_>>,
) -> Option<f64> {
assert_eq!(x.len(), y.len(), "covariance views must share length");
if mask.is_none() {
if let (Some(xs), Some(ys)) = (x.as_slice(), y.as_slice()) {
let n = xs.len();
if n == 0 {
return None;
}
let nf = n as f64;
let mx = xs.iter().sum::<f64>() / nf;
let my = ys.iter().sum::<f64>() / nf;
let mut acc = 0.0;
for i in 0..n {
acc += (xs[i] - mx) * (ys[i] - my);
}
return Some(acc / nf);
}
}
crate::scalar::masked_covariance(x, y, mask)
}
#[must_use]
pub fn standardize_inplace(x: &mut [f64], eps: f64) -> (f64, f64) {
crate::scalar::standardize_inplace(x, eps)
}
pub fn pairwise_l1_fill(x: &[f64], out: &mut [f64]) {
let n = x.len();
assert_eq!(out.len(), n.saturating_mul(n), "pairwise out must be n*n");
for i in 0..n {
let xi = x[i];
let row = &mut out[i * n..(i + 1) * n];
for (j, slot) in row.iter_mut().enumerate() {
*slot = (xi - x[j]).abs();
}
}
}
pub fn accumulate_contingency(
x_codes: &[u32],
y_codes: &[u32],
out: &mut [f64],
n_y_levels: usize,
) {
crate::scalar::accumulate_contingency(x_codes, y_codes, out, n_y_levels);
}
pub fn accumulate_contingency_rows(
x_codes: &[u32],
y_codes: &[u32],
rows: &[usize],
out: &mut [f64],
n_y_levels: usize,
) {
crate::scalar::accumulate_contingency_rows(x_codes, y_codes, rows, out, n_y_levels);
}
#[must_use]
pub fn weighted_sum(x: &[f64], weights: &[f64]) -> f64 {
assert_eq!(x.len(), weights.len(), "weighted_sum length mismatch");
x.iter().zip(weights.iter()).map(|(xi, &w)| crate::scalar::sanitize_weight(w) * xi).sum()
}
#[must_use]
pub fn weighted_mean(x: &[f64], weights: &[f64]) -> Option<f64> {
crate::scalar::weighted_mean(x, weights)
}
#[must_use]
pub fn weighted_dot(x: &[f64], y: &[f64], weights: &[f64]) -> f64 {
assert_eq!(x.len(), y.len(), "weighted_dot xy length");
assert_eq!(x.len(), weights.len(), "weighted_dot weight length");
let mut acc = 0.0;
for i in 0..x.len() {
acc += crate::scalar::sanitize_weight(weights[i]) * x[i] * y[i];
}
acc
}