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
This commit is contained in:
Egor 2024-05-18 12:48:59 +03:00
parent a2e311f295
commit 79d94bf357
6 changed files with 405 additions and 286 deletions

View file

@ -1,5 +1,7 @@
mod matrix; mod matrix;
pub use matrix::Matrix; pub use matrix::Matrix;
pub use matrix::matrix;
mod sq_matrix; mod sq_matrix;
pub use sq_matrix::SquareMatrix; pub use sq_matrix::SquareMatrix;
pub use sq_matrix::sq_matrix;

View file

@ -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)] #[derive(PartialEq, Eq, Debug, Clone)]
pub struct Matrix<T: Num> { pub struct Matrix<T: Num> {
@ -24,39 +28,6 @@ impl<T: Num + Clone> Matrix<T> {
pub fn new_zeroes(width: usize, height: usize) -> Self { pub fn new_zeroes(width: usize, height: usize) -> Self {
Self::new_filled(T::zero(), width, height) 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<T: Num> Matrix<T> { impl<T: Num> Matrix<T> {
@ -89,95 +60,8 @@ impl<T: Num> Matrix<T> {
self.data.len() self.data.len()
} }
pub fn indices(&self) -> IterIndices { pub fn same_size(&self, other: &Self) -> bool {
IterIndices { i: 0, j: 0, width: self.width, height: self.height() } return self.width == other.width && self.height() == other.height()
}
pub fn into_iter(self) -> std::vec::IntoIter<T> {
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<T> {
self.data.iter()
}
pub fn iter_indexed(&self) -> IterIndexed<T> {
IterIndexed { indices: self.indices(), matrix_iter: self.iter() }
}
pub fn iter_rows(&self) -> std::slice::Chunks<T> {
self.data.chunks(self.width)
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<T> {
self.data.iter_mut()
}
pub fn iter_indexed_mut(&mut self) -> IterIndexedMut<T> {
IterIndexedMut { i: 0, j: 0, width: self.width, matrix_iter_mut: self.iter_mut() }
}
pub fn iter_rows_mut(&mut self) -> std::slice::ChunksMut<T> {
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::Item> {
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::Item> {
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::Item> {
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))
} }
} }
@ -209,83 +93,27 @@ impl<T: Num> IndexMut<(usize, usize)> for Matrix<T> {
} }
} }
fn op<T, F>(this: Matrix<T>, rhs: Matrix<T>, op: F) -> Option<Matrix<T>>
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<T, F>(this: &mut Matrix<T>, rhs: Matrix<T>, 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<T: Num + Clone> Add for Matrix<T> {
type Output = Option<Self>;
fn add(self, rhs: Self) -> Self::Output {
op(self, rhs, T::add)
}
}
impl<T: NumAssign + Clone> AddAssign for Matrix<T> {
fn add_assign(&mut self, rhs: Self) {
op_assign(self, rhs, T::add_assign);
}
}
impl<T: Num + Signed + Clone> Neg for Matrix<T> {
type Output = Self;
fn neg(mut self) -> Self::Output {
for e in self.iter_mut() {
*e = -e.clone();
}
self
}
}
impl<T: Num + Clone> Sub for Matrix<T> {
type Output = Option<Self>;
fn sub(self, rhs: Self) -> Self::Output {
op(self, rhs, T::sub)
}
}
impl<T: NumAssign + Clone> SubAssign for Matrix<T> {
fn sub_assign(&mut self, rhs: Self) {
op_assign(self, rhs, T::sub_assign);
}
}
#[macro_export] #[macro_export]
macro_rules! matrix { macro_rules! matrix {
[ $w:expr; $( $x:expr ),+ ] => { [ $w:expr; $( $x:expr ),+ ] => {
Matrix::new(vec![$( $x, )+], $w) $crate::math::Matrix::new(vec![$( $x, )+], $w)
}; };
} }
pub use matrix;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
#[should_panic] #[should_panic(expected = "exceeds usize limit")]
fn size_too_big() { fn size_too_big() {
Matrix::<i32>::new_zeroes(usize::MAX, 2); Matrix::<i32>::new_zeroes(usize::MAX, 2);
} }
#[test] #[test]
#[should_panic] #[should_panic(expected = "size must be greater")]
fn size_too_small() { fn size_too_small() {
Matrix::<i32>::new_zeroes(1, 1); Matrix::<i32>::new_zeroes(1, 1);
} }
@ -320,104 +148,4 @@ mod tests {
matrix[2][1] = 0; matrix[2][1] = 0;
assert_eq!(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]);
}
} }

View file

@ -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<T: Num + Clone> Add for Matrix<T> {
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<T: NumAssign + Clone> AddAssign for Matrix<T> {
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<T: Num + Signed + Clone> Neg for Matrix<T> {
type Output = Self;
fn neg(mut self) -> Self::Output {
for e in self.iter_mut() {
*e = -e.clone();
}
return self;
}
}
impl<T: Num + Clone> Sub for Matrix<T> {
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<T: NumAssign + Clone> SubAssign for Matrix<T> {
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<T: Num + Clone> Mul<T> for Matrix<T> {
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<T: NumAssign + Clone> MulAssign<T> for Matrix<T> {
fn mul_assign(&mut self, rhs: T) {
for e in self.iter_mut() {
*e *= rhs.clone();
}
}
}
impl<T: Num + Clone> Div<T> for Matrix<T> {
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<T: NumAssign + Clone> DivAssign<T> for Matrix<T> {
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]);
}
}

112
src/math/matrix/iter.rs Normal file
View file

@ -0,0 +1,112 @@
use super::*;
use num::Num;
impl<T: Num + Clone> Matrix<T> {
pub fn indices(&self) -> IterIndices {
IterIndices { i: 0, j: 0, width: self.width, height: self.height() }
}
pub fn into_iter(self) -> std::vec::IntoIter<T> {
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<T> {
self.data.iter()
}
pub fn iter_indexed(&self) -> IterIndexed<T> {
IterIndexed { indices: self.indices(), matrix_iter: self.iter() }
}
pub fn iter_rows(&self) -> std::slice::Chunks<T> {
self.data.chunks(self.width)
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<T> {
self.data.iter_mut()
}
pub fn iter_indexed_mut(&mut self) -> IterIndexedMut<T> {
IterIndexedMut { indices: self.indices(), matrix_iter_mut: self.iter_mut() }
}
pub fn iter_rows_mut(&mut self) -> std::slice::ChunksMut<T> {
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::Item> {
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::Item> {
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::Item> {
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);
}
}
}

99
src/math/matrix/ops.rs Normal file
View file

@ -0,0 +1,99 @@
use super::*;
use num::Num;
impl<T: Num + Clone> Matrix<T> {
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
]);
}
}

View file

@ -45,6 +45,8 @@ fn check_size(order: usize) -> usize {
#[macro_export] #[macro_export]
macro_rules! sq_matrix { macro_rules! sq_matrix {
[ $o:expr; $( $x:expr ),+ ] => { [ $o:expr; $( $x:expr ),+ ] => {
SquareMatrix::new(vec![$( $x, )+], $o) $crate::math::SquareMatrix::new(vec![$( $x, )+], $o)
}; };
} }
pub use sq_matrix;