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