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