use core::fmt::Debug;
use core::iter::FusedIterator;
use core::marker::PhantomData;
use core::ops::Index;
use crate::grid::{BoundsError, GridBounds};
use crate::location::{Column, Component as LocComponent, Location, LocationLike, Row};
use crate::range::{ColumnRangeError, ComponentRange, LocationRange, RangeError, RowRangeError};
pub trait Grid: GridBounds {
type Item;
unsafe fn get_unchecked(&self, location: &Location) -> &Self::Item;
#[inline]
fn get(&self, location: impl LocationLike) -> Result<&Self::Item, BoundsError> {
self.check_location(location)
.map(move |loc| unsafe { self.get_unchecked(&loc) })
}
#[inline]
fn view<T: LocComponent>(&self) -> View<Self, T> {
View::new(self)
}
#[inline]
fn rows(&self) -> RowsView<Self> {
self.view()
}
#[inline]
fn columns(&self) -> ColumnsView<Self> {
self.view()
}
#[inline]
unsafe fn single_view_unchecked<T: LocComponent>(&self, index: T) -> SingleView<Self, T> {
SingleView::new_unchecked(self, index)
}
#[inline]
unsafe fn row_unchecked(&self, row: Row) -> RowView<Self> {
self.single_view_unchecked(row)
}
#[inline]
unsafe fn column_unchecked(&self, column: Column) -> ColumnView<Self> {
self.single_view_unchecked(column)
}
#[inline]
fn single_view<T: LocComponent>(&self, index: T) -> Result<SingleView<Self, T>, RangeError<T>> {
SingleView::new(self, index)
}
#[inline]
fn row(&self, row: impl Into<Row>) -> Result<RowView<Self>, RowRangeError> {
self.single_view(row.into())
}
#[inline]
fn column(&self, column: impl Into<Column>) -> Result<ColumnView<Self>, ColumnRangeError> {
self.single_view(column.into())
}
}
impl<G: Grid> Grid for &G {
type Item = G::Item;
#[inline]
unsafe fn get_unchecked(&self, location: &Location) -> &Self::Item {
G::get_unchecked(self, location)
}
}
impl<G: Grid> Grid for &mut G {
type Item = G::Item;
#[inline]
unsafe fn get_unchecked(&self, location: &Location) -> &Self::Item {
G::get_unchecked(self, location)
}
}
#[derive(Debug)]
pub struct View<'a, G: Grid + ?Sized, T: LocComponent> {
grid: &'a G,
index: PhantomData<T>,
}
impl<'a, G: Grid + ?Sized, T: LocComponent> View<'a, G, T> {
#[inline]
fn new(grid: &'a G) -> Self {
Self {
grid,
index: PhantomData,
}
}
#[inline]
pub fn len(&self) -> T::Distance {
self.grid.dimension()
}
#[inline]
pub unsafe fn get_unchecked(&self, index: T) -> SingleView<'a, G, T> {
SingleView::new_unchecked(self.grid, index)
}
#[inline]
pub fn get(&self, index: T) -> Result<SingleView<'a, G, T>, RangeError<T>> {
SingleView::new(self.grid, index)
}
#[inline]
pub fn range(&self) -> ComponentRange<T> {
self.grid.range()
}
#[inline]
pub fn iter(
&self,
) -> impl Iterator<Item = SingleView<'a, G, T>>
+ DoubleEndedIterator
+ FusedIterator
+ ExactSizeIterator
+ Debug
+ Clone {
let grid = self.grid;
self.range()
.map(move |index| unsafe { SingleView::new_unchecked(grid, index) })
}
}
impl<'a, G: Grid + ?Sized, T: LocComponent> Clone for View<'a, G, T> {
fn clone(&self) -> Self {
Self {
grid: self.grid,
index: PhantomData,
}
}
}
pub type RowsView<'a, G> = View<'a, G, Row>;
impl<'a, G: Grid + ?Sized> RowsView<'a, G> {
#[inline]
pub fn row(&self, row: impl Into<Row>) -> Result<RowView<'a, G>, RowRangeError> {
self.get(row.into())
}
}
pub type ColumnsView<'a, G> = View<'a, G, Column>;
impl<'a, G: Grid + ?Sized> ColumnsView<'a, G> {
#[inline]
pub fn column(&self, column: impl Into<Column>) -> Result<ColumnView<'a, G>, ColumnRangeError> {
self.get(column.into())
}
}
#[derive(Debug)]
pub struct SingleView<'a, G: Grid + ?Sized, T: LocComponent> {
grid: &'a G,
index: T,
}
impl<'a, G: Grid + ?Sized, T: LocComponent> SingleView<'a, G, T> {
#[inline]
unsafe fn new_unchecked(grid: &'a G, index: T) -> Self {
Self { grid, index }
}
#[inline]
fn new(grid: &'a G, index: T) -> Result<Self, RangeError<T>> {
grid.check_component(index)
.map(move |index| unsafe { Self::new_unchecked(grid, index) })
}
#[inline]
pub fn len(&self) -> <T::Converse as LocComponent>::Distance {
self.grid.dimension()
}
#[inline]
pub fn index(&self) -> T {
self.index
}
#[inline]
pub unsafe fn get_unchecked(&self, cross: T::Converse) -> &'a G::Item {
self.grid.get_unchecked(&self.index.combine(cross))
}
#[inline]
pub fn get(&self, cross: T::Converse) -> Result<&'a G::Item, RangeError<T::Converse>> {
self.grid
.check_component(cross)
.map(move |cross| unsafe { self.get_unchecked(cross) })
}
#[inline]
pub fn range(&self) -> LocationRange<T> {
LocationRange::new(self.index, self.grid.range())
}
#[inline]
pub fn iter(
&self,
) -> impl Iterator<Item = &'a G::Item>
+ DoubleEndedIterator
+ FusedIterator
+ ExactSizeIterator
+ Debug
+ Clone {
let grid = self.grid;
self.range()
.map(move |loc| unsafe { grid.get_unchecked(&loc) })
}
#[inline]
pub fn iter_with_locations(
&self,
) -> impl Iterator<Item = (Location, &'a G::Item)>
+ DoubleEndedIterator
+ FusedIterator
+ ExactSizeIterator
+ Debug
+ Clone {
let grid = self.grid;
self.range()
.map(move |loc| (loc, unsafe { grid.get_unchecked(&loc) }))
}
#[inline]
pub fn iter_with_indices(
&self,
) -> impl Iterator<Item = (T::Converse, &'a G::Item)>
+ DoubleEndedIterator
+ FusedIterator
+ ExactSizeIterator
+ Debug
+ Clone {
let grid = self.grid;
let index = self.index;
self.grid.range().map(move |cross: T::Converse| {
(cross, unsafe { grid.get_unchecked(&cross.combine(index)) })
})
}
}
impl<'a, G: Grid + ?Sized, T: LocComponent> Index<T::Converse> for SingleView<'a, G, T> {
type Output = G::Item;
#[inline]
fn index(&self, idx: T::Converse) -> &G::Item {
self.get(idx).unwrap_or_else(|err| match err {
RangeError::TooHigh(max) => panic!("{:?} too high, must be < {:?}", idx, max),
RangeError::TooLow(min) => panic!("{:?} too low, must be >= {:?}", idx, min),
})
}
}
impl<'a, G: Grid + ?Sized, T: LocComponent> Clone for SingleView<'a, G, T> {
fn clone(&self) -> Self {
Self {
grid: self.grid,
index: self.index,
}
}
}
pub type RowView<'a, G> = SingleView<'a, G, Row>;
impl<'a, G: Grid + ?Sized> RowView<'a, G> {
#[inline]
pub fn column(&self, column: impl Into<Column>) -> Result<&'a G::Item, ColumnRangeError> {
self.get(column.into())
}
}
pub type ColumnView<'a, G> = SingleView<'a, G, Column>;
impl<'a, G: Grid + ?Sized> ColumnView<'a, G> {
#[inline]
pub fn row(&self, row: impl Into<Row>) -> Result<&'a G::Item, RowRangeError> {
self.get(row.into())
}
}
#[cfg(test)]
mod tests {
use crate::grid::BoundsError;
use crate::prelude::*;
use crate::range::{ColumnRangeError, RangeError, RowRangeError};
#[derive(Debug, Eq, PartialEq)]
struct ThreeByTwo<T> {
rows: [[T; 2]; 3],
}
impl<T> GridBounds for ThreeByTwo<T> {
fn dimensions(&self) -> Vector {
Vector::new(3, 2)
}
fn root(&self) -> Location {
Location::new(-1, 0)
}
}
impl<T> Grid for ThreeByTwo<T> {
type Item = T;
unsafe fn get_unchecked(&self, location: &Location) -> &T {
assert!(location.row.0 >= -1 && location.row.0 <= 1);
assert!(location.column.0 >= 0 && location.column.0 <= 1);
self.rows
.get_unchecked((location.row.0 + 1) as usize)
.get_unchecked(location.column.0 as usize)
}
}
static TEST_GRID: ThreeByTwo<i16> = ThreeByTwo {
rows: [[1, 2], [3, 4], [5, 6]],
};
static TEST_ROWS: [(Row, Option<RowRangeError>); 3] = [
(Row(-10), Some(RangeError::TooLow(Row(-1)))),
(Row(0), None),
(Row(10), Some(RangeError::TooHigh(Row(2)))),
];
static TEST_COLUMNS: [(Column, Option<ColumnRangeError>); 3] = [
(Column(-10), Some(RangeError::TooLow(Column(0)))),
(Column(0), None),
(Column(10), Some(RangeError::TooHigh(Column(2)))),
];
#[test]
fn test_get_in_bounds() {
let mut value = 1;
for row in Row(-1).span(Rows(3)) {
for column in Column(0).span(Columns(2)) {
assert_eq!(TEST_GRID.get(row + column), Ok(&value));
value += 1;
}
}
}
#[test]
fn test_out_of_bounds() {
for &(row, row_error) in &TEST_ROWS {
for &(column, column_error) in &TEST_COLUMNS {
let result = TEST_GRID.get(row + column);
match result {
Err(BoundsError::Row(err)) => {
assert_eq!(row_error, Some(err));
assert_eq!(column_error, None);
}
Err(BoundsError::Column(err)) => {
assert_eq!(row_error, None);
assert_eq!(column_error, Some(err));
}
Err(BoundsError::Both { row, column }) => {
assert_eq!(row_error, Some(row));
assert_eq!(column_error, Some(column));
}
Ok(_) => {}
}
}
}
}
}