use std::ops::Range;
use crate::{
CoordinatesOutOfBoundsError, MatrixCoordinates, MatrixDimensionError, MatrixDimensions,
MatrixRange,
};
pub trait Matrix2D<C>: Dimensions2D + Domain2D + MatrixRef<C> + MatrixMut<C> + Sized {
fn new(values: Vec<C>, n_cols: usize) -> Result<Self, MatrixDimensionError>;
}
pub trait MatrixRef<C> {
fn get<T: Into<MatrixCoordinates>>(
&self,
coordinates: T,
) -> Result<&C, CoordinatesOutOfBoundsError>;
}
pub trait MatrixMut<C> {
fn set<T: Into<MatrixCoordinates>>(
&mut self,
coordinates: T,
value: C,
) -> Result<C, CoordinatesOutOfBoundsError>;
}
pub trait Domain2D {
fn row_range(&self) -> Range<usize>;
fn col_range(&self) -> Range<usize>;
fn range(&self) -> MatrixRange {
MatrixRange::from((self.row_range(), self.col_range()))
}
fn start(&self) -> MatrixCoordinates {
MatrixCoordinates::new(self.row_range().start, self.col_range().start)
}
fn end(&self) -> MatrixCoordinates {
MatrixCoordinates::new(self.row_range().end, self.col_range().end)
}
fn middle(&self) -> (f64, f64) {
let start = self.start();
let end = self.end();
let rows = start.row as f64 + 0.5 * (end.row - start.row) as f64;
let cols = start.col as f64 + 0.5 * (end.col - start.col) as f64;
(rows, cols)
}
}
impl Domain2D for ((usize, usize), (usize, usize)) {
fn row_range(&self) -> Range<usize> {
self.0 .0..self.1 .0
}
fn col_range(&self) -> Range<usize> {
self.0 .1..self.1 .1
}
}
impl Domain2D for (Range<usize>, Range<usize>) {
fn row_range(&self) -> Range<usize> {
self.0.clone()
}
fn col_range(&self) -> Range<usize> {
self.1.clone()
}
}
impl<D: Domain2D, T: Domain2D> Contains2D<D> for T {
fn contains(&self, other: &D) -> bool {
let plus_one = MatrixRange {
start: self.start(),
end: self.end() + MatrixCoordinates { row: 1, col: 1 },
};
self.contains(&other.start()) && plus_one.contains(&other.end())
}
}
impl<T: Domain2D> Contains2D<MatrixCoordinates> for T {
fn contains(&self, other: &MatrixCoordinates) -> bool {
self.row_range().contains(&other.row) && self.col_range().contains(&other.col)
}
}
pub trait Dimensions2D {
fn n_rows(&self) -> usize;
fn n_cols(&self) -> usize;
fn dimensions(&self) -> MatrixDimensions {
MatrixDimensions::new(self.n_rows(), self.n_cols())
}
fn len(&self) -> usize {
self.n_rows() * self.n_cols()
}
fn is_empty(&self) -> bool {
self.n_rows() == 0 && self.n_cols() == 0
}
}
impl<D: Domain2D> Dimensions2D for D {
fn n_rows(&self) -> usize {
self.row_range().len()
}
fn n_cols(&self) -> usize {
self.col_range().len()
}
}
impl Dimensions2D for (usize, usize) {
fn n_rows(&self) -> usize {
self.0
}
fn n_cols(&self) -> usize {
self.1
}
}
pub trait Contains2D<T> {
fn contains(&self, other: &T) -> bool;
}