use eunomia::NumericElement;
use crate::application::array::Array;
use crate::application::iter::AxisIter;
use crate::application::reduction::iter_elements;
use crate::domain::error::{LetoError, Result};
use crate::domain::layout::Layout;
use crate::domain::remove_axis::{RankMarker, RemoveAxis};
use crate::infrastructure::storage::{Storage, VecStorage};
pub fn sum_all<T, S, const N: usize>(arr: &Array<T, S, N>) -> Result<T>
where
T: NumericElement + for<'a> std::ops::AddAssign<&'a T> + Copy,
S: Storage<T>,
{
if arr.size() == 0 {
return Err(LetoError::StorageError {
reason: "reduction over empty array is undefined".to_string(),
});
}
let view = arr.view();
let mut acc = T::ZERO;
for elem in iter_elements(&view) {
acc += elem;
}
Ok(acc)
}
#[allow(clippy::needless_range_loop)]
pub fn sum_axis<T, S, const N: usize, const M: usize>(
arr: &Array<T, S, N>,
axis: usize,
) -> Result<Array<T, VecStorage<T>, M>>
where
T: NumericElement + std::ops::Add<Output = T> + Copy,
S: Storage<T>,
RankMarker<N>: RemoveAxis<N, SmallerShape = [usize; M], SmallerStrides = [isize; M]>,
{
let view = arr.view();
let iter: AxisIter<'_, T, N, M> = AxisIter::new(&view, axis, RankMarker::<N>)?;
let out_shape = RankMarker::<N>.remove_shape(arr.shape(), axis)?;
let out_size: usize = out_shape.iter().product();
let mut buf: Vec<T> = vec![T::ZERO; out_size];
for lane in iter {
if let Some(slice) = lane.as_slice() {
for (buf_val, &lane_val) in buf.iter_mut().zip(slice) {
*buf_val += lane_val;
}
} else {
for (buf_val, &lane_val) in buf.iter_mut().zip(lane.iter()) {
*buf_val += lane_val;
}
}
}
let out_layout = Layout::c_contiguous(out_shape)?;
let storage = VecStorage::new(buf);
Array::new(out_layout, storage)
}