use crate::application::array::Array;
use crate::application::view::{ArrayView, ArrayViewMut};
use crate::domain::error::{LetoError, Result};
use crate::domain::layout::Layout;
use crate::infrastructure::storage::VecStorage;
pub type PadWidth<const N: usize> = [(usize, usize); N];
pub fn padded_shape<const N: usize>(
in_shape: [usize; N],
width: PadWidth<N>,
) -> Result<[usize; N]> {
let mut out_shape = [0usize; N];
for d in 0..N {
out_shape[d] = width[d]
.0
.checked_add(in_shape[d])
.and_then(|v| v.checked_add(width[d].1))
.ok_or(LetoError::Overflow {
reason: "pad width overflows axis extent",
})?;
}
Ok(out_shape)
}
pub fn pad_into<T: Clone, const N: usize>(
input: &ArrayView<'_, T, N>,
width: PadWidth<N>,
fill: T,
output: &mut ArrayViewMut<'_, T, N>,
) -> Result<()> {
let in_shape = input.shape();
let expected = padded_shape(in_shape, width)?;
if output.shape() != expected {
return Err(LetoError::ShapeMismatch {
lhs: expected.to_vec(),
rhs: output.shape().to_vec(),
});
}
for (out_index, slot) in output.reborrow().indexed_iter_mut()? {
let mut src_index = [0usize; N];
let mut inside = true;
for d in 0..N {
if out_index[d] < width[d].0 {
inside = false;
break;
}
let s = out_index[d] - width[d].0;
if s >= in_shape[d] {
inside = false;
break;
}
src_index[d] = s;
}
*slot = if inside {
input.get(src_index)?.clone()
} else {
fill.clone()
};
}
Ok(())
}
pub fn pad<T: Clone, const N: usize>(
input: &ArrayView<'_, T, N>,
width: PadWidth<N>,
fill: T,
) -> Result<Array<T, VecStorage<T>, N>> {
let out_shape = padded_shape(input.shape(), width)?;
let out_layout = Layout::c_contiguous(out_shape)?;
let size = out_layout.size();
let mut output = Array::new(out_layout, VecStorage::uninit(size))?;
pad_into(input, width, fill, &mut output.view_mut())?;
Ok(output)
}