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