1use std::marker::PhantomData;
4use std::ops::{Add, AddAssign, Range, Sub, SubAssign};
5
6use snafu::prelude::*;
7
8use crate::traits::{Contains2D, Dimensions2D, Domain2D, Matrix2D, MatrixMut, MatrixRef};
9
10pub mod traits;
11
12#[cfg(test)]
13mod tests;
14
15#[derive(Clone, Debug, Snafu)]
17pub struct MatrixDimensionError {
18 pub n_values: usize,
20 pub n_cols: usize,
22}
23
24#[derive(Clone, Debug, Snafu)]
26pub struct MatrixViewOutOfBoundsError {
27 pub dimensions: MatrixRange,
29 pub range: MatrixRange,
31}
32
33#[derive(Clone, Debug, Snafu)]
35pub struct CoordinatesOutOfBoundsError {
36 pub range: MatrixRange,
38 pub coordinates: MatrixCoordinates,
40}
41
42#[derive(Clone, Debug, Default, PartialEq)]
44#[cfg_attr(
45 feature = "serde",
46 derive(serde::Serialize, serde::Deserialize),
47 serde(default, rename_all = "camelCase")
48)]
49pub struct Matrix<C> {
50 pub values: Vec<C>,
52 pub dimensions: MatrixDimensions,
54}
55impl<C> Matrix<C> {
56 pub fn index<T: Into<MatrixCoordinates>>(
58 &self,
59 coordinates: T,
60 ) -> Result<usize, CoordinatesOutOfBoundsError> {
61 let x: MatrixCoordinates = coordinates.into();
62 if self.range().contains(&x) {
63 Ok(x.row * self.n_cols() + x.col)
64 } else {
65 Err(CoordinatesOutOfBoundsError {
66 coordinates: x,
67 range: self.range(),
68 })
69 }
70 }
71 pub fn slice<R: Into<MatrixRange>>(
73 &self,
74 range: R,
75 ) -> Result<MatrixSlice<'_, C>, MatrixSliceError> {
76 let range: MatrixRange = range.into();
77 if self.range().contains(&range) {
78 Ok(MatrixSlice {
79 values: &self.values,
80 input_dimensions: self.dimensions,
81 view_range: range,
82 })
83 } else {
84 MatrixViewOutOfBoundsSnafu {
85 range,
86 dimensions: self.dimensions,
87 }
88 .fail()
89 .context(SliceRangeOutOfBoundsSnafu)
90 }
91 }
92}
93impl<C> Matrix2D<C> for Matrix<C> {
94 fn new(values: Vec<C>, n_cols: usize) -> Result<Self, MatrixDimensionError> {
95 let n_values = values.len();
96 let dimensions = MatrixDimensions::new(n_values / n_cols, n_cols);
97 if dimensions.len() == n_values {
98 Ok(Self { values, dimensions })
99 } else {
100 MatrixDimensionSnafu { n_values, n_cols }.fail()
101 }
102 }
103}
104impl<C> MatrixRef<C> for Matrix<C> {
105 fn get<T: Into<MatrixCoordinates>>(
106 &self,
107 coordinates: T,
108 ) -> Result<&C, CoordinatesOutOfBoundsError> {
109 let x: MatrixCoordinates = coordinates.into();
110 let index = self.index(x)?;
111 self.values.get(index).context(CoordinatesOutOfBoundsSnafu {
112 range: self.range(),
113 coordinates: x,
114 })
115 }
116}
117impl<C> MatrixMut<C> for Matrix<C> {
118 fn set<T: Into<MatrixCoordinates>>(
119 &mut self,
120 coordinates: T,
121 value: C,
122 ) -> Result<C, CoordinatesOutOfBoundsError> {
123 let coordinates: MatrixCoordinates = coordinates.into();
124 let index = self.index(coordinates)?;
125 if let Some(entry) = self.values.get_mut(index) {
126 Ok(std::mem::replace(entry, value))
127 } else {
128 Err(CoordinatesOutOfBoundsError {
129 range: self.range(),
130 coordinates,
131 })
132 }
133 }
134}
135impl<C> Domain2D for Matrix<C> {
136 fn row_range(&self) -> Range<usize> {
137 0..self.dimensions.n_rows
138 }
139
140 fn col_range(&self) -> Range<usize> {
141 0..self.dimensions.n_cols
142 }
143}
144
145#[derive(Clone, Debug, Snafu)]
147pub enum MatrixSliceError {
148 SliceInputDimension { source: MatrixDimensionError },
149 SliceRangeOutOfBounds { source: MatrixViewOutOfBoundsError },
150}
151
152#[derive(Clone, Debug, Default, PartialEq)]
154pub struct MatrixSlice<'a, C> {
155 pub values: &'a [C],
157 pub input_dimensions: MatrixDimensions,
159 pub view_range: MatrixRange,
161}
162impl<'a, C> MatrixSlice<'a, C> {
163 pub fn new<R: Into<MatrixRange>>(
166 values: &'a [C],
167 n_cols: usize,
168 range: R,
169 ) -> Result<Self, MatrixSliceError> {
170 let n_values = values.len();
171 let n_rows = n_values / n_cols;
172 let input_dimensions = MatrixDimensions::new(n_rows, n_cols);
173 if values.len() != input_dimensions.len() {
174 return MatrixDimensionSnafu { n_values, n_cols }
175 .fail()
176 .context(SliceInputDimensionSnafu);
177 }
178 let domain: MatrixRange = input_dimensions.into();
179 let range: MatrixRange = range.into();
180 if domain.contains(&range) {
181 Ok(Self {
182 values,
183 input_dimensions,
184 view_range: range,
185 })
186 } else {
187 MatrixViewOutOfBoundsSnafu {
188 dimensions: input_dimensions,
189 range,
190 }
191 .fail()
192 .context(SliceRangeOutOfBoundsSnafu)
193 }
194 }
195
196 pub fn index<T: Into<MatrixCoordinates>>(
199 &self,
200 coordinates: T,
201 ) -> Result<usize, CoordinatesOutOfBoundsError> {
202 let x: MatrixCoordinates = coordinates.into();
203 let x = x + self.view_range.start;
204 if self.view_range.contains(&x) {
205 Ok(x.row * self.input_dimensions.n_cols + x.col)
206 } else {
207 Err(CoordinatesOutOfBoundsError {
208 coordinates: x,
209 range: self.view_range,
210 })
211 }
212 }
213}
214
215impl<'a, C> MatrixSlice<'a, C>
216where
217 C: Clone + Default,
218{
219 pub fn to_matrix(&self) -> Matrix<C> {
221 Matrix::new(
222 self.row_range()
223 .flat_map(|row| {
224 self.col_range().filter_map(move |col| {
225 self.get(MatrixCoordinates::new(row, col)).ok().cloned()
226 })
227 })
228 .collect(),
229 self.view_range.col_range().end,
230 )
231 .unwrap_or_default()
232 }
233}
234impl<'a, C> MatrixRef<C> for MatrixSlice<'a, C> {
235 fn get<T: Into<MatrixCoordinates>>(
236 &self,
237 coordinates: T,
238 ) -> Result<&C, CoordinatesOutOfBoundsError> {
239 let coordinates: MatrixCoordinates = coordinates.into();
240 let index = self.index(coordinates)?;
241 self.values.get(index).context(CoordinatesOutOfBoundsSnafu {
242 range: self.view_range,
243 coordinates,
244 })
245 }
246}
247impl<'a, C> Domain2D for MatrixSlice<'a, C> {
248 fn row_range(&self) -> Range<usize> {
249 0..(self.view_range.end.row - self.view_range.start.row)
250 }
251 fn col_range(&self) -> Range<usize> {
252 0..(self.view_range.end.col - self.view_range.start.col)
253 }
254}
255
256#[derive(Clone, Debug, PartialEq)]
258pub struct MatrixCellRef<'a, M: MatrixRef<C> + ?Sized, C> {
259 pub matrix: &'a M,
261 pub coordinates: MatrixCoordinates,
263 _phantom: PhantomData<C>,
265}
266impl<'a, M: MatrixRef<C> + ?Sized, C> MatrixCellRef<'a, M, C> {
267 pub fn new<T: Into<MatrixCoordinates>>(matrix: &'a M, coordinates: T) -> Self {
269 Self {
270 matrix,
271 coordinates: coordinates.into(),
272 _phantom: PhantomData,
273 }
274 }
275}
276
277#[derive(Copy, Clone, Debug, PartialEq)]
279#[cfg_attr(
280 feature = "serde",
281 derive(serde::Serialize, serde::Deserialize),
282 serde(rename_all = "camelCase")
283)]
284pub enum MatrixLocation {
285 Row(usize),
287 Col(usize),
289 Coordinates(MatrixCoordinates),
291 Range(MatrixRange),
293}
294impl Default for MatrixLocation {
295 fn default() -> Self {
296 Self::Coordinates(Default::default())
297 }
298}
299impl From<MatrixCoordinates> for MatrixLocation {
300 fn from(value: MatrixCoordinates) -> Self {
301 Self::Coordinates(value)
302 }
303}
304impl From<MatrixRange> for MatrixLocation {
305 fn from(value: MatrixRange) -> Self {
306 Self::Range(value)
307 }
308}
309impl MatrixLocation {
310 pub fn from_coords(row: Option<usize>, col: Option<usize>) -> Option<Self> {
312 match (row, col) {
313 (Some(row), Some(col)) => Some(Self::Coordinates(MatrixCoordinates::new(row, col))),
314 (Some(row), None) => Some(Self::Row(row)),
315 (None, Some(col)) => Some(Self::Col(col)),
316 (None, None) => None,
317 }
318 }
319 pub fn row(&self) -> Option<usize> {
321 match self {
322 Self::Row(row) => Some(*row),
323 Self::Col(_) => None,
324 Self::Coordinates(coord) => Some(coord.row),
325 Self::Range(area) => Some(area.row_range().start),
326 }
327 }
328 pub fn col(&self) -> Option<usize> {
330 match self {
331 Self::Row(_) => None,
332 Self::Col(col) => Some(*col),
333 Self::Coordinates(coord) => Some(coord.col),
334 Self::Range(area) => Some(area.col_range().start),
335 }
336 }
337}
338impl Add<MatrixCoordinates> for MatrixLocation {
339 type Output = MatrixLocation;
340 fn add(self, rhs: MatrixCoordinates) -> Self::Output {
341 match self {
342 Self::Col(col) => Self::Col(col + rhs.col),
343 Self::Coordinates(x) => Self::Coordinates(x + rhs),
344 Self::Range(range) => Self::Range(range + rhs),
345 Self::Row(row) => Self::Row(row + rhs.row),
346 }
347 }
348}
349impl AddAssign<MatrixCoordinates> for MatrixLocation {
350 fn add_assign(&mut self, rhs: MatrixCoordinates) {
351 match self {
352 Self::Col(col) => col.add_assign(rhs.col),
353 Self::Coordinates(x) => x.add_assign(rhs),
354 Self::Range(range) => range.add_assign(rhs),
355 Self::Row(row) => row.add_assign(rhs.row),
356 };
357 }
358}
359impl Sub<MatrixCoordinates> for MatrixLocation {
360 type Output = MatrixLocation;
361 fn sub(self, rhs: MatrixCoordinates) -> Self::Output {
362 match self {
363 Self::Col(col) => Self::Col(col - rhs.col),
364 Self::Coordinates(x) => Self::Coordinates(x - rhs),
365 Self::Range(range) => Self::Range(range - rhs),
366 Self::Row(row) => Self::Row(row - rhs.row),
367 }
368 }
369}
370impl SubAssign<MatrixCoordinates> for MatrixLocation {
371 fn sub_assign(&mut self, rhs: MatrixCoordinates) {
372 match self {
373 Self::Col(col) => col.sub_assign(rhs.col),
374 Self::Coordinates(x) => x.sub_assign(rhs),
375 Self::Range(range) => range.sub_assign(rhs),
376 Self::Row(row) => row.sub_assign(rhs.row),
377 };
378 }
379}
380impl Add<MatrixLocation> for MatrixLocation {
381 type Output = MatrixLocation;
382 fn add(self, rhs: MatrixLocation) -> Self::Output {
383 match self {
384 Self::Col(col) => Self::Col(col + rhs.col().unwrap_or_default()),
385 Self::Coordinates(x) => Self::Coordinates(
386 x + MatrixCoordinates::new(
387 rhs.row().unwrap_or_default(),
388 rhs.col().unwrap_or_default(),
389 ),
390 ),
391 Self::Range(range) => Self::Range(
392 range
393 + MatrixCoordinates::new(
394 rhs.row().unwrap_or_default(),
395 rhs.col().unwrap_or_default(),
396 ),
397 ),
398 Self::Row(row) => Self::Row(row + rhs.row().unwrap_or_default()),
399 }
400 }
401}
402impl AddAssign<MatrixLocation> for MatrixLocation {
403 fn add_assign(&mut self, rhs: MatrixLocation) {
404 match self {
405 Self::Col(col) => col.add_assign(rhs.col().unwrap_or_default()),
406 Self::Coordinates(x) => x.add_assign(MatrixCoordinates::new(
407 rhs.row().unwrap_or_default(),
408 rhs.col().unwrap_or_default(),
409 )),
410 Self::Range(range) => range.add_assign(MatrixCoordinates::new(
411 rhs.row().unwrap_or_default(),
412 rhs.col().unwrap_or_default(),
413 )),
414 Self::Row(row) => row.add_assign(rhs.row().unwrap_or_default()),
415 }
416 }
417}
418impl Sub<MatrixLocation> for MatrixLocation {
419 type Output = MatrixLocation;
420 fn sub(self, rhs: MatrixLocation) -> Self::Output {
421 match self {
422 Self::Col(col) => Self::Col(col - rhs.col().unwrap_or_default()),
423 Self::Coordinates(x) => Self::Coordinates(
424 x - MatrixCoordinates::new(
425 rhs.row().unwrap_or_default(),
426 rhs.col().unwrap_or_default(),
427 ),
428 ),
429 Self::Range(range) => Self::Range(
430 range
431 - MatrixCoordinates::new(
432 rhs.row().unwrap_or_default(),
433 rhs.col().unwrap_or_default(),
434 ),
435 ),
436 Self::Row(row) => Self::Row(row - rhs.row().unwrap_or_default()),
437 }
438 }
439}
440impl SubAssign<MatrixLocation> for MatrixLocation {
441 fn sub_assign(&mut self, rhs: MatrixLocation) {
442 match self {
443 Self::Col(col) => col.sub_assign(rhs.col().unwrap_or_default()),
444 Self::Coordinates(x) => x.sub_assign(MatrixCoordinates::new(
445 rhs.row().unwrap_or_default(),
446 rhs.col().unwrap_or_default(),
447 )),
448 Self::Range(range) => range.sub_assign(MatrixCoordinates::new(
449 rhs.row().unwrap_or_default(),
450 rhs.col().unwrap_or_default(),
451 )),
452 Self::Row(row) => row.sub_assign(rhs.row().unwrap_or_default()),
453 }
454 }
455}
456impl Domain2D for MatrixLocation {
457 fn row_range(&self) -> Range<usize> {
458 match self {
459 Self::Coordinates(coordinates) => Range {
460 start: coordinates.row,
461 end: coordinates.row + 1,
462 },
463 Self::Row(row) => Range {
464 start: *row,
465 end: row + 1,
466 },
467 Self::Col(_) => Range { start: 0, end: 0 },
468 Self::Range(range) => range.row_range(),
469 }
470 }
471 fn col_range(&self) -> Range<usize> {
472 match self {
473 Self::Coordinates(coordinates) => Range {
474 start: coordinates.col,
475 end: coordinates.col + 1,
476 },
477 Self::Row(_) => Range { start: 0, end: 0 },
478 Self::Col(col) => Range {
479 start: *col,
480 end: col + 1,
481 },
482 Self::Range(range) => range.col_range(),
483 }
484 }
485}
486
487#[derive(Copy, Clone, Debug, Default, PartialEq)]
489#[cfg_attr(
490 feature = "serde",
491 derive(serde::Serialize, serde::Deserialize),
492 serde(default, rename_all = "camelCase")
493)]
494pub struct MatrixCoordinates {
495 pub row: usize,
497 pub col: usize,
499}
500impl MatrixCoordinates {
501 pub fn new(row: usize, col: usize) -> Self {
503 Self { row, col }
504 }
505}
506impl From<(usize, usize)> for MatrixCoordinates {
507 fn from(value: (usize, usize)) -> Self {
508 Self {
509 row: value.0,
510 col: value.1,
511 }
512 }
513}
514impl Add<MatrixCoordinates> for MatrixCoordinates {
515 type Output = Self;
516 fn add(self, rhs: MatrixCoordinates) -> Self::Output {
517 MatrixCoordinates {
518 row: self.row + rhs.row,
519 col: self.col + rhs.col,
520 }
521 }
522}
523impl AddAssign<MatrixCoordinates> for MatrixCoordinates {
524 fn add_assign(&mut self, rhs: MatrixCoordinates) {
525 self.row += rhs.row;
526 self.col += rhs.col;
527 }
528}
529impl Sub<MatrixCoordinates> for MatrixCoordinates {
530 type Output = Self;
531 fn sub(self, rhs: MatrixCoordinates) -> Self::Output {
532 MatrixCoordinates {
533 row: self.row - rhs.row,
534 col: self.col - rhs.col,
535 }
536 }
537}
538impl SubAssign<MatrixCoordinates> for MatrixCoordinates {
539 fn sub_assign(&mut self, rhs: MatrixCoordinates) {
540 self.row -= rhs.row;
541 self.col -= rhs.col;
542 }
543}
544
545#[derive(Copy, Clone, Debug, Default, PartialEq)]
547#[cfg_attr(
548 feature = "serde",
549 derive(serde::Serialize, serde::Deserialize),
550 serde(default, rename_all = "camelCase")
551)]
552pub struct MatrixDimensions {
553 pub n_rows: usize,
555 pub n_cols: usize,
557}
558impl MatrixDimensions {
559 pub fn new(n_rows: usize, n_cols: usize) -> Self {
561 Self { n_rows, n_cols }
562 }
563}
564impl From<(usize, usize)> for MatrixDimensions {
565 fn from(value: (usize, usize)) -> Self {
566 Self {
567 n_rows: value.0,
568 n_cols: value.1,
569 }
570 }
571}
572impl Dimensions2D for MatrixDimensions {
573 fn n_rows(&self) -> usize {
574 self.n_rows
575 }
576 fn n_cols(&self) -> usize {
577 self.n_cols
578 }
579}
580impl Add<MatrixDimensions> for MatrixDimensions {
581 type Output = Self;
582 fn add(self, rhs: MatrixDimensions) -> Self::Output {
583 MatrixDimensions {
584 n_rows: self.n_rows + rhs.n_rows,
585 n_cols: self.n_cols + rhs.n_cols,
586 }
587 }
588}
589impl AddAssign<MatrixDimensions> for MatrixDimensions {
590 fn add_assign(&mut self, rhs: MatrixDimensions) {
591 self.n_rows += rhs.n_rows;
592 self.n_cols += rhs.n_cols;
593 }
594}
595impl Sub<MatrixDimensions> for MatrixDimensions {
596 type Output = Self;
597 fn sub(self, rhs: MatrixDimensions) -> Self::Output {
598 MatrixDimensions {
599 n_rows: self.n_rows - rhs.n_rows,
600 n_cols: self.n_cols - rhs.n_cols,
601 }
602 }
603}
604impl SubAssign<MatrixDimensions> for MatrixDimensions {
605 fn sub_assign(&mut self, rhs: MatrixDimensions) {
606 self.n_rows -= rhs.n_rows;
607 self.n_cols -= rhs.n_cols;
608 }
609}
610
611#[derive(Copy, Clone, Debug, Default, PartialEq)]
613#[cfg_attr(
614 feature = "serde",
615 derive(serde::Serialize, serde::Deserialize),
616 serde(default, rename_all = "camelCase")
617)]
618pub struct MatrixRange {
619 pub start: MatrixCoordinates,
621 pub end: MatrixCoordinates,
623}
624impl MatrixRange {
625 pub fn new(rows: Range<usize>, cols: Range<usize>) -> Self {
627 Self {
628 start: MatrixCoordinates::new(rows.start, cols.start),
629 end: MatrixCoordinates::new(rows.end, cols.end),
630 }
631 }
632}
633impl Domain2D for MatrixRange {
634 fn row_range(&self) -> Range<usize> {
636 self.start.row..self.end.row
637 }
638
639 fn col_range(&self) -> Range<usize> {
641 self.start.col..self.end.col
642 }
643}
644impl From<(Range<usize>, Range<usize>)> for MatrixRange {
645 fn from(value: (Range<usize>, Range<usize>)) -> Self {
646 Self::new(value.0, value.1)
647 }
648}
649impl From<((usize, usize), (usize, usize))> for MatrixRange {
650 fn from(value: ((usize, usize), (usize, usize))) -> Self {
652 Self {
653 start: value.0.into(),
654 end: value.1.into(),
655 }
656 }
657}
658impl From<MatrixDimensions> for MatrixRange {
659 fn from(value: MatrixDimensions) -> Self {
660 Self::new(0..value.n_rows, 0..value.n_cols)
661 }
662}
663impl Add<MatrixCoordinates> for MatrixRange {
664 type Output = Self;
665 fn add(self, rhs: MatrixCoordinates) -> Self::Output {
666 Self {
667 start: self.start + rhs,
668 end: self.end + rhs,
669 }
670 }
671}
672impl AddAssign<MatrixCoordinates> for MatrixRange {
673 fn add_assign(&mut self, rhs: MatrixCoordinates) {
674 self.start += rhs;
675 self.end += rhs;
676 }
677}
678impl Sub<MatrixCoordinates> for MatrixRange {
679 type Output = Self;
680 fn sub(self, rhs: MatrixCoordinates) -> Self::Output {
681 Self {
682 start: self.start - rhs,
683 end: self.end - rhs,
684 }
685 }
686}
687impl SubAssign<MatrixCoordinates> for MatrixRange {
688 fn sub_assign(&mut self, rhs: MatrixCoordinates) {
689 self.start -= rhs;
690 self.end -= rhs;
691 }
692}