From 881236c7254d416588286740c707bf1b7a5ba79d Mon Sep 17 00:00:00 2001 From: erius Date: Fri, 24 May 2024 17:18:52 +0300 Subject: [PATCH] Generic type T for Matrix and SquareMatrix is now bound by the trait Copy instead of Clone Removed all explicit calls to clone and slightly modified matrix operations (due to the change mentioned above) Added methods remove_row, remove_column, remove_last_row and remove_last_column + tests Minor SquareMatrix refactor Added matrix style in index.scss and removed html selector Implemented "Add row" and "Add column" buttons for yew matrix component --- math/src/matrix.rs | 88 ++++++++++++++++++++++++++++++++++- math/src/matrix/arithemtic.rs | 72 ++++++++++++++-------------- math/src/matrix/iter.rs | 2 +- math/src/matrix/ops.rs | 6 +-- math/src/sq_matrix.rs | 26 +++++------ web/index.scss | 29 ++++++++---- web/src/main.rs | 42 ++++++++++------- 7 files changed, 184 insertions(+), 81 deletions(-) diff --git a/math/src/matrix.rs b/math/src/matrix.rs index 5f677bb..097e235 100644 --- a/math/src/matrix.rs +++ b/math/src/matrix.rs @@ -12,7 +12,7 @@ pub struct Matrix { data: Box> } -impl Matrix { +impl Matrix { pub fn new_filled(val: T, width: usize, height: usize) -> Self { let Some(size) = width.checked_mul(height) else { @@ -34,7 +34,7 @@ impl Matrix { } pub fn column(&self, j: usize) -> Vec { - self.iter_column(j).cloned().collect() + self.iter_column(j).copied().collect() } pub fn append_row_zeroes(&mut self) { @@ -44,6 +44,19 @@ impl Matrix { pub fn append_column_zeroes(&mut self) { self.append_column(vec![T::zero(); self.height()]) } + + pub fn remove_row(&mut self, i: usize) { + if self.height() <= 2 { + panic!("Unable to remove row of a matrix with height {}", self.height()); + } + let row_start = i * self.width; + self.data.copy_within(row_start+self.width.., row_start); + self.data.truncate(self.data.len() - self.width); + } + + pub fn remove_last_row(&mut self) { + self.remove_row(self.height() - 1); + } } impl Matrix { @@ -102,6 +115,21 @@ impl Matrix { } self.width += 1; } + + pub fn remove_column(&mut self, j: usize) { + if self.width <= 2 { + panic!("Unable to remove column of a matrix with height {}", self.width); + } + for i in 0..self.height() { + let remove_at = i * (self.width - 1) + j; + self.data.remove(remove_at); + } + self.width -= 1; + } + + pub fn remove_last_column(&mut self) { + self.remove_column(self.width - 1); + } } impl Index for Matrix { @@ -260,4 +288,60 @@ mod tests { 3, 4 ].append_column(vec![1, 2, 3]); } + + #[test] + fn remove_row_and_column() { + let mut matrix = matrix![3; + 1, 2, 3, + 4, 5, 6, + 7, 8, 9 + ]; + matrix.remove_row(1); + assert_eq!(matrix, matrix![3; + 1, 2, 3, + 7, 8, 9 + ]); + matrix.remove_column(1); + assert_eq!(matrix, matrix![2; + 1, 3, + 7, 9 + ]); + } + + #[test] + #[should_panic(expected = "Unable to remove")] + fn remove_row_wrong_height() { + matrix![2; + 1, 2, + 3, 4 + ].remove_row(1); + } + + #[test] + #[should_panic(expected = "Unable to remove")] + fn remove_column_wrong_height() { + matrix![2; + 1, 2, + 3, 4 + ].remove_column(1); + } + + #[test] + fn remove_last_row_and_column() { + let mut matrix = matrix![3; + 1, 2, 3, + 4, 5, 6, + 7, 8, 9 + ]; + matrix.remove_last_row(); + assert_eq!(matrix, matrix![3; + 1, 2, 3, + 4, 5, 6 + ]); + matrix.remove_last_column(); + assert_eq!(matrix, matrix![2; + 1, 2, + 4, 5 + ]); + } } diff --git a/math/src/matrix/arithemtic.rs b/math/src/matrix/arithemtic.rs index d393a5d..fa92df4 100644 --- a/math/src/matrix/arithemtic.rs +++ b/math/src/matrix/arithemtic.rs @@ -4,7 +4,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi use num::{traits::NumAssign, Num, Signed}; -impl Matrix { +impl Matrix { pub fn multiply(&self, rhs: &Self) -> Self { if !self.can_mul(&rhs) { panic!("Unable to multiply matrices with sizes {}x{} and {}x{}", @@ -22,109 +22,109 @@ impl Matrix { } } -impl Add for Matrix { +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()); + let width = self.width; + let mut data = Box::new(Vec::with_capacity(self.size())); + for (e1, e2) in self.into_iter().zip(rhs.into_iter()) { + data.push(e1 + e2); } - Self { width: self.width, data: new_data } + Self { width, data } } } -impl AddAssign for Matrix { +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(); + for (e1, e2) in self.iter_mut().zip(rhs.into_iter()) { + *e1 += e2; } } } -impl Neg for Matrix { +impl Neg for Matrix { type Output = Self; fn neg(mut self) -> Self::Output { for e in self.iter_mut() { - *e = -e.clone(); + *e = -*e; } return self; } } -impl Sub for Matrix { +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()); + let width = self.width; + let mut data = Box::new(Vec::with_capacity(self.size())); + for (e1, e2) in self.into_iter().zip(rhs.into_iter()) { + data.push(e1 - e2); } - Self { width: self.width, data: new_data } + Self { width, data } } } -impl SubAssign for Matrix { +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(); + for (e1, e2) in self.iter_mut().zip(rhs.into_iter()) { + *e1 -= e2; } } } -impl Mul for Matrix { +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()); + fn mul(mut self, rhs: T) -> Self::Output { + for e in self.iter_mut() { + *e = *e * rhs; } - Self { width: self.width, data: new_data } + return self; } } -impl MulAssign for Matrix { +impl MulAssign for Matrix { fn mul_assign(&mut self, rhs: T) { for e in self.iter_mut() { - *e *= rhs.clone(); + *e *= rhs; } } } -impl Div for Matrix { +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()); + fn div(mut self, rhs: T) -> Self::Output { + for e in self.iter_mut() { + *e = *e / rhs; } - Self { width: self.width, data: new_data } + return self; } } -impl DivAssign for Matrix { +impl DivAssign for Matrix { fn div_assign(&mut self, rhs: T) { for e in self.iter_mut() { - *e /= rhs.clone(); + *e /= rhs; } } } -impl Mul for Matrix { +impl Mul for Matrix { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { self.multiply(&rhs) diff --git a/math/src/matrix/iter.rs b/math/src/matrix/iter.rs index 5e10d0b..715b766 100644 --- a/math/src/matrix/iter.rs +++ b/math/src/matrix/iter.rs @@ -4,7 +4,7 @@ use std::{slice::{Chunks, ChunksMut, Iter, IterMut}, vec::IntoIter}; use num::Num; -impl Matrix { +impl Matrix { pub fn indices(&self) -> IterIndices { IterIndices { i: 0, j: 0, width: self.width, height: self.height() } } diff --git a/math/src/matrix/ops.rs b/math/src/matrix/ops.rs index d449db8..3cbfc32 100644 --- a/math/src/matrix/ops.rs +++ b/math/src/matrix/ops.rs @@ -2,7 +2,7 @@ use super::*; use num::Num; -impl Matrix { +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) @@ -18,7 +18,7 @@ impl Matrix { 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()); + minor_data.push(*e); } Self { width: minor_width, data: minor_data } } @@ -29,7 +29,7 @@ impl Matrix { for j in 0..self.width { let mut row_start = 0usize; for _ in 0..height { - transposed_data.push(self.data[row_start + j].clone()); + transposed_data.push(self.data[row_start + j]); row_start += self.width; } } diff --git a/math/src/sq_matrix.rs b/math/src/sq_matrix.rs index b259b70..6a684f4 100644 --- a/math/src/sq_matrix.rs +++ b/math/src/sq_matrix.rs @@ -1,21 +1,13 @@ use num::Num; use super::matrix::Matrix; -pub struct SquareMatrix { +pub struct SquareMatrix { matrix: Matrix } -impl SquareMatrix { - pub fn new_filled(val: T, order: usize) -> Self { - let size = check_size(order); - let data = vec![val; size]; - Self { - matrix: Matrix::new_unchecked(data, order) - } - } - - pub fn new_zeroes(order: usize) -> Self { - Self::new_filled(T::zero(), order) +impl SquareMatrix { + pub fn order(&self) -> usize { + self.matrix.width() } pub fn new(mut data: Vec, order: usize) -> Self { @@ -25,9 +17,15 @@ impl SquareMatrix { matrix: Matrix::new_unchecked(data, order) } } +} - pub fn order(&self) -> usize { - self.matrix.width() +impl SquareMatrix { + pub fn new_filled(val: T, order: usize) -> Self { + let size = check_size(order); + let data = vec![val; size]; + Self { + matrix: Matrix::new_unchecked(data, order) + } } } diff --git a/web/index.scss b/web/index.scss index 0e50fa8..20c0552 100644 --- a/web/index.scss +++ b/web/index.scss @@ -2,17 +2,28 @@ $bg-color: #2b2927; $text-color: #e2e19c; $cell-color: #473b34; -html, body { +body { background-color: $bg-color; color: $text-color; - position: absolute; - top: 50%; - left: 50%; } -.matrix-cell { - background-color: $cell-color; - color: $text-color; - border: solid 1px black; - width: 30pt; +.matrix { + display: grid; + gap: 50px; + grid-template-columns: auto 100px; + grid-template-rows: auto 100px; + justify-content: start; + + input { + background-color: $cell-color; + color: $text-color; + border: solid 1px black; + width: 30pt; + } + + button { + background-color: $cell-color; + color: $text-color; + border: solid 1px black; + } } diff --git a/web/src/main.rs b/web/src/main.rs index 914006f..31d1253 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -8,7 +8,9 @@ pub struct MatrixComponent { pub enum MatrixMsg { CellChange(Cell), AddRow, - AddColumn + AddColumn, + RemoveRow, + RemoveColumn } impl Component for MatrixComponent { @@ -23,30 +25,38 @@ impl Component for MatrixComponent { match msg { MatrixMsg::CellChange(cell) => self.matrix[cell.i][cell.j] = cell.value, MatrixMsg::AddRow => self.matrix.append_row_zeroes(), - MatrixMsg::AddColumn => self.matrix.append_column_zeroes() + MatrixMsg::AddColumn => self.matrix.append_column_zeroes(), + MatrixMsg::RemoveRow => (), + MatrixMsg::RemoveColumn => () } return true; } fn view(&self, ctx: &Context) -> Html { let on_cell_change = ctx.link().callback(MatrixMsg::CellChange); + let on_click_add_row = ctx.link().callback(|_| MatrixMsg::AddRow); + let on_click_add_column = ctx.link().callback(|_| MatrixMsg::AddColumn); html! { - { - self.matrix.iter_rows().enumerate().map(|(i, row)| html! { - { - row.iter().enumerate().map(|(j, e)| { - let cell = Cell { i, j, value: *e }; - html! { - +
+
- -
{ + self.matrix.iter_rows().enumerate().map(|(i, row)| html! { + { + row.iter().enumerate().map(|(j, e)| { + let cell = Cell { i, j, value: *e }; + html! { + + } + }).collect::() } + }).collect::() } - - }).collect::() - } -
+ +
+ + + + } } } @@ -79,7 +89,7 @@ impl Component for MatrixCellComponent { Cell { i: cell.i, j: cell.j, value: e.as_f64().unwrap_or_default() } ); html! { - + } } }