1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Common traits for working with 2D (matrix) data.

use std::ops::Range;

use crate::{
    CoordinatesOutOfBoundsError, MatrixCoordinates, MatrixDimensionError, MatrixDimensions,
    MatrixRange,
};

/// 2D matrix behavior.
pub trait Matrix2D<C>: Dimensions2D + Domain2D + MatrixRef<C> + MatrixMut<C> + Sized {
    /// Create a new matrix from this value slice with the given number of columns in a row.
    fn new(values: Vec<C>, n_cols: usize) -> Result<Self, MatrixDimensionError>;
}

/// Trait to handle matrix data by reference.
pub trait MatrixRef<C> {
    /// Get a matrix value at this coordinate.
    fn get<T: Into<MatrixCoordinates>>(
        &self,
        coordinates: T,
    ) -> Result<&C, CoordinatesOutOfBoundsError>;
}

/// Trait to handle mutable matrix operations.
pub trait MatrixMut<C> {
    /// Set a certain cell's value.
    fn set<T: Into<MatrixCoordinates>>(
        &mut self,
        coordinates: T,
        value: C,
    ) -> Result<C, CoordinatesOutOfBoundsError>;
}

/// Domain in two directions.
pub trait Domain2D {
    /// Range of rows included in this object's domain.
    fn row_range(&self) -> Range<usize>;
    /// Range of columns included in this object's domain.
    fn col_range(&self) -> Range<usize>;
    /// The area spanned by this domain.
    fn range(&self) -> MatrixRange {
        MatrixRange::from((self.row_range(), self.col_range()))
    }
    /// Starting coordinate of this domain (inclusive).
    fn start(&self) -> MatrixCoordinates {
        MatrixCoordinates::new(self.row_range().start, self.col_range().start)
    }
    /// Ending coordinate of this domain (exclusive).
    fn end(&self) -> MatrixCoordinates {
        MatrixCoordinates::new(self.row_range().end, self.col_range().end)
    }
    /// The middle point of this cluster as a tuple of (rows, columns), fractional.
    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)
    }
}

/// Dimensions in two directions.
pub trait Dimensions2D {
    /// Number of rows in the output.
    fn n_rows(&self) -> usize;
    /// Number of columns in the output.
    fn n_cols(&self) -> usize;
    /// Dimensions of the output as a dimensions object.
    fn dimensions(&self) -> MatrixDimensions {
        MatrixDimensions::new(self.n_rows(), self.n_cols())
    }
    /// Length of these dimensions if it were a 1D vector.
    fn len(&self) -> usize {
        self.n_rows() * self.n_cols()
    }
    /// Whether these dimensions are (0,0).
    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
    }
}

/// Whether this instance contains or covers something else.
pub trait Contains2D<T> {
    /// Whether this instance contains or covers the other.
    fn contains(&self, other: &T) -> bool;
}