From 79d94bf35796b85e414dfa53c91027a8af9a1bbe Mon Sep 17 00:00:00 2001 From: erius Date: Sat, 18 May 2024 12:48:59 +0300 Subject: [PATCH] Restructured matrix module, it is now separated into 1 primary module matrix (struct definition, init methods and getters) and 3 sub-modules: iter - structs and methods for iteration, arithmetic - impl of traits for arithmetic operations, ops - non-arithmetic matrix operations Implemented Mul, MulAssign, Div and DivAssign traits for Matrix Added more tests --- src/{math/mod.rs => math.rs} | 2 + src/math/matrix.rs | 298 ++-------------------------------- src/math/matrix/arithemtic.rs | 176 ++++++++++++++++++++ src/math/matrix/iter.rs | 112 +++++++++++++ src/math/matrix/ops.rs | 99 +++++++++++ src/math/sq_matrix.rs | 4 +- 6 files changed, 405 insertions(+), 286 deletions(-) rename src/{math/mod.rs => math.rs} (61%) create mode 100644 src/math/matrix/arithemtic.rs create mode 100644 src/math/matrix/iter.rs create mode 100644 src/math/matrix/ops.rs diff --git a/src/math/mod.rs b/src/math.rs similarity index 61% rename from src/math/mod.rs rename to src/math.rs index 4de312a..a005381 100644 --- a/src/math/mod.rs +++ b/src/math.rs @@ -1,5 +1,7 @@ mod matrix; pub use matrix::Matrix; +pub use matrix::matrix; mod sq_matrix; pub use sq_matrix::SquareMatrix; +pub use sq_matrix::sq_matrix; diff --git a/src/math/matrix.rs b/src/math/matrix.rs index a224d5c..6cfda15 100644 --- a/src/math/matrix.rs +++ b/src/math/matrix.rs @@ -1,6 +1,10 @@ -use std::ops::{Add, AddAssign, Index, IndexMut, Neg, Sub, SubAssign}; +mod iter; +mod arithemtic; +mod ops; -use num::{traits::NumAssign, Num, Signed}; +use std::ops::{Index, IndexMut}; + +use num::Num; #[derive(PartialEq, Eq, Debug, Clone)] pub struct Matrix { @@ -24,39 +28,6 @@ impl Matrix { pub fn new_zeroes(width: usize, height: usize) -> Self { 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 { @@ -89,95 +60,8 @@ impl Matrix { self.data.len() } - pub fn indices(&self) -> IterIndices { - IterIndices { i: 0, j: 0, width: self.width, height: self.height() } - } - - pub fn into_iter(self) -> std::vec::IntoIter { - self.data.into_iter() - } - - pub fn into_iter_indexed(self) { - todo!() - } - - pub fn into_iter_rows(self) { - todo!() - } - - pub fn iter(&self) -> std::slice::Iter { - self.data.iter() - } - - pub fn iter_indexed(&self) -> IterIndexed { - IterIndexed { indices: self.indices(), matrix_iter: self.iter() } - } - - pub fn iter_rows(&self) -> std::slice::Chunks { - self.data.chunks(self.width) - } - - pub fn iter_mut(&mut self) -> std::slice::IterMut { - self.data.iter_mut() - } - - 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_rows_mut(&mut self) -> std::slice::ChunksMut { - self.data.chunks_mut(self.width) - } -} - -pub struct IterIndexed<'a, T: Num> { - indices: IterIndices, - matrix_iter: std::slice::Iter<'a, T> -} - -pub struct IterIndexedMut<'a, T: Num> { - i: usize, j: usize, width: usize, - matrix_iter_mut: std::slice::IterMut<'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.indices.next() - .zip(self.matrix_iter.next()) - .map(|((i, j), e)| (i, j, e)) - } -} - -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| { - let next = (self.i, self.j, e); - self.j += 1; - if self.j >= self.width { - self.i += 1; - self.j = 0; - } - next - }) - } -} - -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)) + pub fn same_size(&self, other: &Self) -> bool { + return self.width == other.width && self.height() == other.height() } } @@ -209,83 +93,27 @@ impl IndexMut<(usize, usize)> for Matrix { } } -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 ),+ ] => { - Matrix::new(vec![$( $x, )+], $w) + $crate::math::Matrix::new(vec![$( $x, )+], $w) }; } +pub use matrix; + #[cfg(test)] mod tests { use super::*; #[test] - #[should_panic] + #[should_panic(expected = "exceeds usize limit")] fn size_too_big() { Matrix::::new_zeroes(usize::MAX, 2); } #[test] - #[should_panic] + #[should_panic(expected = "size must be greater")] fn size_too_small() { Matrix::::new_zeroes(1, 1); } @@ -320,104 +148,4 @@ mod tests { matrix[2][1] = 0; assert_eq!(matrix[2][1], 0); } - - #[test] - fn iter() { - let data = vec![1,2,3,4,5,6,7,8,9]; - let mut matrix = Matrix::new(data.clone(), 3); - for (i, e) in matrix.iter().enumerate() { - assert_eq!(data[i], *e); - } - for (i, e) in matrix.iter_mut().enumerate() { - *e += 2; - assert_eq!(data[i] + 2, *e); - } - for (i, e) in matrix.into_iter().enumerate() { - assert_eq!(data[i] + 2, e); - } - } - - #[test] - fn minor() { - let matrix = matrix![4; - 1, 2, 3, 4, - 5, 6, 7, 8, - 9, 10, 11, 12 - ]; - assert_eq!(matrix.minor(0, 0), matrix![3; - 6, 7, 8, - 10, 11, 12 - ]); - assert_eq!(matrix.minor(1, 2), matrix![3; - 1, 2, 4, - 9, 10, 12 - ]); - assert_eq!(matrix.minor(0, 0).minor(0, 0), matrix![2; - 11, 12 - ]); - } - - #[test] - #[should_panic] - fn minor_2x2_panic() { - matrix![2; - 1, 2, - 3, 4 - ].minor(0, 0); - } - - #[test] - #[should_panic] - fn minor_1xn_panic() { - matrix![1; 1, 2, 3, 4].minor(0, 0); - } - - #[test] - #[should_panic] - 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]); - } } diff --git a/src/math/matrix/arithemtic.rs b/src/math/matrix/arithemtic.rs new file mode 100644 index 0000000..004cf60 --- /dev/null +++ b/src/math/matrix/arithemtic.rs @@ -0,0 +1,176 @@ +use super::*; + +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use num::{traits::NumAssign, Num, Signed}; + +impl Add for Matrix { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { + if !self.same_size(&rhs) { + panic!("Unable to add matrices with different sizes {}x{} and {}x{}", + self.width, self.height(), rhs.width, rhs.height()); + } + let mut new_data = Box::new(Vec::with_capacity(self.size())); + for (e1, e2) in self.iter().zip(rhs.iter()) { + new_data.push(e1.clone() + e2.clone()); + } + Self { width: self.width, data: new_data } + } +} + +impl AddAssign for Matrix { + fn add_assign(&mut self, rhs: Self) { + if !self.same_size(&rhs) { + panic!("Unable to add matrices with different sizes {}x{} and {}x{}", + self.width, self.height(), rhs.width, rhs.height()); + } + for (e1, e2) in self.iter_mut().zip(rhs.iter()) { + *e1 += e2.clone(); + } + } +} + +impl Neg for Matrix { + type Output = Self; + fn neg(mut self) -> Self::Output { + for e in self.iter_mut() { + *e = -e.clone(); + } + return self; + } +} + +impl Sub for Matrix { + type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { + if !self.same_size(&rhs) { + panic!("Unable to subtract matrices with different sizes {}x{} and {}x{}", + self.width, self.height(), rhs.width, rhs.height()); + } + let mut new_data = Box::new(Vec::with_capacity(self.size())); + for (e1, e2) in self.iter().zip(rhs.iter()) { + new_data.push(e1.clone() - e2.clone()); + } + Self { width: self.width, data: new_data } + } +} + +impl SubAssign for Matrix { + fn sub_assign(&mut self, rhs: Self) { + if !self.same_size(&rhs) { + panic!("Unable to subtract matrices with different sizes {}x{} and {}x{}", + self.width, self.height(), rhs.width, rhs.height()); + } + for (e1, e2) in self.iter_mut().zip(rhs.iter()) { + *e1 -= e2.clone(); + } + } +} + +impl Mul for Matrix { + type Output = Self; + fn mul(self, rhs: T) -> Self::Output { + let mut new_data = Box::new(Vec::with_capacity(self.size())); + for e in self.iter() { + new_data.push(e.clone() * rhs.clone()); + } + Self { width: self.width, data: new_data } + } +} + +impl MulAssign for Matrix { + fn mul_assign(&mut self, rhs: T) { + for e in self.iter_mut() { + *e *= rhs.clone(); + } + } +} + +impl Div for Matrix { + type Output = Self; + fn div(self, rhs: T) -> Self::Output { + let mut new_data = Box::new(Vec::with_capacity(self.size())); + for e in self.iter() { + new_data.push(e.clone() / rhs.clone()); + } + Self { width: self.width, data: new_data } + } +} + +impl DivAssign for Matrix { + fn div_assign(&mut self, rhs: T) { + for e in self.iter_mut() { + *e /= rhs.clone(); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[should_panic(expected = "different sizes")] + fn add_diff_sizes() { + let _ = Matrix::new_filled(1, 3, 3) + Matrix::new_filled(1, 3, 4); + } + + #[test] + #[should_panic(expected = "different sizes")] + fn add_assign_diff_sizes() { + let mut matrix = Matrix::new_filled(1, 3, 3); + matrix += Matrix::new_filled(1, 3, 4); + } + + #[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]); + assert_eq!(matrix + matrix![2; 2, 3, 5, 4], matrix![2; 5, 8, 13, 12]); + } + + #[test] + #[should_panic(expected = "different sizes")] + fn sub_diff_sizes() { + let _ = Matrix::new_filled(1, 3, 3) - Matrix::new_filled(1, 3, 4); + } + + #[test] + #[should_panic(expected = "different sizes")] + fn sub_assign_diff_sizes() { + let mut matrix = Matrix::new_filled(1, 3, 3); + matrix -= Matrix::new_filled(1, 3, 4); + } + + #[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]); + assert_eq!(matrix - matrix![2; 2, 3, 5, 4], matrix![2; -3, -4, -7, -4]); + } + + #[test] + fn neg() { + assert_eq!(-matrix![2; 1, 2, 3, 4], matrix![2; -1, -2, -3, -4]); + } + + #[test] + fn mul() { + let mut matrix = matrix![2; 1, 2, 3, 4]; + matrix *= 2; + assert_eq!(matrix, matrix![2; 2, 4, 6, 8]); + assert_eq!(matrix * 0, matrix![2; 0, 0, 0, 0]); + } + + #[test] + fn div() { + let mut matrix = matrix![2; 2, 5, 8, 9]; + matrix /= 2; + assert_eq!(matrix, matrix![2; 1, 2, 4, 4]); + assert_eq!(matrix / 4, matrix![2; 0, 0, 1, 1]); + } +} + diff --git a/src/math/matrix/iter.rs b/src/math/matrix/iter.rs new file mode 100644 index 0000000..186509c --- /dev/null +++ b/src/math/matrix/iter.rs @@ -0,0 +1,112 @@ +use super::*; + +use num::Num; + +impl Matrix { + pub fn indices(&self) -> IterIndices { + IterIndices { i: 0, j: 0, width: self.width, height: self.height() } + } + + pub fn into_iter(self) -> std::vec::IntoIter { + self.data.into_iter() + } + + pub fn into_iter_indexed(self) { + todo!() + } + + pub fn into_iter_rows(self) { + todo!() + } + + pub fn iter(&self) -> std::slice::Iter { + self.data.iter() + } + + pub fn iter_indexed(&self) -> IterIndexed { + IterIndexed { indices: self.indices(), matrix_iter: self.iter() } + } + + pub fn iter_rows(&self) -> std::slice::Chunks { + self.data.chunks(self.width) + } + + pub fn iter_mut(&mut self) -> std::slice::IterMut { + self.data.iter_mut() + } + + pub fn iter_indexed_mut(&mut self) -> IterIndexedMut { + IterIndexedMut { indices: self.indices(), matrix_iter_mut: self.iter_mut() } + } + + pub fn iter_rows_mut(&mut self) -> std::slice::ChunksMut { + self.data.chunks_mut(self.width) + } +} + +pub struct IterIndexed<'a, T: Num> { + indices: IterIndices, + matrix_iter: std::slice::Iter<'a, T> +} + +pub struct IterIndexedMut<'a, T: Num> { + indices: IterIndices, + matrix_iter_mut: std::slice::IterMut<'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.indices.next() + .zip(self.matrix_iter.next()) + .map(|((i, j), e)| (i, j, e)) + } +} + +impl<'a, T: Num> Iterator for IterIndexedMut<'a, T> { + type Item = (usize, usize, &'a mut T); + fn next(&mut self) -> Option { + self.indices.next() + .zip(self.matrix_iter_mut.next()) + .map(|((i, j), e)| (i, j, e)) + } +} + +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)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn iter() { + let data = vec![1,2,3,4,5,6,7,8,9]; + let mut matrix = Matrix::new(data.clone(), 3); + for (i, e) in matrix.iter().enumerate() { + assert_eq!(data[i], *e); + } + for (i, e) in matrix.iter_mut().enumerate() { + *e += 2; + assert_eq!(data[i] + 2, *e); + } + for (i, e) in matrix.into_iter().enumerate() { + assert_eq!(data[i] + 2, e); + } + } +} + diff --git a/src/math/matrix/ops.rs b/src/math/matrix/ops.rs new file mode 100644 index 0000000..d449db8 --- /dev/null +++ b/src/math/matrix/ops.rs @@ -0,0 +1,99 @@ +use super::*; + +use num::Num; + +impl Matrix { + 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 } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn minor() { + let matrix = matrix![4; + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12 + ]; + assert_eq!(matrix.minor(0, 0), matrix![3; + 6, 7, 8, + 10, 11, 12 + ]); + assert_eq!(matrix.minor(1, 2), matrix![3; + 1, 2, 4, + 9, 10, 12 + ]); + assert_eq!(matrix.minor(0, 0).minor(0, 0), matrix![2; + 11, 12 + ]); + } + + #[test] + #[should_panic(expected = "minor of a 2x2 matrix")] + fn minor_2x2_panic() { + matrix![2; + 1, 2, + 3, 4 + ].minor(0, 0); + } + + #[test] + #[should_panic(expected = "width must be greater")] + fn minor_1xn_panic() { + matrix![1; 1, 2, 3, 4].minor(0, 0); + } + + #[test] + #[should_panic(expected = "height must be greater")] + 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 + ]); + } +} diff --git a/src/math/sq_matrix.rs b/src/math/sq_matrix.rs index abb63a3..c531561 100644 --- a/src/math/sq_matrix.rs +++ b/src/math/sq_matrix.rs @@ -45,6 +45,8 @@ fn check_size(order: usize) -> usize { #[macro_export] macro_rules! sq_matrix { [ $o:expr; $( $x:expr ),+ ] => { - SquareMatrix::new(vec![$( $x, )+], $o) + $crate::math::SquareMatrix::new(vec![$( $x, )+], $o) }; } + +pub use sq_matrix;