use ndarray::{ArrayBase, ArrayD, ArrayView, AsArray, Axis, Dimension, IxDyn, Slice, ViewRepr};
use rayon::prelude::*;
use crate::prelude::*;
pub fn div_tile<'a, T, A, D>(
data: A,
div: usize,
threads: Option<usize>,
) -> Result<Vec<ArrayView<'a, T, D>>, ImgalError>
where
A: AsArray<'a, T, D>,
D: Dimension,
T: 'a + AsNumeric,
{
if div == 0 {
return Err(ImgalError::InvalidParameterValueEqual {
param_name: "div",
value: 0,
});
}
let data: ArrayBase<ViewRepr<&'a T>, D> = data.into();
let shape = data.shape().to_vec();
let n_dims = data.shape().len();
let tile_positions: Vec<Vec<(isize, isize)>> = shape
.iter()
.map(|&v| get_div_start_stop_positions(div, v))
.collect();
let n_tiles: usize = tile_positions.iter().map(|v| v.len()).product();
let tile_view = |t: usize| {
let mut tile = data.clone();
let mut remaining = t;
(0..n_dims).for_each(|a| {
let stride: usize = tile_positions.iter().skip(a + 1).map(|v| v.len()).product();
let tile_pos = remaining / stride;
remaining %= stride;
let ax_slice = Slice {
start: tile_positions[a][tile_pos].0,
end: Some(tile_positions[a][tile_pos].1),
step: 1,
};
tile.slice_axis_inplace(Axis(a), ax_slice);
});
tile
};
Ok(par!(threads,
seq_exp: (0..n_tiles).map(&tile_view)
.collect::<Vec<ArrayView<T, D>>>(),
par_exp: (0..n_tiles).into_par_iter().map(&tile_view)
.collect::<Vec<ArrayView<T, D>>>()
))
}
pub fn div_untile<'a, T, D>(
tile_stack: Vec<ArrayView<'a, T, D>>,
div: usize,
shape: &[usize],
) -> Result<ArrayD<T>, ImgalError>
where
D: Dimension,
T: 'a + AsNumeric,
{
if tile_stack.is_empty() {
return Err(ImgalError::InvalidParameterEmptyArray {
param_name: "tile_stack",
});
}
if div == 0 {
return Err(ImgalError::InvalidParameterValueEqual {
param_name: "div",
value: 0,
});
}
let n_dims = tile_stack[0].shape().len();
if shape.len() != n_dims {
return Err(ImgalError::MismatchedArrayLengths {
a_arr_name: "tile shape",
a_arr_len: n_dims,
b_arr_name: "shape",
b_arr_len: shape.len(),
});
}
let tile_positions: Vec<Vec<(isize, isize)>> = shape
.iter()
.map(|&v| get_div_start_stop_positions(div, v))
.collect();
let n_tiles: usize = tile_positions.iter().map(|v| v.len()).product();
if n_tiles != tile_stack.len() {
return Err(ImgalError::InvalidArrayLengthExpected {
arr_name: "tile_stack",
expected: n_tiles,
got: tile_stack.len(),
});
}
let tile_shape: Vec<usize> = shape.iter().map(|&v| v / div).collect();
if tile_shape != tile_stack[0].shape() {
return Err(ImgalError::MismatchedArrayShapes {
a_arr_name: "expected tile",
a_shape: tile_shape,
b_arr_name: "input tile",
b_shape: tile_stack[0].shape().to_vec(),
});
}
let mut untile_arr: ArrayD<T> = ArrayD::from_elem(IxDyn(shape), T::default());
(0..n_tiles).for_each(|t| {
let tile_view = tile_stack[t].view();
let mut untile_view = untile_arr.view_mut();
let mut remaining = t;
(0..n_dims).for_each(|a| {
let stride: usize = tile_positions.iter().skip(a + 1).map(|v| v.len()).product();
let tile_pos = remaining / stride;
remaining %= stride;
let ax_slice = Slice {
start: tile_positions[a][tile_pos].0,
end: Some(tile_positions[a][tile_pos].1),
step: 1,
};
untile_view.slice_axis_inplace(Axis(a), ax_slice);
});
untile_view.assign(&tile_view);
});
Ok(untile_arr)
}
fn get_div_start_stop_positions(div: usize, axis_len: usize) -> Vec<(isize, isize)> {
let mut start_stop_arr: Vec<(isize, isize)> = Vec::with_capacity(div);
let inc = (axis_len / div) as isize;
(0..div).fold(0_isize, |acc, _| {
let start = acc;
let stop = acc + inc;
start_stop_arr.push((start, stop));
stop
});
start_stop_arr[div.saturating_sub(1)].1 = axis_len as isize;
start_stop_arr
}