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