use core::cmp::{Ordering, PartialOrd};
use core::fmt::Debug;
use core::hash::Hash;
use core::marker::PhantomData;
use core::ops::{Add, AddAssign, Deref, DerefMut, Sub, SubAssign};
use crate::direction::Direction;
use crate::range::ComponentRange;
use crate::vector::{Columns, Component as VecComponent, Rows, Vector, VectorLike};
pub trait Component: Sized + From<isize> + Copy + Debug + Ord + Eq + Hash + Default {
type Converse: Component<Converse = Self>;
type Distance: VecComponent<Point = Self>;
#[must_use]
fn from_location<L: LocationLike>(location: L) -> Self;
#[must_use]
fn combine(self, other: Self::Converse) -> Location;
#[must_use]
fn name() -> &'static str;
#[must_use]
fn value(self) -> isize;
#[must_use]
fn add_distance(self, amount: impl Into<Self::Distance>) -> Self;
#[must_use]
#[inline(always)]
fn distance_to(self, target: Self) -> Self::Distance {
target.distance_from(self)
}
#[must_use]
fn distance_from(self, origin: Self) -> Self::Distance;
#[must_use]
#[inline]
fn transpose(self) -> Self::Converse {
self.value().into()
}
#[must_use]
#[inline]
fn span(self, length: Self::Distance) -> ComponentRange<Self> {
ComponentRange::span(self, length)
}
#[must_use]
#[inline]
fn range_to(self, end: Self) -> ComponentRange<Self> {
ComponentRange::bounded(self, end)
}
}
macro_rules! make_component {
(
$Name:ident,
$Converse:ident,
$Distance:ident,
$lower_name:ident,
$lower_converse:ident,
$name:literal,
$test:ident
) => {
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
#[doc = "A "]
#[doc = $name]
#[doc = " component of a [`Location`]. See [`Component`] for details."]
pub struct $Name(pub isize);
impl Add<$Converse> for $Name {
type Output = Location;
#[must_use]
#[inline]
fn add(self, rhs: $Converse) -> Location {
self.combine(rhs)
}
}
impl Add<$Distance> for $Name {
type Output = Self;
#[must_use]
#[inline]
fn add(self, rhs: $Distance) -> Self {
$Name(self.0 + rhs.0)
}
}
impl AddAssign<$Distance> for $Name {
#[inline]
fn add_assign(&mut self, rhs: $Distance) {
self.0 += rhs.0
}
}
impl Sub<$Distance> for $Name {
type Output = Self;
#[must_use]
#[inline]
fn sub(self, rhs: $Distance) -> Self {
$Name(self.0 - rhs.0)
}
}
impl SubAssign<$Distance> for $Name {
#[inline]
fn sub_assign(&mut self, rhs: $Distance) {
self.0 -= rhs.0
}
}
impl Sub<$Name> for $Name {
type Output = $Distance;
#[must_use]
#[inline]
fn sub(self, rhs: Self) -> $Distance {
$Distance(self.0 - rhs.0)
}
}
impl From<isize> for $Name {
#[must_use]
#[inline]
fn from(value: isize) -> Self {
$Name(value)
}
}
impl LocationLike for ($Name, $Converse) {
#[inline]
#[must_use]
fn $lower_name(&self) -> $Name {
self.0
}
#[inline]
#[must_use]
fn $lower_converse(&self) -> $Converse {
self.1
}
#[inline]
#[must_use]
fn as_location(&self) -> Location {
self.0.combine(self.1)
}
}
impl Component for $Name {
type Converse = $Converse;
type Distance = $Distance;
#[inline]
fn from_location<L: LocationLike>(location: L) -> Self {
location.$lower_name()
}
#[inline]
fn combine(self, other: $Converse) -> Location {
Location {
$lower_name: self,
$lower_converse: other,
}
}
#[inline(always)]
fn name() -> &'static str {
stringify!($lower_name)
}
#[inline]
fn add_distance(self, distance: impl Into<$Distance>) -> Self {
self + distance.into()
}
#[inline]
fn distance_from(self, origin: Self) -> $Distance {
self - origin
}
#[inline]
fn value(self) -> isize {
self.0
}
}
#[cfg(test)]
mod $test {
use crate::location::{Location, $Name, $Converse, Component};
use crate::vector::{$Distance};
#[test]
fn test_combine_converse() {
let base = $Name(3);
let converse = $Converse(4);
assert_eq!(base.combine(converse), Location{
$lower_name: base,
$lower_converse: converse,
});
}
#[test]
fn test_add_converse() {
let base = $Name(3);
let converse = $Converse(4);
assert_eq!(base + converse, base.combine(converse));
}
#[test]
fn test_add_distance() {
let base = $Name(3);
let distance = $Distance(4);
assert_eq!(base + distance, $Name(7));
}
#[test]
fn test_add_assign() {
let mut base = $Name(3);
base += $Distance(4);
assert_eq!(base, $Name(7));
}
#[test]
fn test_sub_distance() {
let base = $Name(3);
let distance = $Distance(4);
assert_eq!(base - distance, $Name(-1));
}
#[test]
fn test_sub_assign() {
let mut base = $Name(3);
base -= $Distance(4);
assert_eq!(base, $Name(-1));
}
#[test]
fn test_sub_self() {
let origin = $Name(2);
let remote = $Name(5);
assert_eq!(remote - origin, $Distance(3));
}
}
}
}
make_component! {Row, Column, Rows, row, column, "row", test_row}
make_component! {Column, Row, Columns, column, row, "column", test_column}
#[derive(Debug, Clone, Copy, Default, Hash, Eq)]
pub struct Location {
pub row: Row,
pub column: Column,
}
impl Location {
#[inline]
#[must_use]
pub fn new(row: impl Into<Row>, column: impl Into<Column>) -> Self {
Location {
row: row.into(),
column: column.into(),
}
}
#[must_use]
#[inline]
pub const fn zero() -> Self {
Location {
row: Row(0),
column: Column(0),
}
}
}
pub trait LocationLike: Sized {
fn row(&self) -> Row;
fn column(&self) -> Column;
#[inline]
#[must_use]
fn as_location(&self) -> Location {
Location {
row: self.row(),
column: self.column(),
}
}
#[inline]
#[must_use]
fn get_component<T: Component>(&self) -> T {
T::from_location(self)
}
#[inline]
#[must_use]
fn above(&self, distance: impl Into<Rows>) -> Location {
Location {
row: self.row() - distance.into(),
column: self.column(),
}
}
#[inline]
#[must_use]
fn below(&self, distance: impl Into<Rows>) -> Location {
Location {
row: self.row() + distance.into(),
column: self.column(),
}
}
#[inline]
#[must_use]
fn left(&self, distance: impl Into<Columns>) -> Location {
Location {
row: self.row(),
column: self.column() - distance.into(),
}
}
#[inline]
#[must_use]
fn right(&self, distance: impl Into<Columns>) -> Location {
Location {
row: self.row(),
column: self.column() + distance.into(),
}
}
#[inline]
#[must_use]
fn add(&self, distance: impl VectorLike) -> Location {
self.as_location() + distance
}
#[inline]
#[must_use]
fn relative(&self, direction: Direction, distance: isize) -> Location {
self.add(direction.sized_vec(distance))
}
#[inline]
#[must_use]
fn step(&self, direction: Direction) -> Location {
self.add(direction.unit_vec())
}
#[inline]
#[must_use]
fn transpose(&self) -> Location {
Location {
row: self.column().transpose(),
column: self.row().transpose(),
}
}
#[inline]
#[must_use]
fn order_by<Major: Component>(self) -> Ordered<Self, Major> {
self.into()
}
#[inline]
#[must_use]
fn row_ordered(self) -> RowOrdered<Self> {
self.order_by()
}
#[inline]
#[must_use]
fn column_ordered(self) -> ColumnOrdered<Self> {
self.order_by()
}
}
impl LocationLike for Location {
#[inline(always)]
#[must_use]
fn row(&self) -> Row {
self.row
}
#[inline(always)]
#[must_use]
fn column(&self) -> Column {
self.column
}
#[inline(always)]
#[must_use]
fn as_location(&self) -> Location {
*self
}
}
impl<T: LocationLike> LocationLike for &T {
#[inline(always)]
#[must_use]
fn row(&self) -> Row {
T::row(self)
}
#[inline(always)]
#[must_use]
fn column(&self) -> Column {
T::column(self)
}
#[inline(always)]
#[must_use]
fn as_location(&self) -> Location {
T::as_location(self)
}
}
impl<T: VectorLike> Add<T> for Location {
type Output = Location;
#[inline]
#[must_use]
fn add(self, rhs: T) -> Location {
let rhs = rhs.as_vector();
Location {
row: self.row + rhs.rows,
column: self.column + rhs.columns,
}
}
}
#[cfg(test)]
#[test]
fn test_add() {
use crate::direction::*;
assert_eq!(
Location::new(3, 5) + Vector::new(-1, 6),
Location::new(2, 11)
);
assert_eq!(Location::zero() + Rows(5), Location::new(5, 0));
assert_eq!(Location::zero() + Columns(-2), Location::new(0, -2));
assert_eq!(Location::zero() + (2, 3), Location::new(2, 3));
assert_eq!(
Location::zero() + (Rows(1), Columns(1)),
Location::new(1, 1)
);
assert_eq!(
Location::zero() + (Columns(4), Rows(4)),
Location::new(4, 4)
);
assert_eq!(Location::zero() + Up, Location::new(-1, 0));
}
impl<T: VectorLike> AddAssign<T> for Location {
#[inline]
fn add_assign(&mut self, rhs: T) {
let rhs = rhs.as_vector();
self.row += rhs.rows;
self.column += rhs.columns;
}
}
#[cfg(test)]
#[test]
fn test_add_assign() {
let mut loc = Location::zero();
loc += Vector::new(-2, 5);
assert_eq!(loc, Location::new(-2, 5));
loc += Rows(4);
assert_eq!(loc, Location::new(2, 5));
loc += Columns(5);
assert_eq!(loc, Location::new(2, 10));
}
impl<T: VectorLike> Sub<T> for Location {
type Output = Location;
#[inline]
#[must_use]
fn sub(self, rhs: T) -> Location {
let rhs = rhs.as_vector();
Location {
row: self.row - rhs.rows,
column: self.column - rhs.columns,
}
}
}
#[cfg(test)]
#[test]
fn test_sub() {
assert_eq!(
Location::new(3, 5) - Vector::new(-1, 6),
Location::new(4, -1)
);
assert_eq!(Location::zero() - Rows(5), Location::new(-5, 0));
assert_eq!(Location::zero() - Columns(-2), Location::new(0, 2));
assert_eq!(Location::zero() - (2, 3), Location::new(-2, -3));
assert_eq!(
Location::zero() - (Rows(1), Columns(1)),
Location::new(-1, -1)
);
assert_eq!(
Location::zero() - (Columns(4), Rows(4)),
Location::new(-4, -4)
);
}
impl<T: VectorLike> SubAssign<T> for Location {
#[inline]
fn sub_assign(&mut self, rhs: T) {
let rhs = rhs.as_vector();
self.row -= rhs.rows;
self.column -= rhs.columns;
}
}
#[cfg(test)]
#[test]
fn test_sub_assign() {
let mut loc = Location::zero();
loc -= Vector::new(-2, 5);
assert_eq!(loc, Location::new(2, -5));
loc -= Rows(4);
assert_eq!(loc, Location::new(-2, -5));
loc -= Columns(5);
assert_eq!(loc, Location::new(-2, -10));
}
impl Sub<Location> for Location {
type Output = Vector;
#[inline]
#[must_use]
fn sub(self, rhs: Location) -> Vector {
Vector {
rows: self.row - rhs.row,
columns: self.column - rhs.column,
}
}
}
impl Sub<(Row, Column)> for Location {
type Output = Vector;
#[inline]
#[must_use]
fn sub(self, (row, column): (Row, Column)) -> Vector {
Vector {
rows: self.row - row,
columns: self.column - column,
}
}
}
impl Sub<(Column, Row)> for Location {
type Output = Vector;
#[inline]
#[must_use]
fn sub(self, (column, row): (Column, Row)) -> Vector {
Vector {
rows: self.row - row,
columns: self.column - column,
}
}
}
#[cfg(test)]
#[test]
fn test_sub_self() {
let loc1 = Location::new(4, 5);
let loc2 = Location::new(1, 1);
assert_eq!(loc1 - loc2, Vector::new(3, 4));
}
#[cfg(test)]
#[test]
fn test_sub_self_tuple() {
let loc1 = Location::new(4, 5);
let loc2 = (Row(1), Column(2));
assert_eq!(loc1 - loc2, Vector::new(3, 3));
}
#[cfg(test)]
#[test]
fn test_sub_self_reverse_tuple() {
let loc1 = Location::new(4, 5);
let loc2 = (Column(2), Row(1));
assert_eq!(loc1 - loc2, Vector::new(3, 3));
}
impl<T: LocationLike> PartialEq<T> for Location {
#[must_use]
#[inline]
fn eq(&self, rhs: &T) -> bool {
self.row == rhs.row() && self.column == rhs.column()
}
}
impl<T: LocationLike> PartialOrd<T> for Location {
#[must_use]
fn partial_cmp(&self, rhs: &T) -> Option<Ordering> {
match (self.row.cmp(&rhs.row()), self.column.cmp(&rhs.column())) {
(Ordering::Greater, Ordering::Less) | (Ordering::Less, Ordering::Greater) => None,
(o1, o2) => Some(o1.then(o2)),
}
}
}
#[cfg(test)]
mod partial_ord_tests {
use crate::prelude::{Location, LocationLike};
use crate::shorthand::{C, L, R};
use core::cmp::Ordering;
const ZERO: Location = Location::zero();
#[test]
fn test_eq() {
assert_eq!(L(3, 4), (R(3), C(4)));
assert_eq!(L(3, 4), (C(4), R(3)));
assert_eq!(L(3, 4), L(3, 4));
assert_eq!(L(3, 4), (3, 4));
}
#[test]
fn test_orderliness() {
assert_eq!(ZERO.partial_cmp(&ZERO.above(1)), Some(Ordering::Greater));
assert_eq!(ZERO.partial_cmp(&ZERO.left(1)), Some(Ordering::Greater));
assert_eq!(ZERO.partial_cmp(&(ZERO - (1, 1))), Some(Ordering::Greater));
assert_eq!(ZERO.partial_cmp(&ZERO.below(1)), Some(Ordering::Less));
assert_eq!(ZERO.partial_cmp(&ZERO.right(1)), Some(Ordering::Less));
assert_eq!(ZERO.partial_cmp(&(ZERO + (1, 1))), Some(Ordering::Less));
assert_eq!(ZERO.partial_cmp(&(ZERO + (-1, 1))), None);
assert_eq!(ZERO.partial_cmp(&(ZERO - (-1, 1))), None);
assert_eq!(ZERO.partial_cmp(&ZERO), Some(Ordering::Equal));
}
#[test]
fn test_bad_diagonal() {
for location in &[L(1, -1), L(-1, 1)] {
assert!(!(ZERO < *location));
assert!(!(ZERO > *location));
assert!(!(ZERO <= *location));
assert!(!(ZERO >= *location));
}
}
}
impl LocationLike for (isize, isize) {
#[inline]
#[must_use]
fn row(&self) -> Row {
Row(self.0)
}
#[inline]
#[must_use]
fn column(&self) -> Column {
Column(self.1)
}
#[inline]
#[must_use]
fn as_location(&self) -> Location {
Location::new(self.0, self.1)
}
}
#[derive(Debug, Clone, Copy, Default, Hash)]
pub struct Ordered<L: LocationLike, Major: Component> {
pub location: L,
phantom: PhantomData<Major>,
}
impl<L: LocationLike, M: Component> Ordered<L, M> {
#[inline]
#[must_use]
pub fn new(location: L) -> Self {
Self {
location,
phantom: PhantomData,
}
}
}
impl<L: LocationLike, M: Component> From<L> for Ordered<L, M> {
#[inline]
#[must_use]
fn from(location: L) -> Self {
Self::new(location)
}
}
impl<L: LocationLike, M: Component> AsRef<L> for Ordered<L, M> {
#[inline]
#[must_use]
fn as_ref(&self) -> &L {
&self.location
}
}
impl<L: LocationLike, M: Component> AsMut<L> for Ordered<L, M> {
#[inline]
#[must_use]
fn as_mut(&mut self) -> &mut L {
&mut self.location
}
}
impl<L: LocationLike, M: Component> Deref for Ordered<L, M> {
type Target = L;
#[inline]
#[must_use]
fn deref(&self) -> &L {
&self.location
}
}
impl<L: LocationLike, M: Component> DerefMut for Ordered<L, M> {
#[inline]
#[must_use]
fn deref_mut(&mut self) -> &mut L {
&mut self.location
}
}
impl<L: LocationLike, M: Component> LocationLike for Ordered<L, M> {
#[inline]
#[must_use]
fn row(&self) -> Row {
self.location.row()
}
#[inline]
#[must_use]
fn column(&self) -> Column {
self.location.column()
}
#[inline]
#[must_use]
fn as_location(&self) -> Location {
self.location.as_location()
}
}
impl<L: LocationLike, M: Component, R: LocationLike> PartialEq<R> for Ordered<L, M> {
#[inline]
#[must_use]
fn eq(&self, rhs: &R) -> bool {
self.as_location() == rhs.as_location()
}
}
impl<L: LocationLike, M: Component> Eq for Ordered<L, M> {}
impl<L: LocationLike, M: Component> PartialOrd for Ordered<L, M> {
#[inline]
#[must_use]
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(self.cmp(rhs))
}
#[inline]
#[must_use]
fn lt(&self, rhs: &Self) -> bool {
self.cmp(rhs) == Ordering::Less
}
#[inline]
#[must_use]
fn le(&self, rhs: &Self) -> bool {
self.cmp(rhs) != Ordering::Greater
}
#[inline]
#[must_use]
fn gt(&self, rhs: &Self) -> bool {
self.cmp(rhs) == Ordering::Greater
}
#[inline]
#[must_use]
fn ge(&self, rhs: &Self) -> bool {
self.cmp(rhs) != Ordering::Less
}
}
impl<L: LocationLike, M: Component> Ord for Ordered<L, M> {
fn cmp(&self, rhs: &Self) -> Ordering {
M::from_location(self)
.cmp(&M::from_location(rhs))
.then_with(move || {
M::Converse::from_location(self).cmp(&M::Converse::from_location(rhs))
})
}
}
pub type RowOrdered<L> = Ordered<L, Row>;
pub type ColumnOrdered<L> = Ordered<L, Column>;
pub type RowOrderedLocation = RowOrdered<Location>;
pub type ColumnOrderedLocation = ColumnOrdered<Location>;