gridly/
location.rs

1//! [`Location`][crate::location::Location] type used to index into grids,
2//! as well as associated types and traits.
3
4use core::cmp::{Ordering, PartialOrd};
5use core::fmt::Debug;
6use core::hash::Hash;
7use core::marker::PhantomData;
8use core::ops::{Add, AddAssign, Deref, DerefMut, Sub, SubAssign};
9
10use crate::direction::Direction;
11use crate::range::{ComponentRange, LocationRange};
12use crate::vector::{Columns, Component as VecComponent, Rows, Vector, VectorLike};
13
14// TODO: add additional implied traits?
15// TODO: docstrings
16
17/// A component of a [`Location`], which may be either a [`Row`] or a
18/// [`Column`]. It is effectively an index into a given row or column of a
19/// grid; for instance, a [`Row`] can index a row in a grid.
20///
21/// In practice, most code will call methods directly on [`Row`] or [`Column`]
22/// values. However, a lot of gridly functionality is agnostic towards
23/// rows and columns (for instance, a view over a row in a grid is functionally
24/// the same as a view over a column), so the `Component` trait allows such
25/// functionality to be written generically.
26///
27/// The key methods for [`Component`] that allow it to work in generic contexts
28/// are [`from_location`][Component::from_location], which gets a component
29/// from a [`Location`], and [`combine`][Component::combine], which combines a
30/// [`Row`] or [`Column`] with its converse (a [`Column`] or a [`Row`]) to
31/// create a new [`Location`].
32pub trait Component: Sized + From<isize> + Copy + Debug + Ord + Eq + Hash + Default {
33    /// The converse component ([`Row`] to [`Column`], or vice versa)
34    type Converse: Component<Converse = Self>;
35
36    /// The associated vector component ([`Rows`] or [`Columns`])
37    type Distance: VecComponent<Point = Self>;
38
39    /// Get this component type from a [`Location`].
40    ///
41    /// Example:
42    ///
43    /// ```
44    /// use gridly::prelude::*;
45    ///
46    /// let loc = Location::new(2, 5);
47    /// assert_eq!(Row::from_location(&loc), Row(2));
48    /// assert_eq!(Column::from_location(&loc), Column(5));
49    /// ```
50    #[must_use]
51    fn from_location<L: LocationLike>(location: L) -> Self;
52
53    /// Combine this component with its converse to create a [`Location`]. Note
54    /// that `Row` and `Column` can simply be added together with `+` to create
55    /// a new `Location`; this method exists to assist with generic code.
56    ///
57    /// Example:
58    ///
59    /// ```
60    /// use gridly::prelude::*;
61    ///
62    /// let loc = Row(2).combine(Column(5));
63    /// assert_eq!(loc, Location::new(2, 5));
64    /// assert_eq!(loc, Row(2) + Column(5));
65    /// ```
66    #[must_use]
67    fn combine(self, other: Self::Converse) -> Location;
68
69    /// Return the lowercase name of this component type– "row" or "column".
70    /// Intended for debug printing, error messages, etc.
71    ///
72    /// ```
73    /// use gridly::prelude::*;
74    ///
75    /// assert_eq!(Row::name(), "row");
76    /// assert_eq!(Column::name(), "column");
77    /// ```
78    #[must_use]
79    fn name() -> &'static str;
80
81    /// Get the integer value of this component
82    ///
83    /// ```
84    /// use gridly::prelude::*;
85    ///
86    /// assert_eq!(Row(5).value(), 5);
87    /// ```
88    #[must_use]
89    fn value(self) -> isize;
90
91    /// Add a distance to this component. This method is provided because we can't
92    /// require a trait bound on `Add` for `Component`, but in general just using
93    /// `+` is preferable.
94    ///
95    /// ```
96    /// use gridly::prelude::*;
97    ///
98    /// assert_eq!(Row(4).add_distance(Rows(5)), Row(9));
99    /// ```
100    #[must_use]
101    fn add_distance(self, amount: impl Into<Self::Distance>) -> Self;
102
103    /// Find the distance between two components, using this component as the origin
104    ///
105    /// ```
106    /// use gridly::prelude::*;
107    ///
108    /// assert_eq!(Row(3).distance_to(Row(8)), Rows(5));
109    /// ```
110    #[must_use]
111    #[inline(always)]
112    fn distance_to(self, target: Self) -> Self::Distance {
113        target.distance_from(self)
114    }
115
116    /// Find the distance between two components, using the other component as the origin
117    ///
118    /// ```
119    /// use gridly::prelude::*;
120    ///
121    /// assert_eq!(Row(8).distance_from(Row(3)), Rows(5));
122    /// ```
123    #[must_use]
124    fn distance_from(self, origin: Self) -> Self::Distance;
125
126    /// Convert a Row into a Column or vice versa
127    ///
128    /// ```
129    /// use gridly::prelude::*;
130    ///
131    /// assert_eq!(Row(3).transpose(), Column(3));
132    /// ```
133    #[must_use]
134    #[inline]
135    fn transpose(self) -> Self::Converse {
136        self.value().into()
137    }
138
139    /// Create a range, starting at this component, with the given length
140    ///
141    /// ```
142    /// use gridly::prelude::*;
143    ///
144    /// assert_eq!(Row(-1).span(Rows(2)), RowRange::bounded(Row(-1), Row(1)));
145    /// ```
146    #[must_use]
147    #[inline]
148    fn span(self, length: Self::Distance) -> ComponentRange<Self> {
149        ComponentRange::span(self, length)
150    }
151
152    /// Create a range, starting at this component, ending at the given
153    /// component
154    ///
155    /// ```
156    /// use gridly::prelude::*;
157    ///
158    /// assert_eq!(Row(10).range_to(Row(20)), RowRange::span(Row(10), Rows(10)));
159    /// ```
160    #[must_use]
161    #[inline]
162    fn range_to(self, end: Self) -> ComponentRange<Self> {
163        ComponentRange::bounded(self, end)
164    }
165}
166
167// TODO: TryFrom<usize>, once it's stable
168
169// TODO: add docstrings to these. Perhaps refer back to Component
170macro_rules! make_component {
171    (
172        // The component type (Row or Column)
173        $Name:ident,
174
175        // The converse type (Column or Row)
176        $Converse:ident,
177
178        // The equivelent distance type (Rows or Columns)
179        $Distance:ident,
180
181        // The field to use when getting from a location
182        $lower_name:ident,
183
184        // The converse field when getting from a location
185        $lower_converse:ident,
186
187        // The string name of the field, to use in docstrings
188        $name:literal,
189
190        // The name of the module in which to place unit tests
191        $test:ident
192    ) => {
193        #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
194        #[repr(transparent)]
195        #[doc = "A "]
196        #[doc = $name]
197        #[doc = " component of a [`Location`]. See [`Component`] for details."]
198        pub struct $Name(pub isize);
199
200        /// Adding a component to its converse (ie, a [`Row`] to a [`Column`])
201        /// produces a [`Location`]
202        impl Add<$Converse> for $Name {
203            type Output = Location;
204
205            #[must_use]
206            #[inline]
207            fn add(self, rhs: $Converse) -> Location {
208                self.combine(rhs)
209            }
210        }
211
212        impl Add<$Distance> for $Name {
213            type Output = Self;
214
215            #[must_use]
216            #[inline]
217            fn add(self, rhs: $Distance) -> Self {
218                $Name(self.0 + rhs.0)
219            }
220        }
221
222        impl AddAssign<$Distance> for $Name {
223            #[inline]
224            fn add_assign(&mut self, rhs: $Distance) {
225                self.0 += rhs.0
226            }
227        }
228
229        impl Sub<$Distance> for $Name {
230            type Output = Self;
231
232            #[must_use]
233            #[inline]
234            fn sub(self, rhs: $Distance) -> Self {
235                $Name(self.0 - rhs.0)
236            }
237        }
238
239        impl SubAssign<$Distance> for $Name {
240            #[inline]
241            fn sub_assign(&mut self, rhs: $Distance) {
242                self.0 -= rhs.0
243            }
244        }
245
246        /// The difference between two location components is the distance between them
247        impl Sub<$Name> for $Name {
248            type Output = $Distance;
249
250            #[must_use]
251            #[inline]
252            fn sub(self, rhs: Self) -> $Distance {
253                $Distance(self.0 - rhs.0)
254            }
255        }
256
257        impl From<isize> for $Name {
258            #[must_use]
259            #[inline]
260            fn from(value: isize) -> Self {
261                $Name(value)
262            }
263        }
264
265        impl LocationLike for ($Name, $Converse) {
266            #[inline]
267            #[must_use]
268            fn $lower_name(&self) -> $Name {
269                self.0
270            }
271
272            #[inline]
273            #[must_use]
274            fn $lower_converse(&self) -> $Converse {
275                self.1
276            }
277
278            #[inline]
279            #[must_use]
280            fn as_location(&self) -> Location {
281                self.0.combine(self.1)
282            }
283        }
284
285        impl Component for $Name {
286            type Converse = $Converse;
287            type Distance = $Distance;
288
289            #[inline]
290            fn from_location<L: LocationLike>(location: L) -> Self {
291                location.$lower_name()
292            }
293
294            #[inline]
295            fn combine(self, other: $Converse) -> Location {
296                Location {
297                    $lower_name: self,
298                    $lower_converse: other,
299                }
300            }
301
302            #[inline(always)]
303            fn name() -> &'static str {
304                stringify!($lower_name)
305            }
306
307            #[inline]
308            fn add_distance(self, distance: impl Into<$Distance>) -> Self {
309                self + distance.into()
310            }
311
312            #[inline]
313            fn distance_from(self, origin: Self) -> $Distance {
314                self - origin
315            }
316
317            #[inline]
318            fn value(self) -> isize {
319                self.0
320            }
321        }
322
323        #[cfg(test)]
324        mod $test {
325            use crate::location::{$Converse, $Name, Component, Location};
326            use crate::vector::$Distance;
327
328            #[test]
329            fn test_combine_converse() {
330                let base = $Name(3);
331                let converse = $Converse(4);
332
333                assert_eq!(
334                    base.combine(converse),
335                    Location {
336                        $lower_name: base,
337                        $lower_converse: converse,
338                    }
339                );
340            }
341
342            #[test]
343            fn test_add_converse() {
344                let base = $Name(3);
345                let converse = $Converse(4);
346
347                assert_eq!(base + converse, base.combine(converse));
348            }
349
350            #[test]
351            fn test_add_distance() {
352                let base = $Name(3);
353                let distance = $Distance(4);
354
355                assert_eq!(base + distance, $Name(7));
356            }
357
358            #[test]
359            fn test_add_assign() {
360                let mut base = $Name(3);
361                base += $Distance(4);
362
363                assert_eq!(base, $Name(7));
364            }
365
366            #[test]
367            fn test_sub_distance() {
368                let base = $Name(3);
369                let distance = $Distance(4);
370
371                assert_eq!(base - distance, $Name(-1));
372            }
373
374            #[test]
375            fn test_sub_assign() {
376                let mut base = $Name(3);
377                base -= $Distance(4);
378
379                assert_eq!(base, $Name(-1));
380            }
381
382            #[test]
383            fn test_sub_self() {
384                let origin = $Name(2);
385                let remote = $Name(5);
386
387                assert_eq!(remote - origin, $Distance(3));
388            }
389        }
390    };
391}
392
393make_component! {Row, Column, Rows, row, column, "row", test_row}
394make_component! {Column, Row, Columns, column, row, "column", test_column}
395
396/// A location on a grid. A location is the primary indexing type for a
397/// Grid, and represents a single cell on that grid. It is comprised of a
398/// [`Row`] and a [`Column`]. Increasing row count downward and increasing
399/// columns count rightward.
400///
401/// Locations support arithmetic operations with [`Vector`]s. They can also be
402/// subtracted from each other to produce [`Vector`]s measuring the distance
403/// between them.
404#[derive(Debug, Clone, Copy, Default, Hash, Eq)]
405pub struct Location {
406    pub row: Row,
407    pub column: Column,
408}
409
410impl Location {
411    /// Create a new location out of a `row` and a `column`
412    #[inline]
413    #[must_use]
414    pub fn new(row: impl Into<Row>, column: impl Into<Column>) -> Self {
415        Location {
416            row: row.into(),
417            column: column.into(),
418        }
419    }
420
421    /// Create a new location at `(0, 0)`.
422    #[must_use]
423    #[inline]
424    pub const fn zero() -> Self {
425        Location {
426            row: Row(0),
427            column: Column(0),
428        }
429    }
430}
431
432/// This trait covers structs that act like a [`Location`], such as tuples.
433/// See the [`Location`] documentation for more details.
434pub trait LocationLike: Sized {
435    /// Get the row of this location.
436    fn row(&self) -> Row;
437
438    /// Get the column of this location.
439    fn column(&self) -> Column;
440
441    /// Convert this object into a [`Location`] struct.
442    #[inline]
443    #[must_use]
444    fn as_location(&self) -> Location {
445        Location {
446            row: self.row(),
447            column: self.column(),
448        }
449    }
450
451    /// Get either the row or column of a location. This method is useful in
452    /// code that is generic over the Row or Column.
453    #[inline]
454    #[must_use]
455    fn get_component<T: Component>(&self) -> T {
456        T::from_location(self)
457    }
458
459    /// Return the location that is `distance` rows above this one
460    ///
461    /// ```
462    /// use gridly::prelude::*;
463    /// use gridly::shorthand::*;
464    ///
465    /// assert_eq!(L(3, 4).above(2), L(1, 4));
466    /// ```
467    #[inline]
468    #[must_use]
469    fn above(&self, distance: impl Into<Rows>) -> Location {
470        Location {
471            row: self.row() - distance.into(),
472            column: self.column(),
473        }
474    }
475
476    /// Return the location that is `distance` rows below this one
477    ///
478    /// ```
479    /// use gridly::prelude::*;
480    /// use gridly::shorthand::*;
481    ///
482    /// assert_eq!(L(3, 4).below(2), L(5, 4));
483    /// ```
484    #[inline]
485    #[must_use]
486    fn below(&self, distance: impl Into<Rows>) -> Location {
487        Location {
488            row: self.row() + distance.into(),
489            column: self.column(),
490        }
491    }
492
493    /// Return the location that is `distance` columns to the left of this one
494    ///
495    /// ```
496    /// use gridly::prelude::*;
497    /// use gridly::shorthand::*;
498    ///
499    /// assert_eq!(L(3, 4).left(2), L(3, 2));
500    /// ```
501    #[inline]
502    #[must_use]
503    fn left(&self, distance: impl Into<Columns>) -> Location {
504        Location {
505            row: self.row(),
506            column: self.column() - distance.into(),
507        }
508    }
509
510    /// Return the location that is `distance` columns to the right of this one
511    ///
512    /// ```
513    /// use gridly::prelude::*;
514    /// use gridly::shorthand::*;
515    ///
516    /// assert_eq!(L(3, 4).right(2), L(3, 6));
517    /// ```
518    #[inline]
519    #[must_use]
520    fn right(&self, distance: impl Into<Columns>) -> Location {
521        Location {
522            row: self.row(),
523            column: self.column() + distance.into(),
524        }
525    }
526
527    /// Return the location that is `distance` away from this one.
528    #[inline]
529    #[must_use]
530    fn add(&self, distance: impl VectorLike) -> Location {
531        self.as_location() + distance
532    }
533
534    /// Return the location that is `distance` away in the given `direction`
535    ///
536    /// ```
537    /// use gridly::prelude::*;
538    /// use gridly::shorthand::*;
539    ///
540    /// assert_eq!(Location::zero().relative(Up, 4), L(-4, 0));
541    /// assert_eq!(Location::zero().relative(Down, 5), L(5, 0));
542    /// assert_eq!(Location::zero().relative(Left, 3), L(0, -3));
543    /// assert_eq!(Location::zero().relative(Right, 1), L(0, 1));
544    /// ```
545    #[inline]
546    #[must_use]
547    fn relative(&self, direction: Direction, distance: isize) -> Location {
548        self.add(direction.sized_vec(distance))
549    }
550
551    /// Return the location that is 1 away in the given `direction`
552    ///
553    /// ```
554    /// use gridly::prelude::*;
555    /// use gridly::shorthand::*;
556    ///
557    /// let base = Location::new(2, 4);
558    /// assert_eq!(base.step(Up), L(1, 4));
559    /// assert_eq!(base.step(Down), L(3, 4));
560    /// assert_eq!(base.step(Left), L(2, 3));
561    /// assert_eq!(base.step(Right), L(2, 5));
562    /// ```
563    #[inline]
564    #[must_use]
565    fn step(&self, direction: Direction) -> Location {
566        self.add(direction.unit_vec())
567    }
568
569    /// Swap the row and colimn of this Location
570    ///
571    /// Example:
572    ///
573    /// ```
574    /// use gridly::prelude::*;
575    /// use gridly::shorthand::*;
576    ///
577    /// assert_eq!(L(5, 8).transpose(), L(8, 5));
578    /// ```
579    #[inline]
580    #[must_use]
581    fn transpose(&self) -> Location {
582        Location {
583            row: self.column().transpose(),
584            column: self.row().transpose(),
585        }
586    }
587
588    /// Generically get strictly ordered version of this `Location`. The `Major`
589    /// is the ordering; for example, `order_by::<Row>` will create a row-ordered
590    /// [`Location`]. See [`row_ordered`][LocationLike::row_ordered] or
591    /// [`column_ordered`][LocationLike::column_ordered] for an example.
592    #[inline]
593    #[must_use]
594    fn order_by<Major: Component>(self) -> Ordered<Self, Major> {
595        self.into()
596    }
597
598    /// Get a strictly row-ordered version of this `Location`; that is, a
599    /// location which is ordered by comparing the `row`, then the `column`.
600    ///
601    /// Example:
602    ///
603    /// ```
604    /// use gridly::prelude::*;
605    /// use gridly::shorthand::*;
606    ///
607    /// let l0 = L(0, 0).row_ordered();
608    /// let l1 = L(0, 1).row_ordered();
609    /// let l2 = L(1, 0).row_ordered();
610    /// let l3 = L(1, 1).row_ordered();
611    ///
612    /// assert!(l0 < l1 && l0 < l2 && l0 < l3);
613    /// assert!(l1 < l2 && l1 < l3);
614    /// assert!(l2 < l3);
615    /// ```
616    #[inline]
617    #[must_use]
618    fn row_ordered(self) -> RowOrdered<Self> {
619        self.order_by()
620    }
621
622    /// Get a strictly row-ordered version of this `Location`; that is, a
623    /// location which is ordered by comparing the `row`, then the `column`.
624    ///
625    /// Example:
626    ///
627    /// ```
628    ///
629    /// use gridly::prelude::*;
630    /// use gridly::shorthand::*;
631    ///
632    /// let l0 = L(0, 0).column_ordered();
633    /// let l1 = L(1, 0).column_ordered();
634    /// let l2 = L(0, 1).column_ordered();
635    /// let l3 = L(1, 1).column_ordered();
636    ///
637    /// assert!(l0 < l1 && l0 < l2 && l0 < l3);
638    /// assert!(l1 < l2 && l1 < l3);
639    /// assert!(l2 < l3);
640    ///
641    /// ```
642    #[inline]
643    #[must_use]
644    fn column_ordered(self) -> ColumnOrdered<Self> {
645        self.order_by()
646    }
647
648    /// Create a range, starting at this location, with the given length
649    ///
650    /// ```
651    /// use gridly::prelude::*;
652    /// use gridly::shorthand::*;
653    ///
654    /// let location = L(1, 2);
655    /// let range = location.span_over(Rows(3));
656    ///
657    /// assert_eq!(range, LocationRange::bounded(Column(2), Row(1), Row(4)));
658    /// ```
659    #[inline]
660    #[must_use]
661    fn span_over<C: VecComponent>(
662        self,
663        distance: C,
664    ) -> LocationRange<<C::Point as Component>::Converse> {
665        LocationRange::rooted(self.as_location(), distance)
666    }
667
668    /// Create a range, starting at this component, ending at the given
669    /// component
670    ///
671    /// ```
672    /// use gridly::prelude::*;
673    /// use gridly::shorthand::*;
674    ///
675    /// let location = L(1, 2);
676    /// let range = location.range_to(Column(6));
677    ///
678    /// assert_eq!(range, LocationRange::bounded(Row(1), Column(2), Column(6)));
679    /// ```
680    #[inline]
681    #[must_use]
682    fn range_to<C: Component>(self, end: C) -> LocationRange<C::Converse> {
683        LocationRange::bounded(self.get_component(), self.get_component(), end)
684    }
685}
686
687impl LocationLike for Location {
688    #[inline(always)]
689    #[must_use]
690    fn row(&self) -> Row {
691        self.row
692    }
693
694    #[inline(always)]
695    #[must_use]
696    fn column(&self) -> Column {
697        self.column
698    }
699
700    #[inline(always)]
701    #[must_use]
702    fn as_location(&self) -> Location {
703        *self
704    }
705}
706
707impl<T: LocationLike> LocationLike for &T {
708    #[inline(always)]
709    #[must_use]
710    fn row(&self) -> Row {
711        T::row(self)
712    }
713
714    #[inline(always)]
715    #[must_use]
716    fn column(&self) -> Column {
717        T::column(self)
718    }
719
720    #[inline(always)]
721    #[must_use]
722    fn as_location(&self) -> Location {
723        T::as_location(self)
724    }
725}
726
727impl<T: VectorLike> Add<T> for Location {
728    type Output = Location;
729
730    #[inline]
731    #[must_use]
732    fn add(self, rhs: T) -> Location {
733        let rhs = rhs.as_vector();
734        Location {
735            row: self.row + rhs.rows,
736            column: self.column + rhs.columns,
737        }
738    }
739}
740
741#[cfg(test)]
742#[test]
743fn test_add() {
744    use crate::direction::*;
745
746    assert_eq!(
747        Location::new(3, 5) + Vector::new(-1, 6),
748        Location::new(2, 11)
749    );
750    assert_eq!(Location::zero() + Rows(5), Location::new(5, 0));
751    assert_eq!(Location::zero() + Columns(-2), Location::new(0, -2));
752    assert_eq!(Location::zero() + (2, 3), Location::new(2, 3));
753    assert_eq!(
754        Location::zero() + (Rows(1), Columns(1)),
755        Location::new(1, 1)
756    );
757    assert_eq!(
758        Location::zero() + (Columns(4), Rows(4)),
759        Location::new(4, 4)
760    );
761    assert_eq!(Location::zero() + Up, Location::new(-1, 0));
762}
763
764impl<T: VectorLike> AddAssign<T> for Location {
765    #[inline]
766    fn add_assign(&mut self, rhs: T) {
767        let rhs = rhs.as_vector();
768
769        self.row += rhs.rows;
770        self.column += rhs.columns;
771    }
772}
773
774#[cfg(test)]
775#[test]
776fn test_add_assign() {
777    let mut loc = Location::zero();
778
779    loc += Vector::new(-2, 5);
780    assert_eq!(loc, Location::new(-2, 5));
781
782    loc += Rows(4);
783    assert_eq!(loc, Location::new(2, 5));
784
785    loc += Columns(5);
786    assert_eq!(loc, Location::new(2, 10));
787}
788
789impl<T: VectorLike> Sub<T> for Location {
790    type Output = Location;
791
792    #[inline]
793    #[must_use]
794    fn sub(self, rhs: T) -> Location {
795        let rhs = rhs.as_vector();
796
797        Location {
798            row: self.row - rhs.rows,
799            column: self.column - rhs.columns,
800        }
801    }
802}
803
804#[cfg(test)]
805#[test]
806fn test_sub() {
807    assert_eq!(
808        Location::new(3, 5) - Vector::new(-1, 6),
809        Location::new(4, -1)
810    );
811    assert_eq!(Location::zero() - Rows(5), Location::new(-5, 0));
812    assert_eq!(Location::zero() - Columns(-2), Location::new(0, 2));
813    assert_eq!(Location::zero() - (2, 3), Location::new(-2, -3));
814    assert_eq!(
815        Location::zero() - (Rows(1), Columns(1)),
816        Location::new(-1, -1)
817    );
818    assert_eq!(
819        Location::zero() - (Columns(4), Rows(4)),
820        Location::new(-4, -4)
821    );
822}
823
824impl<T: VectorLike> SubAssign<T> for Location {
825    #[inline]
826    fn sub_assign(&mut self, rhs: T) {
827        let rhs = rhs.as_vector();
828        self.row -= rhs.rows;
829        self.column -= rhs.columns;
830    }
831}
832
833#[cfg(test)]
834#[test]
835fn test_sub_assign() {
836    let mut loc = Location::zero();
837
838    loc -= Vector::new(-2, 5);
839    assert_eq!(loc, Location::new(2, -5));
840
841    loc -= Rows(4);
842    assert_eq!(loc, Location::new(-2, -5));
843
844    loc -= Columns(5);
845    assert_eq!(loc, Location::new(-2, -10));
846}
847
848// Note: we can't do Sub<LocationLike> because of the conflict with
849// Sub<VectorLike>. Consider that (isize, isize) implements both VectorLike
850// and LocationLike, which means that Location - (isize, isize) doesn't make
851// sense anyway (though conceivably it could be resolved via return type
852// analysis)
853impl Sub<Location> for Location {
854    type Output = Vector;
855
856    #[inline]
857    #[must_use]
858    fn sub(self, rhs: Location) -> Vector {
859        Vector {
860            rows: self.row - rhs.row,
861            columns: self.column - rhs.column,
862        }
863    }
864}
865
866impl Sub<(Row, Column)> for Location {
867    type Output = Vector;
868
869    #[inline]
870    #[must_use]
871    fn sub(self, (row, column): (Row, Column)) -> Vector {
872        Vector {
873            rows: self.row - row,
874            columns: self.column - column,
875        }
876    }
877}
878
879impl Sub<(Column, Row)> for Location {
880    type Output = Vector;
881
882    #[inline]
883    #[must_use]
884    fn sub(self, (column, row): (Column, Row)) -> Vector {
885        Vector {
886            rows: self.row - row,
887            columns: self.column - column,
888        }
889    }
890}
891
892// TODO: add other Sub<LocationLike> implementations as they're needed
893
894#[cfg(test)]
895#[test]
896fn test_sub_self() {
897    let loc1 = Location::new(4, 5);
898    let loc2 = Location::new(1, 1);
899    assert_eq!(loc1 - loc2, Vector::new(3, 4));
900}
901
902#[cfg(test)]
903#[test]
904fn test_sub_self_tuple() {
905    let loc1 = Location::new(4, 5);
906    let loc2 = (Row(1), Column(2));
907    assert_eq!(loc1 - loc2, Vector::new(3, 3));
908}
909
910#[cfg(test)]
911#[test]
912fn test_sub_self_reverse_tuple() {
913    let loc1 = Location::new(4, 5);
914    let loc2 = (Column(2), Row(1));
915    assert_eq!(loc1 - loc2, Vector::new(3, 3));
916}
917
918impl<T: LocationLike> PartialEq<T> for Location {
919    #[must_use]
920    #[inline]
921    fn eq(&self, rhs: &T) -> bool {
922        self.row == rhs.row() && self.column == rhs.column()
923    }
924}
925
926/// Locations have a partial ordering. `loc1` is considered greater than `loc2` iff
927/// its row or its column are greater than those in `loc2`. This chart shows an
928/// example:
929///
930/// ```text
931/// <<<??
932/// <<<??
933/// <<=>>
934/// ??>>>
935/// ??>>>
936/// ```
937///
938/// Cells marked `>` are considered greater than the center location (marked `=`),
939/// and cells marked '<' are less than the center location. Cells marked `?` do
940/// not have an ordering with the center location.
941///
942/// For a strict ordering between all possible locations, see the [`Ordered`]
943/// wrapper struct, which allows for row-major or column-major orderings.
944impl<T: LocationLike> PartialOrd<T> for Location {
945    #[must_use]
946    fn partial_cmp(&self, rhs: &T) -> Option<Ordering> {
947        match (self.row.cmp(&rhs.row()), self.column.cmp(&rhs.column())) {
948            (Ordering::Greater, Ordering::Less) | (Ordering::Less, Ordering::Greater) => None,
949            (o1, o2) => Some(o1.then(o2)),
950        }
951    }
952}
953
954#[cfg(test)]
955mod partial_ord_tests {
956    use crate::prelude::{Location, LocationLike};
957    use crate::shorthand::{C, L, R};
958    use core::cmp::Ordering;
959
960    const ZERO: Location = Location::zero();
961
962    #[test]
963    fn test_eq() {
964        assert_eq!(L(3, 4), (R(3), C(4)));
965        assert_eq!(L(3, 4), (C(4), R(3)));
966        assert_eq!(L(3, 4), L(3, 4));
967        assert_eq!(L(3, 4), (3, 4));
968    }
969
970    #[test]
971    fn test_orderliness() {
972        assert_eq!(ZERO.partial_cmp(&ZERO.above(1)), Some(Ordering::Greater));
973        assert_eq!(ZERO.partial_cmp(&ZERO.left(1)), Some(Ordering::Greater));
974        assert_eq!(ZERO.partial_cmp(&(ZERO - (1, 1))), Some(Ordering::Greater));
975
976        assert_eq!(ZERO.partial_cmp(&ZERO.below(1)), Some(Ordering::Less));
977        assert_eq!(ZERO.partial_cmp(&ZERO.right(1)), Some(Ordering::Less));
978        assert_eq!(ZERO.partial_cmp(&(ZERO + (1, 1))), Some(Ordering::Less));
979
980        assert_eq!(ZERO.partial_cmp(&(ZERO + (-1, 1))), None);
981        assert_eq!(ZERO.partial_cmp(&(ZERO - (-1, 1))), None);
982
983        assert_eq!(ZERO.partial_cmp(&ZERO), Some(Ordering::Equal));
984    }
985
986    #[test]
987    fn test_bad_diagonal() {
988        for location in &[L(1, -1), L(-1, 1)] {
989            assert!(!(ZERO < *location));
990            assert!(!(ZERO > *location));
991            assert!(!(ZERO <= *location));
992            assert!(!(ZERO >= *location));
993        }
994    }
995}
996
997/// A pair of [`isize`] values acts as a `(`[`Row`]`, `[`Column`]`)` pair.
998impl LocationLike for (isize, isize) {
999    #[inline]
1000    #[must_use]
1001    fn row(&self) -> Row {
1002        Row(self.0)
1003    }
1004
1005    #[inline]
1006    #[must_use]
1007    fn column(&self) -> Column {
1008        Column(self.1)
1009    }
1010
1011    #[inline]
1012    #[must_use]
1013    fn as_location(&self) -> Location {
1014        Location::new(self.0, self.1)
1015    }
1016}
1017
1018/// Rules for ordering a location. This struct wraps a [`LocationLike`] and
1019/// supplies an [`Ord`] and [`PartialOrd`] implementation. The `Major`
1020/// type parameter indicates which ordering is used; for instance,
1021/// `Ordering<Row>` provides row-major ordering, where Locations are sorted
1022/// first by row, then by column.
1023#[derive(Debug, Clone, Copy, Default, Hash)]
1024pub struct Ordered<L: LocationLike, Major: Component> {
1025    pub location: L,
1026    phantom: PhantomData<Major>,
1027}
1028
1029impl<L: LocationLike, M: Component> Ordered<L, M> {
1030    #[inline]
1031    #[must_use]
1032    pub fn new(location: L) -> Self {
1033        Self {
1034            location,
1035            phantom: PhantomData,
1036        }
1037    }
1038}
1039
1040impl<L: LocationLike, M: Component> From<L> for Ordered<L, M> {
1041    #[inline]
1042    #[must_use]
1043    fn from(location: L) -> Self {
1044        Self::new(location)
1045    }
1046}
1047
1048impl<L: LocationLike, M: Component> AsRef<L> for Ordered<L, M> {
1049    #[inline]
1050    #[must_use]
1051    fn as_ref(&self) -> &L {
1052        &self.location
1053    }
1054}
1055
1056impl<L: LocationLike, M: Component> AsMut<L> for Ordered<L, M> {
1057    #[inline]
1058    #[must_use]
1059    fn as_mut(&mut self) -> &mut L {
1060        &mut self.location
1061    }
1062}
1063
1064impl<L: LocationLike, M: Component> Deref for Ordered<L, M> {
1065    type Target = L;
1066
1067    #[inline]
1068    #[must_use]
1069    fn deref(&self) -> &L {
1070        &self.location
1071    }
1072}
1073
1074impl<L: LocationLike, M: Component> DerefMut for Ordered<L, M> {
1075    #[inline]
1076    #[must_use]
1077    fn deref_mut(&mut self) -> &mut L {
1078        &mut self.location
1079    }
1080}
1081
1082impl<L: LocationLike, M: Component> LocationLike for Ordered<L, M> {
1083    #[inline]
1084    #[must_use]
1085    fn row(&self) -> Row {
1086        self.location.row()
1087    }
1088
1089    #[inline]
1090    #[must_use]
1091    fn column(&self) -> Column {
1092        self.location.column()
1093    }
1094
1095    #[inline]
1096    #[must_use]
1097    fn as_location(&self) -> Location {
1098        self.location.as_location()
1099    }
1100}
1101
1102impl<L: LocationLike, M: Component, R: LocationLike> PartialEq<R> for Ordered<L, M> {
1103    #[inline]
1104    #[must_use]
1105    fn eq(&self, rhs: &R) -> bool {
1106        self.as_location() == rhs.as_location()
1107    }
1108}
1109
1110impl<L: LocationLike, M: Component> Eq for Ordered<L, M> {}
1111
1112impl<L: LocationLike, M: Component> PartialOrd for Ordered<L, M> {
1113    #[inline]
1114    #[must_use]
1115    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
1116        Some(self.cmp(rhs))
1117    }
1118
1119    #[inline]
1120    #[must_use]
1121    fn lt(&self, rhs: &Self) -> bool {
1122        self.cmp(rhs) == Ordering::Less
1123    }
1124
1125    #[inline]
1126    #[must_use]
1127    fn le(&self, rhs: &Self) -> bool {
1128        self.cmp(rhs) != Ordering::Greater
1129    }
1130
1131    #[inline]
1132    #[must_use]
1133    fn gt(&self, rhs: &Self) -> bool {
1134        self.cmp(rhs) == Ordering::Greater
1135    }
1136
1137    #[inline]
1138    #[must_use]
1139    fn ge(&self, rhs: &Self) -> bool {
1140        self.cmp(rhs) != Ordering::Less
1141    }
1142}
1143
1144impl<L: LocationLike, M: Component> Ord for Ordered<L, M> {
1145    fn cmp(&self, rhs: &Self) -> Ordering {
1146        M::from_location(self)
1147            .cmp(&M::from_location(rhs))
1148            .then_with(move || {
1149                M::Converse::from_location(self).cmp(&M::Converse::from_location(rhs))
1150            })
1151    }
1152}
1153
1154/// A generic type alias for ordering a [`LocationLike`] type `T` by row.
1155pub type RowOrdered<L> = Ordered<L, Row>;
1156
1157/// A generic type for ordering a [`LocationLike`] type `T` by column.
1158pub type ColumnOrdered<L> = Ordered<L, Column>;
1159
1160/// Type alias for a [`Location`] ordered by row.
1161pub type RowOrderedLocation = RowOrdered<Location>;
1162
1163/// Type alias for a [`Location`] ordered by column.
1164pub type ColumnOrderedLocation = ColumnOrdered<Location>;