diff options
author | Zhongheng Liu <z.liu@outlook.com.gr> | 2025-01-22 20:29:02 +0200 |
---|---|---|
committer | Zhongheng Liu <z.liu@outlook.com.gr> | 2025-01-22 20:29:02 +0200 |
commit | fcf4d05b8a0b72395f1f6fd8773b4094594120be (patch) | |
tree | cfd3537124984eb1dde34f1d354affafdb004c17 | |
parent | d59f83753e159dbe75509db121b10ff9b5049e3b (diff) | |
download | matrix-rs-fcf4d05b8a0b72395f1f6fd8773b4094594120be.tar.gz matrix-rs-fcf4d05b8a0b72395f1f6fd8773b4094594120be.tar.bz2 matrix-rs-fcf4d05b8a0b72395f1f6fd8773b4094594120be.zip |
feat: various fixes
clippy: rm unnecessary borrows
structure: move struct definitions around
tests: add unit test for matrix operations
errors: add custom error types
-rw-r--r-- | src/main.rs | 34 | ||||
-rw-r--r-- | src/tests.rs | 2 | ||||
-rw-r--r-- | src/tests/matrix_test.rs | 54 | ||||
-rw-r--r-- | src/types.rs | 4 | ||||
-rw-r--r-- | src/types/matrix.rs | 65 | ||||
-rw-r--r-- | src/types/matrix_err.rs | 18 |
6 files changed, 134 insertions, 43 deletions
diff --git a/src/main.rs b/src/main.rs index c55e199..0b7000d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,25 @@ -use std::io::stdin; +use std::{error::Error, io::stdin, str::FromStr}; -use types::matrix::Matrix; +#[cfg(test)] +mod tests; + +use types::{matrix::Matrix, matrix_err::ParseMatrixError}; mod types; -fn handle_input() -> Matrix { +fn handle_input() -> Result<Matrix, ParseMatrixError> { let input = stdin(); - let c = true; let mut construct_string = String::from(""); - while c { + loop { let mut s = "".to_string(); let _ = input.read_line(&mut s); - if s == "exit\n" { break; } + if s == "exit\n" { + break; + } construct_string += &s; } - // println!("Constructed \"{}\"", construct_string.trim_end()); - Matrix::from_str(construct_string.trim_end().to_string()) + Matrix::from_str(construct_string.trim_end()) } -fn main() { - // println!("Hello, world!"); - let m1 = Matrix::from_str("1,2,3\n4,5,6\n7,8,9".to_string()); - let m2 = Matrix::from_str("1,1,1\n1,1,1".to_string()); - let m4d = Matrix::from_str("1,2,3,4\n5,6,7,8\n9,18,11,12\n13,14,15,15".to_string()); - println!("Matrix:\n{}Has determinant:{}",&m1, &m1.determinant()); - println!("det(m4d): {}", &m4d.determinant()); - let mi = handle_input(); - println!("m from input:\n{}", mi); - // println!("row: {}, col: {}", mi.nrows, mi.ncols); - // println!("row: {}, col: {}", m2.nrows, m2.ncols); - println!("{}", &mi+&m2); +fn main() -> Result<(), Box<dyn Error>> { + let m = handle_input()?; + Ok(println!("The matrix is:\n{}", m)) } diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..a921a25 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,2 @@ +#[cfg(test)] +pub mod matrix_test; diff --git a/src/tests/matrix_test.rs b/src/tests/matrix_test.rs new file mode 100644 index 0000000..7a7461a --- /dev/null +++ b/src/tests/matrix_test.rs @@ -0,0 +1,54 @@ +use std::{error::Error, str::FromStr}; + +use crate::types::{matrix::Matrix, matrix_err::ParseMatrixError}; + +#[test] +pub fn test_matrix_init_from_string() -> Result<(), ParseMatrixError> { + let data_target = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; + let target = Matrix { + nrows: 3, + ncols: 3, + data: data_target, + }; + let test = Matrix::from_str("1,2,3\n4,5,6\n7,8,9")?; + assert_eq!(target, test); + Ok(()) +} +#[test] +pub fn test_matrix_add() -> Result<(), ParseMatrixError> { + let m1 = Matrix::from_str("1,2,3\n4,5,6\n7,8,9")?; + let m2 = Matrix::from_str("1,1,1\n1,1,1\n1,1,1")?; + let t = Matrix::from_str("2,3,4\n5,6,7\n8,9,10")?; + assert_eq!(&m1 + &m2, t); + Ok(()) +} +#[test] +pub fn test_matrix_determinate() -> Result<(), ParseMatrixError> { + let m = Matrix::from_str("3,4\n5,6")?; + let det = 3 * 6 - 4 * 5; + assert_eq!(m.determinant(), det); + Ok(()) +} +#[test] +pub fn test_matrix_transposition() -> Result<(), ParseMatrixError> { + let m = Matrix::from_str("1,2,3\n4,5,6\n7,8,9")?; + let t = Matrix::from_str("1,4,7\n2,5,8\n3,6,9")?; + assert_eq!(m.transpose(), t); + Ok(()) +} +#[test] +pub fn test_matrix_parse_malformed() -> () { + let malformed = "1,23,\n,567,\n\n5"; + let m = Matrix::from_str(malformed); + match m { + Ok(_) => panic!("This malformed matrix string should not have succeeded"), + Err(_) => (), + } +} +#[test] +#[should_panic] +pub fn test_matrix_add_bad_dimensions() -> () { + let bad = Matrix::from_str("1,1,1\n1,1,1").unwrap(); + let add = Matrix::from_str("1,1\n1,1").unwrap(); + let _ = &bad + &add; +} diff --git a/src/types.rs b/src/types.rs index 3bb3506..fd26a29 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1 +1,5 @@ +//! Type definition crate for the program +//! Contains a Matrix definition + pub mod matrix; +pub mod matrix_err; diff --git a/src/types/matrix.rs b/src/types/matrix.rs index d3b4333..eb0801a 100644 --- a/src/types/matrix.rs +++ b/src/types/matrix.rs @@ -1,10 +1,35 @@ use std::{fmt::Display, ops::Add, str::FromStr}; + +use super::matrix_err::{MatrixSetValueError, ParseMatrixError}; /// Matrix +#[derive(Debug, PartialEq, Eq)] pub struct Matrix { pub nrows: usize, pub ncols: usize, pub data: Vec<Vec<i32>>, } + + +impl FromStr for Matrix { + type Err = ParseMatrixError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let mut d: Vec<Vec<i32>> = Vec::new(); + let rows_iter = s.split('\n'); + for txt in rows_iter { + let mut r: Vec<i32> = Vec::new(); + for ch in txt.split(',') { + let parsed = match i32::from_str(ch) { + Ok(n) => Ok(n), + Err(_e) => Err(ParseMatrixError), + }; + r.push(parsed?); + } + d.push(r); + } + Ok(Matrix::new(d)) + } +} + impl Display for Matrix { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut builder = String::new(); @@ -44,7 +69,11 @@ impl<'a, 'b> Add<&'b Matrix> for &'a Matrix { x } } + + impl Matrix { + + /// Matrix initialiser function pub fn new(data: Vec<Vec<i32>>) -> Matrix { Matrix { nrows: data.len(), @@ -52,27 +81,19 @@ impl Matrix { data, } } - pub fn from_str(s: String) -> Matrix { - let mut d: Vec<Vec<i32>> = Vec::new(); - let rows_iter = s.split('\n'); - for (i, txt) in rows_iter.enumerate() { - let mut r: Vec<i32> = Vec::new(); - for (j, ch) in txt.split(',').enumerate() { - // println!("Put {} at {},{}", ch, i, j); - let parsed = match i32::from_str(ch) { - Ok(n) => n, - Err(e) => panic!("Err: {}", e), - }; - r.push(parsed); - } - d.push(r); - } - Matrix::new(d) + pub fn get(&self, row_index: usize, column_index: usize) -> Option<i32> { + let r = self.data.get(row_index)?; + let n = r.get(column_index)?; + Some(*n) + } + pub fn set(&mut self, row_index: usize, column_index: usize, new_data: i32) -> Result<(), MatrixSetValueError> { + self.data[row_index][column_index] = new_data; + Ok(()) } pub fn is_square(&self) -> bool { - &self.nrows == &self.ncols + self.nrows == self.ncols } - pub fn splice(&self, at_index: usize) -> Matrix { + fn splice(&self, at_index: usize) -> Matrix { let mut data: Vec<Vec<i32>> = Vec::new(); for i in 0..self.data.len() { if i == 0 { @@ -87,14 +108,12 @@ impl Matrix { } data.push(r); } - let m = Matrix::new(data); - // println!("Splice at {}: {}", at_index, m); - m + Matrix::new(data) } pub fn determinant(&self) -> i32 { if !self.is_square() { panic!() }; - if self.nrows == 2 && self.nrows == 2 { - return &self.data[0][0] * &self.data[1][1] - &self.data[0][1] * &self.data[1][0]; + if self.nrows == 2 && self.ncols == 2 { + return self.data[0][0] * self.data[1][1] - self.data[0][1] * self.data[1][0]; } let mut tmp = 0; for (i, n) in self.data[0].iter().enumerate() { diff --git a/src/types/matrix_err.rs b/src/types/matrix_err.rs new file mode 100644 index 0000000..f5309e1 --- /dev/null +++ b/src/types/matrix_err.rs @@ -0,0 +1,18 @@ +use std::{error::Error, fmt::Display}; + +#[derive(Debug, PartialEq, Eq)] +pub struct ParseMatrixError; +impl Display for ParseMatrixError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Matrix parsing error") + } +} +impl Error for ParseMatrixError {} +#[derive(Debug)] +pub struct MatrixSetValueError; +impl Error for MatrixSetValueError {} +impl Display for MatrixSetValueError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Matrix set value error") + } +} |