#![warn(missing_docs, clippy::all, clippy::pedantic)]
use super::{bins::Bins, errors::BinsBuildError, strategies::BinsBuildingStrategy};
use itertools::izip;
use ndarray::{ArrayBase, Axis, Data, Ix1, Ix2};
use std::ops::Range;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Grid<A: Ord> {
projections: Vec<Bins<A>>,
}
impl<A: Ord> From<Vec<Bins<A>>> for Grid<A> {
fn from(projections: Vec<Bins<A>>) -> Self {
Grid { projections }
}
}
impl<A: Ord> Grid<A> {
#[must_use]
pub fn ndim(&self) -> usize {
self.projections.len()
}
#[must_use]
pub fn shape(&self) -> Vec<usize> {
self.projections.iter().map(Bins::len).collect()
}
#[must_use]
pub fn projections(&self) -> &[Bins<A>] {
&self.projections
}
pub fn index_of<S>(&self, point: &ArrayBase<S, Ix1>) -> Option<Vec<usize>>
where
S: Data<Elem = A>,
{
assert_eq!(
point.len(),
self.ndim(),
"Dimension mismatch: the point has {:?} dimensions, the grid \
expected {:?} dimensions.",
point.len(),
self.ndim()
);
point
.iter()
.zip(self.projections.iter())
.map(|(v, e)| e.index_of(v))
.collect()
}
}
impl<A: Ord + Clone> Grid<A> {
#[must_use]
pub fn index(&self, index: &[usize]) -> Vec<Range<A>> {
assert_eq!(
index.len(),
self.ndim(),
"Dimension mismatch: the index has {0:?} dimensions, the grid \
expected {1:?} dimensions.",
index.len(),
self.ndim()
);
izip!(&self.projections, index)
.map(|(bins, &i)| bins.index(i))
.collect()
}
}
#[allow(clippy::module_name_repetitions)]
pub struct GridBuilder<B: BinsBuildingStrategy> {
bin_builders: Vec<B>,
}
impl<A, B> GridBuilder<B>
where
A: Ord,
B: BinsBuildingStrategy<Elem = A>,
{
pub fn from_array<S>(array: &ArrayBase<S, Ix2>) -> Result<Self, BinsBuildError>
where
S: Data<Elem = A>,
{
let bin_builders = array
.axis_iter(Axis(1))
.map(|data| B::from_array(&data))
.collect::<Result<Vec<B>, BinsBuildError>>()?;
Ok(Self { bin_builders })
}
#[must_use]
pub fn build(&self) -> Grid<A> {
let projections: Vec<_> = self.bin_builders.iter().map(|b| b.build()).collect();
Grid::from(projections)
}
}