From a2e311f29529d58a9095acc302ad614466703f13 Mon Sep 17 00:00:00 2001 From: erius Date: Fri, 17 May 2024 13:33:56 +0300 Subject: [PATCH] Separated Num and Num + Clone implementations Implemented iterator over every matrix element index pair (i, j) in method Matrix::indices Removed redundat lifetime specifiers in some iterator methods Implemented Add, AddAssign, Sub, SubAssign and Neg traits for Matrix Added some tests for new functionality --- src/math/matrix.rs | 219 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 175 insertions(+), 44 deletions(-) diff --git a/src/math/matrix.rs b/src/math/matrix.rs index c041bf6..a224d5c 100644 --- a/src/math/matrix.rs +++ b/src/math/matrix.rs @@ -1,9 +1,9 @@ -use std::ops::{Index, IndexMut}; +use std::ops::{Add, AddAssign, Index, IndexMut, Neg, Sub, SubAssign}; -use num::Num; +use num::{traits::NumAssign, Num, Signed}; -#[derive(PartialEq, Eq, Debug)] -pub struct Matrix { +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Matrix { width: usize, data: Box> } @@ -25,6 +25,41 @@ impl Matrix { Self::new_filled(T::zero(), width, height) } + pub fn minor(&self, row_index: usize, column_index: usize) -> Self { + if self.width < 2 { + panic!("Matrix width must be greater than 1 to form its minor, but got {}", self.width) + } + let height = self.height(); + if height < 2 { + panic!("Matrix height must be greater than 1 to form its minor, but got {}", height) + } + if self.width == 2 && height == 2 { + panic!("Unable to form minor of a 2x2 matrix"); + } + let minor_width = self.width - 1; + let mut minor_data = Box::new(Vec::with_capacity(minor_width * (height - 1))); + for (i, j, e) in self.iter_indexed() { + if i == row_index || j == column_index { continue; } + minor_data.push(e.clone()); + } + Self { width: minor_width, data: minor_data } + } + + pub fn transposed(&self) -> Self { + let mut transposed_data = Box::new(Vec::with_capacity(self.data.len())); + let height = self.height(); + for j in 0..self.width { + let mut row_start = 0usize; + for _ in 0..height { + transposed_data.push(self.data[row_start + j].clone()); + row_start += self.width; + } + } + Self { width: height, data: transposed_data } + } +} + +impl Matrix { pub fn new(mut data: Vec, width: usize) -> Self { if width <= 0 { panic!("Matrix width must be greater than 0, but got {}", width) @@ -54,24 +89,8 @@ impl Matrix { self.data.len() } - pub fn minor(&self, row_index: usize, column_index: usize) -> Self { - if self.width < 2 { - panic!("Matrix width must be greater than 1 to form its minor, but got {}", self.width) - } - let height = self.height(); - if height < 2 { - panic!("Matrix height must be greater than 1 to form its minor, but got {}", height) - } - if self.width == 2 && height == 2 { - panic!("Unable to form minor of a 2x2 matrix"); - } - let minor_width = self.width - 1; - let mut minor_data = Vec::with_capacity(minor_width * (height - 1)); - for (i, j, e) in self.iter_indexed() { - if i == row_index || j == column_index { continue; } - minor_data.push(e.clone()); - } - Self { width: minor_width, data: Box::new(minor_data) } + pub fn indices(&self) -> IterIndices { + IterIndices { i: 0, j: 0, width: self.width, height: self.height() } } pub fn into_iter(self) -> std::vec::IntoIter { @@ -90,8 +109,8 @@ impl Matrix { self.data.iter() } - pub fn iter_indexed<'a>(&'a self) -> IterIndexed<'a, T> { - IterIndexed { i: 0, j: 0, width: self.width, matrix_iter: self.iter() } + pub fn iter_indexed(&self) -> IterIndexed { + IterIndexed { indices: self.indices(), matrix_iter: self.iter() } } pub fn iter_rows(&self) -> std::slice::Chunks { @@ -102,41 +121,39 @@ impl Matrix { self.data.iter_mut() } - pub fn iter_indexed_mut<'a>(&'a mut self) -> IterIndexedMut<'a, T> { + pub fn iter_indexed_mut(&mut self) -> IterIndexedMut { IterIndexedMut { i: 0, j: 0, width: self.width, matrix_iter_mut: self.iter_mut() } } - pub fn iter_mut_rows(&mut self) -> std::slice::ChunksMut { + pub fn iter_rows_mut(&mut self) -> std::slice::ChunksMut { self.data.chunks_mut(self.width) } } -pub struct IterIndexed<'a, T: Num + Clone> { - i: usize, j: usize, width: usize, +pub struct IterIndexed<'a, T: Num> { + indices: IterIndices, matrix_iter: std::slice::Iter<'a, T> } -pub struct IterIndexedMut<'a, T: Num + Clone> { +pub struct IterIndexedMut<'a, T: Num> { i: usize, j: usize, width: usize, matrix_iter_mut: std::slice::IterMut<'a, T> } -impl<'a, T: Num + Clone> Iterator for IterIndexed<'a, T> { +pub struct IterIndices { + i: usize, j: usize, width: usize, height: usize +} + +impl<'a, T: Num> Iterator for IterIndexed<'a, T> { type Item = (usize, usize, &'a T); fn next(&mut self) -> Option { - self.matrix_iter.next().map(|e| { - let next = (self.i, self.j, e); - self.j += 1; - if self.j >= self.width { - self.i += 1; - self.j = 0; - } - next - }) + self.indices.next() + .zip(self.matrix_iter.next()) + .map(|((i, j), e)| (i, j, e)) } } -impl<'a, T: Num + Clone> Iterator for IterIndexedMut<'a, T> { +impl<'a, T: Num> Iterator for IterIndexedMut<'a, T> { type Item = (usize, usize, &'a mut T); fn next(&mut self) -> Option { self.matrix_iter_mut.next().map(|e| { @@ -151,7 +168,20 @@ impl<'a, T: Num + Clone> Iterator for IterIndexedMut<'a, T> { } } -impl Index for Matrix { +impl Iterator for IterIndices { + type Item = (usize, usize); + fn next(&mut self) -> Option { + self.j += 1; + if self.j > self.width { + self.i += 1; + if self.i >= self.height { return None; } + self.j = 1; + } + Some((self.i, self.j - 1)) + } +} + +impl Index for Matrix { type Output = [T]; fn index(&self, index: usize) -> &Self::Output { let row_start = index * self.width; @@ -159,26 +189,84 @@ impl Index for Matrix { } } -impl IndexMut for Matrix { +impl IndexMut for Matrix { fn index_mut(&mut self, index: usize) -> &mut Self::Output { let row_start = index * self.width; &mut self.data[row_start..row_start + self.width] } } -impl Index<(usize, usize)> for Matrix { +impl Index<(usize, usize)> for Matrix { type Output = T; fn index(&self, index: (usize, usize)) -> &Self::Output { &self.data[index.0 * self.width + index.1] } } -impl IndexMut<(usize, usize)> for Matrix { +impl IndexMut<(usize, usize)> for Matrix { fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output { &mut self.data[index.0 * self.width + index.1] } } +fn op(this: Matrix, rhs: Matrix, op: F) -> Option> + where T: Num + Clone, F: Fn(T, T) -> T { + if this.width != rhs.width || this.height() != rhs.height() { + return None; + } + let mut new = this.clone(); + for (i, j) in new.indices() { + new[i][j] = op(new[i][j].clone(), rhs[i][j].clone()); + } + Some(new) +} + +fn op_assign(this: &mut Matrix, rhs: Matrix, op: F) + where T: NumAssign + Clone, F: Fn(&mut T, T) { + if this.width != rhs.width || this.height() != rhs.height() { + return; + } + for (i, j, e) in this.iter_indexed_mut() { + op(e, rhs[i][j].clone()); + } +} + +impl Add for Matrix { + type Output = Option; + fn add(self, rhs: Self) -> Self::Output { + op(self, rhs, T::add) + } +} + +impl AddAssign for Matrix { + fn add_assign(&mut self, rhs: Self) { + op_assign(self, rhs, T::add_assign); + } +} + +impl Neg for Matrix { + type Output = Self; + fn neg(mut self) -> Self::Output { + for e in self.iter_mut() { + *e = -e.clone(); + } + self + } +} + +impl Sub for Matrix { + type Output = Option; + fn sub(self, rhs: Self) -> Self::Output { + op(self, rhs, T::sub) + } +} + +impl SubAssign for Matrix { + fn sub_assign(&mut self, rhs: Self) { + op_assign(self, rhs, T::sub_assign); + } +} + #[macro_export] macro_rules! matrix { [ $w:expr; $( $x:expr ),+ ] => { @@ -289,4 +377,47 @@ mod tests { fn minor_nx1_panic() { matrix![4; 1, 2, 3, 4].minor(0, 0); } + + #[test] + fn transposed() { + let matrix = matrix![4; + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12 + ]; + assert_eq!(matrix.transposed(), matrix![3; + 1, 5, 9, + 2, 6, 10, + 3, 7, 11, + 4, 8, 12 + ]); + } + + #[test] + fn add() { + let mut matrix = matrix![2; 1, 2, 3, 4]; + matrix += matrix![2; 2, 3, 5, 4]; + assert_eq!(matrix, matrix![2; 3, 5, 8, 8]); + matrix += Matrix::new_filled(1, 3, 4); + assert_eq!(matrix, matrix![2; 3, 5, 8, 8]); + assert_eq!(matrix + matrix![2; 2, 3, 5, 4], Some(matrix![2; 5, 8, 13, 12])); + assert_eq!(Matrix::new_filled(1, 3, 3) + Matrix::new_filled(1, 3, 4), None); + } + + #[test] + fn sub() { + let mut matrix = matrix![2; 1, 2, 3, 4]; + matrix -= matrix![2; 2, 3, 5, 4]; + assert_eq!(matrix, matrix![2; -1, -1, -2, 0]); + matrix -= Matrix::new_filled(1, 3, 4); + assert_eq!(matrix, matrix![2; -1, -1, -2, 0]); + assert_eq!(matrix - matrix![2; 2, 3, 5, 4], Some(matrix![2; -3, -4, -7, -4])); + assert_eq!(Matrix::new_filled(1, 3, 3) - Matrix::new_filled(1, 3, 4), None); + } + + #[test] + fn neg() { + let matrix = matrix![2; 1, 2, 3, 4]; + assert_eq!(-matrix, matrix![2; -1, -2, -3, -4]); + } }