use crate::{coordinate_position::CoordPos, dimensions::Dimensions};
#[derive(PartialEq, Eq, Clone)]
pub struct IntersectionMatrix(LocationArray<LocationArray<Dimensions>>);
#[derive(PartialEq, Eq, Clone, Copy)]
struct LocationArray<T>([T; 3]);
impl<T> LocationArray<T> {
fn iter(&self) -> impl Iterator<Item = &T> {
self.0.iter()
}
}
impl<T> std::ops::Index<CoordPos> for LocationArray<T> {
type Output = T;
fn index(&self, index: CoordPos) -> &Self::Output {
match index {
CoordPos::Inside => &self.0[0],
CoordPos::OnBoundary => &self.0[1],
CoordPos::Outside => &self.0[2],
}
}
}
impl<T> std::ops::IndexMut<CoordPos> for LocationArray<T> {
fn index_mut(&mut self, index: CoordPos) -> &mut Self::Output {
match index {
CoordPos::Inside => &mut self.0[0],
CoordPos::OnBoundary => &mut self.0[1],
CoordPos::Outside => &mut self.0[2],
}
}
}
#[derive(Debug)]
pub struct InvalidInputError {
message: String,
}
impl InvalidInputError {
fn new(message: String) -> Self {
Self { message }
}
}
impl std::error::Error for InvalidInputError {}
impl std::fmt::Display for InvalidInputError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "invalid input: {}", self.message)
}
}
impl std::fmt::Debug for IntersectionMatrix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn char_for_dim(dim: &Dimensions) -> &'static str {
match dim {
Dimensions::Empty => "F",
Dimensions::ZeroDimensional => "0",
Dimensions::OneDimensional => "1",
Dimensions::TwoDimensional => "2",
}
}
let text = self
.0
.iter()
.flat_map(|r| r.iter().map(char_for_dim))
.collect::<Vec<&str>>()
.join("");
write!(f, "IntersectionMatrix({})", &text)
}
}
impl IntersectionMatrix {
pub fn empty() -> Self {
IntersectionMatrix(LocationArray([LocationArray([Dimensions::Empty; 3]); 3]))
}
pub(crate) fn set(
&mut self,
position_a: CoordPos,
position_b: CoordPos,
dimensions: Dimensions,
) {
self.0[position_a][position_b] = dimensions;
}
pub(crate) fn set_at_least(
&mut self,
position_a: CoordPos,
position_b: CoordPos,
minimum_dimensions: Dimensions,
) {
if self.0[position_a][position_b] < minimum_dimensions {
self.0[position_a][position_b] = minimum_dimensions;
}
}
pub(crate) fn set_at_least_if_in_both(
&mut self,
position_a: Option<CoordPos>,
position_b: Option<CoordPos>,
minimum_dimensions: Dimensions,
) {
if let (Some(position_a), Some(position_b)) = (position_a, position_b) {
self.set_at_least(position_a, position_b, minimum_dimensions);
}
}
pub(crate) fn set_at_least_from_string(
&mut self,
dimensions: &str,
) -> Result<(), InvalidInputError> {
if dimensions.len() != 9 {
let message = format!("Expected dimensions length 9, found: {}", dimensions.len());
return Err(InvalidInputError::new(message));
}
let mut chars = dimensions.chars();
for a in &[CoordPos::Inside, CoordPos::OnBoundary, CoordPos::Outside] {
for b in &[CoordPos::Inside, CoordPos::OnBoundary, CoordPos::Outside] {
match chars.next().expect("already validated length is 9") {
'0' => self.0[*a][*b] = self.0[*a][*b].max(Dimensions::ZeroDimensional),
'1' => self.0[*a][*b] = self.0[*a][*b].max(Dimensions::OneDimensional),
'2' => self.0[*a][*b] = self.0[*a][*b].max(Dimensions::TwoDimensional),
'F' => {}
other => {
let message = format!("expected '0', '1', '2', or 'F'. Found: {}", other);
return Err(InvalidInputError::new(message));
}
}
}
}
Ok(())
}
pub fn is_disjoint(&self) -> bool {
self.0[CoordPos::Inside][CoordPos::Inside] == Dimensions::Empty
&& self.0[CoordPos::Inside][CoordPos::OnBoundary] == Dimensions::Empty
&& self.0[CoordPos::OnBoundary][CoordPos::Inside] == Dimensions::Empty
&& self.0[CoordPos::OnBoundary][CoordPos::OnBoundary] == Dimensions::Empty
}
pub fn is_intersects(&self) -> bool {
!self.is_disjoint()
}
pub fn is_within(&self) -> bool {
self.0[CoordPos::Inside][CoordPos::Inside] != Dimensions::Empty
&& self.0[CoordPos::Inside][CoordPos::Outside] == Dimensions::Empty
&& self.0[CoordPos::OnBoundary][CoordPos::Outside] == Dimensions::Empty
}
pub fn is_contains(&self) -> bool {
self.0[CoordPos::Inside][CoordPos::Inside] != Dimensions::Empty
&& self.0[CoordPos::Outside][CoordPos::Inside] == Dimensions::Empty
&& self.0[CoordPos::Outside][CoordPos::OnBoundary] == Dimensions::Empty
}
pub fn get(&self, lhs: CoordPos, rhs: CoordPos) -> Dimensions {
self.0[lhs][rhs]
}
}
impl std::str::FromStr for IntersectionMatrix {
type Err = InvalidInputError;
fn from_str(str: &str) -> Result<Self, Self::Err> {
let mut im = IntersectionMatrix::empty();
im.set_at_least_from_string(str)?;
Ok(im)
}
}