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
This commit is contained in:
Egor 2024-05-17 13:33:56 +03:00
parent 25c1f55254
commit a2e311f295

View file

@ -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)] #[derive(PartialEq, Eq, Debug, Clone)]
pub struct Matrix<T: Num + Clone> { pub struct Matrix<T: Num> {
width: usize, width: usize,
data: Box<Vec<T>> data: Box<Vec<T>>
} }
@ -25,6 +25,41 @@ impl<T: Num + Clone> Matrix<T> {
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> {
pub fn new(mut data: Vec<T>, width: usize) -> Self { pub fn new(mut data: Vec<T>, width: usize) -> Self {
if width <= 0 { if width <= 0 {
panic!("Matrix width must be greater than 0, but got {}", width) panic!("Matrix width must be greater than 0, but got {}", width)
@ -54,24 +89,8 @@ impl<T: Num + Clone> Matrix<T> {
self.data.len() self.data.len()
} }
pub fn minor(&self, row_index: usize, column_index: usize) -> Self { pub fn indices(&self) -> IterIndices {
if self.width < 2 { IterIndices { i: 0, j: 0, width: self.width, height: self.height() }
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 into_iter(self) -> std::vec::IntoIter<T> { pub fn into_iter(self) -> std::vec::IntoIter<T> {
@ -90,8 +109,8 @@ impl<T: Num + Clone> Matrix<T> {
self.data.iter() self.data.iter()
} }
pub fn iter_indexed<'a>(&'a self) -> IterIndexed<'a, T> { pub fn iter_indexed(&self) -> IterIndexed<T> {
IterIndexed { i: 0, j: 0, width: self.width, matrix_iter: self.iter() } IterIndexed { indices: self.indices(), matrix_iter: self.iter() }
} }
pub fn iter_rows(&self) -> std::slice::Chunks<T> { pub fn iter_rows(&self) -> std::slice::Chunks<T> {
@ -102,41 +121,39 @@ impl<T: Num + Clone> Matrix<T> {
self.data.iter_mut() self.data.iter_mut()
} }
pub fn iter_indexed_mut<'a>(&'a mut self) -> IterIndexedMut<'a, T> { pub fn iter_indexed_mut(&mut self) -> IterIndexedMut<T> {
IterIndexedMut { i: 0, j: 0, width: self.width, matrix_iter_mut: self.iter_mut() } IterIndexedMut { i: 0, j: 0, width: self.width, matrix_iter_mut: self.iter_mut() }
} }
pub fn iter_mut_rows(&mut self) -> std::slice::ChunksMut<T> { pub fn iter_rows_mut(&mut self) -> std::slice::ChunksMut<T> {
self.data.chunks_mut(self.width) self.data.chunks_mut(self.width)
} }
} }
pub struct IterIndexed<'a, T: Num + Clone> { pub struct IterIndexed<'a, T: Num> {
i: usize, j: usize, width: usize, indices: IterIndices,
matrix_iter: std::slice::Iter<'a, T> 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, i: usize, j: usize, width: usize,
matrix_iter_mut: std::slice::IterMut<'a, T> 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); type Item = (usize, usize, &'a T);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.matrix_iter.next().map(|e| { self.indices.next()
let next = (self.i, self.j, e); .zip(self.matrix_iter.next())
self.j += 1; .map(|((i, j), e)| (i, j, e))
if self.j >= self.width {
self.i += 1;
self.j = 0;
}
next
})
} }
} }
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); type Item = (usize, usize, &'a mut T);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.matrix_iter_mut.next().map(|e| { self.matrix_iter_mut.next().map(|e| {
@ -151,7 +168,20 @@ impl<'a, T: Num + Clone> Iterator for IterIndexedMut<'a, T> {
} }
} }
impl<T: Num + Clone> Index<usize> for Matrix<T> { 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))
}
}
impl<T: Num> Index<usize> for Matrix<T> {
type Output = [T]; type Output = [T];
fn index(&self, index: usize) -> &Self::Output { fn index(&self, index: usize) -> &Self::Output {
let row_start = index * self.width; let row_start = index * self.width;
@ -159,26 +189,84 @@ impl<T: Num + Clone> Index<usize> for Matrix<T> {
} }
} }
impl<T: Num + Clone> IndexMut<usize> for Matrix<T> { impl<T: Num> IndexMut<usize> for Matrix<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output { fn index_mut(&mut self, index: usize) -> &mut Self::Output {
let row_start = index * self.width; let row_start = index * self.width;
&mut self.data[row_start..row_start + self.width] &mut self.data[row_start..row_start + self.width]
} }
} }
impl<T: Num + Clone> Index<(usize, usize)> for Matrix<T> { impl<T: Num> Index<(usize, usize)> for Matrix<T> {
type Output = T; type Output = T;
fn index(&self, index: (usize, usize)) -> &Self::Output { fn index(&self, index: (usize, usize)) -> &Self::Output {
&self.data[index.0 * self.width + index.1] &self.data[index.0 * self.width + index.1]
} }
} }
impl<T: Num + Clone> IndexMut<(usize, usize)> for Matrix<T> { impl<T: Num> IndexMut<(usize, usize)> for Matrix<T> {
fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output { fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
&mut self.data[index.0 * self.width + index.1] &mut self.data[index.0 * self.width + index.1]
} }
} }
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 ),+ ] => {
@ -289,4 +377,47 @@ mod tests {
fn minor_nx1_panic() { fn minor_nx1_panic() {
matrix![4; 1, 2, 3, 4].minor(0, 0); 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]);
}
} }