use eunomia::FloatElement;
use crate::application::array::Array;
use crate::application::reduction::variance::degrees_of_freedom;
use crate::domain::error::{LetoError, Result};
use crate::domain::layout::Layout;
use crate::infrastructure::storage::{Storage, VecStorage};
pub fn covariance<T, S>(arr: &Array<T, S, 2>, ddof: T) -> Result<Array<T, VecStorage<T>, 2>>
where
T: FloatElement,
S: Storage<T>,
{
let [v, n] = arr.shape();
if v == 0 || n == 0 {
return Err(LetoError::StorageError {
reason: "covariance over an empty matrix is undefined".to_string(),
});
}
let denom = degrees_of_freedom(n, ddof)?;
let nf = T::from_f64((n) as f64);
let view = arr.view();
let layout = view.layout();
let data = view.data();
let mut centered = vec![T::ZERO; v * n];
for i in 0..v {
let mut sum = T::ZERO;
for k in 0..n {
sum += data[layout.offset_of([i, k])?];
}
let mean = sum / nf;
for k in 0..n {
centered[i * n + k] = data[layout.offset_of([i, k])?] - mean;
}
}
let mut out = vec![T::ZERO; v * v];
for i in 0..v {
for j in i..v {
let mut acc = T::ZERO;
for k in 0..n {
acc += centered[i * n + k] * centered[j * n + k];
}
let c = acc / denom;
out[i * v + j] = c;
out[j * v + i] = c;
}
}
Array::new(Layout::c_contiguous([v, v])?, VecStorage::new(out))
}