use crate::application::array::Array;
use crate::application::view::ArrayView;
use crate::infrastructure::storage::{Storage, StorageMut, VecStorage};
impl<T, S, const N: usize> Array<T, S, N>
where
S: Storage<T>,
T: Copy,
{
#[must_use = "mapv returns a new array; use mapv_inplace to mutate in place"]
pub fn mapv<U, F>(&self, mut f: F) -> Array<U, VecStorage<U>, N>
where
F: FnMut(T) -> U,
{
let data: Vec<U> = self.iter().map(|&x| f(x)).collect();
Array::<U, VecStorage<U>, N>::from_shape_vec(self.shape(), data)
.expect("invariant: mapv preserves the element count and shape")
}
#[must_use]
pub fn to_vec(&self) -> Vec<T> {
self.iter().copied().collect()
}
pub fn fold<B, F>(&self, init: B, mut f: F) -> B
where
F: FnMut(B, T) -> B,
{
self.iter().fold(init, |acc, &x| f(acc, x))
}
#[must_use]
pub fn zip_map<U, S2, V, F>(
&self,
other: &Array<U, S2, N>,
mut f: F,
) -> Array<V, VecStorage<V>, N>
where
U: Copy,
S2: Storage<U>,
F: FnMut(T, U) -> V,
{
assert_eq!(
self.shape(),
other.shape(),
"zip_map requires matching shapes: self {:?} vs other {:?}",
self.shape(),
other.shape()
);
let data: Vec<V> = self
.iter()
.zip(other.iter())
.map(|(&a, &b)| f(a, b))
.collect();
Array::<V, VecStorage<V>, N>::from_shape_vec(self.shape(), data)
.expect("invariant: zip_map preserves the element count and shape")
}
}
impl<T, S, const N: usize> Array<T, S, N>
where
S: StorageMut<T>,
T: Copy,
{
pub fn mapv_inplace<F>(&mut self, mut f: F)
where
F: FnMut(T) -> T,
{
let layout = self.layout;
let size = layout.size();
if size == 0 {
return;
}
if layout.is_c_dense() {
let start = layout.offset;
for slot in &mut self.storage.as_mut_slice()[start..start + size] {
*slot = f(*slot);
}
} else {
let shape = layout.shape;
let slice = self.storage.as_mut_slice();
let mut index = [0usize; N];
for _ in 0..size {
let off = layout
.offset_of(index)
.expect("invariant: logical index is in bounds");
slice[off] = f(slice[off]);
for d in (0..N).rev() {
index[d] += 1;
if index[d] < shape[d] {
break;
}
index[d] = 0;
}
}
}
}
pub fn fill(&mut self, value: T) {
self.mapv_inplace(|_| value);
}
pub fn assign(&mut self, src: &ArrayView<'_, T, N>) {
assert_eq!(
self.shape(),
src.shape(),
"assign requires matching shapes: self {:?} vs src {:?}",
self.shape(),
src.shape()
);
let mut it = src.iter();
self.mapv_inplace(|_| *it.next().expect("invariant: src has self.size() elements"));
}
}
#[cfg(test)]
mod tests {
use crate::application::array::Array;
use crate::infrastructure::storage::VecStorage;
fn arr(shape: [usize; 2], data: Vec<f64>) -> Array<f64, VecStorage<f64>, 2> {
Array::<f64, VecStorage<f64>, 2>::from_shape_vec(shape, data).unwrap()
}
#[test]
fn mapv_applies_elementwise_and_preserves_shape() {
let a = arr([2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let b = a.mapv(|x| x * x);
assert_eq!(b.shape(), [2, 3]);
assert_eq!(
b.iter().copied().collect::<Vec<_>>(),
vec![1.0, 4.0, 9.0, 16.0, 25.0, 36.0]
);
}
#[test]
fn mapv_can_change_element_type() {
let a = arr([1, 3], vec![0.4, 1.6, 2.5]);
let b: Array<i64, VecStorage<i64>, 2> = a.mapv(|x| x as i64);
assert_eq!(b.iter().copied().collect::<Vec<_>>(), vec![0, 1, 2]);
}
#[test]
fn fold_reduces_in_row_major_order() {
let a = arr([2, 2], vec![1.0, 2.0, 3.0, 4.0]);
assert_eq!(a.fold(0.0, |acc, x| acc + x), 10.0);
assert_eq!(a.fold(0.0, |acc, x| acc - x), -10.0);
}
#[test]
fn fill_sets_every_element() {
let mut a = arr([2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
a.fill(7.0);
assert_eq!(a.iter().copied().collect::<Vec<_>>(), vec![7.0; 6]);
}
#[test]
fn assign_copies_source_elementwise() {
let mut dst = arr([2, 3], vec![0.0; 6]);
let src = arr([2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
dst.assign(&src.view());
assert_eq!(
dst.iter().copied().collect::<Vec<_>>(),
vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
);
}
#[test]
#[should_panic(expected = "matching shapes")]
fn assign_shape_mismatch_panics() {
let mut dst = arr([2, 2], vec![0.0; 4]);
let src = arr([2, 3], vec![0.0; 6]);
dst.assign(&src.view());
}
#[test]
fn zip_map_pairs_colocated_elements() {
let a = arr([2, 2], vec![1.0, 2.0, 3.0, 4.0]);
let b = arr([2, 2], vec![10.0, 20.0, 30.0, 40.0]);
let c = a.zip_map(&b, |x, y| x + y);
assert_eq!(c.shape(), [2, 2]);
assert_eq!(
c.iter().copied().collect::<Vec<_>>(),
vec![11.0, 22.0, 33.0, 44.0]
);
}
#[test]
#[should_panic(expected = "matching shapes")]
fn zip_map_shape_mismatch_panics() {
let a = arr([2, 2], vec![0.0; 4]);
let b = arr([2, 3], vec![0.0; 6]);
let _ = a.zip_map(&b, |x, y| x + y);
}
#[test]
fn mapv_inplace_mutates_and_matches_mapv() {
let mut a = arr([2, 3], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let expected = a.mapv(|x| 2.0 * x + 1.0);
a.mapv_inplace(|x| 2.0 * x + 1.0);
assert_eq!(
a.iter().copied().collect::<Vec<_>>(),
expected.iter().copied().collect::<Vec<_>>()
);
assert_eq!(a.shape(), [2, 3]);
}
}