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
This commit is contained in:
Egor 2024-05-24 17:18:52 +03:00
parent e4e17953e2
commit 881236c725
7 changed files with 184 additions and 81 deletions

View file

@ -12,7 +12,7 @@ pub struct Matrix<T: Num> {
data: Box<Vec<T>>
}
impl<T: Num + Clone> Matrix<T> {
impl<T: Num + Copy> Matrix<T> {
pub fn new_filled(val: T, width: usize, height: usize) -> Self {
let Some(size) = width.checked_mul(height)
else {
@ -34,7 +34,7 @@ impl<T: Num + Clone> Matrix<T> {
}
pub fn column(&self, j: usize) -> Vec<T> {
self.iter_column(j).cloned().collect()
self.iter_column(j).copied().collect()
}
pub fn append_row_zeroes(&mut self) {
@ -44,6 +44,19 @@ impl<T: Num + Clone> Matrix<T> {
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<T: Num> Matrix<T> {
@ -102,6 +115,21 @@ impl<T: Num> Matrix<T> {
}
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<T: Num> Index<usize> for Matrix<T> {
@ -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
]);
}
}

View file

@ -4,7 +4,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi
use num::{traits::NumAssign, Num, Signed};
impl<T: Num + Clone> Matrix<T> {
impl<T: Num + Copy> Matrix<T> {
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<T: Num + Clone> Matrix<T> {
}
}
impl<T: Num + Clone> Add for Matrix<T> {
impl<T: Num + Copy> 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());
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<T: NumAssign + Clone> AddAssign for Matrix<T> {
impl<T: NumAssign + Copy> 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();
for (e1, e2) in self.iter_mut().zip(rhs.into_iter()) {
*e1 += e2;
}
}
}
impl<T: Num + Signed + Clone> Neg for Matrix<T> {
impl<T: Num + Signed + Copy> Neg for Matrix<T> {
type Output = Self;
fn neg(mut self) -> Self::Output {
for e in self.iter_mut() {
*e = -e.clone();
*e = -*e;
}
return self;
}
}
impl<T: Num + Clone> Sub for Matrix<T> {
impl<T: Num + Copy> 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());
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<T: NumAssign + Clone> SubAssign for Matrix<T> {
impl<T: NumAssign + Copy> 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();
for (e1, e2) in self.iter_mut().zip(rhs.into_iter()) {
*e1 -= e2;
}
}
}
impl<T: Num + Clone> Mul<T> for Matrix<T> {
impl<T: Num + Copy> 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());
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<T: NumAssign + Clone> MulAssign<T> for Matrix<T> {
impl<T: NumAssign + Copy> MulAssign<T> for Matrix<T> {
fn mul_assign(&mut self, rhs: T) {
for e in self.iter_mut() {
*e *= rhs.clone();
*e *= rhs;
}
}
}
impl<T: Num + Clone> Div<T> for Matrix<T> {
impl<T: Num + Copy> 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());
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<T: NumAssign + Clone> DivAssign<T> for Matrix<T> {
impl<T: NumAssign + Copy> DivAssign<T> for Matrix<T> {
fn div_assign(&mut self, rhs: T) {
for e in self.iter_mut() {
*e /= rhs.clone();
*e /= rhs;
}
}
}
impl<T: Num + Clone> Mul for Matrix<T> {
impl<T: Num + Copy> Mul for Matrix<T> {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
self.multiply(&rhs)

View file

@ -4,7 +4,7 @@ use std::{slice::{Chunks, ChunksMut, Iter, IterMut}, vec::IntoIter};
use num::Num;
impl<T: Num + Clone> Matrix<T> {
impl<T: Num> Matrix<T> {
pub fn indices(&self) -> IterIndices {
IterIndices { i: 0, j: 0, width: self.width, height: self.height() }
}

View file

@ -2,7 +2,7 @@ use super::*;
use num::Num;
impl<T: Num + Clone> Matrix<T> {
impl<T: Num + Copy> 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)
@ -18,7 +18,7 @@ impl<T: Num + Clone> Matrix<T> {
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<T: Num + Clone> Matrix<T> {
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;
}
}

View file

@ -1,21 +1,13 @@
use num::Num;
use super::matrix::Matrix;
pub struct SquareMatrix<T: Num + Clone> {
pub struct SquareMatrix<T: Num> {
matrix: Matrix<T>
}
impl<T: Num + Clone> SquareMatrix<T> {
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<T: Num> SquareMatrix<T> {
pub fn order(&self) -> usize {
self.matrix.width()
}
pub fn new(mut data: Vec<T>, order: usize) -> Self {
@ -25,9 +17,15 @@ impl<T: Num + Clone> SquareMatrix<T> {
matrix: Matrix::new_unchecked(data, order)
}
}
}
pub fn order(&self) -> usize {
self.matrix.width()
impl<T: Num + Copy> SquareMatrix<T> {
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)
}
}
}

View file

@ -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;
}
}

View file

@ -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<Self>) -> 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! {
<table> {
self.matrix.iter_rows().enumerate().map(|(i, row)| html! {
<tr> {
row.iter().enumerate().map(|(j, e)| {
let cell = Cell { i, j, value: *e };
html! {
<td>
<MatrixCellComponent cell={cell} on_cell_change={on_cell_change.clone()} />
</td>
<div class={classes!("matrix")}>
<table> {
self.matrix.iter_rows().enumerate().map(|(i, row)| html! {
<tr> {
row.iter().enumerate().map(|(j, e)| {
let cell = Cell { i, j, value: *e };
html! {
<td>
<MatrixCellComponent cell={cell} on_cell_change={on_cell_change.clone()} />
</td>
}
}).collect::<Html>()
}
</tr>
}).collect::<Html>()
}
</tr>
}).collect::<Html>()
}
</table>
</table>
<button onclick={on_click_add_row}>{"Add row"}</button>
<button onclick={on_click_add_column}>{"Add column"}</button>
</div>
}
}
}
@ -79,7 +89,7 @@ impl Component for MatrixCellComponent {
Cell { i: cell.i, j: cell.j, value: e.as_f64().unwrap_or_default() }
);
html! {
<input class="matrix-cell" type="text" {oninput}/>
<input type="text" {oninput}/>
}
}
}