tinymatrix/
lib.rs

1#[derive(Debug, Clone)]
2pub struct Matrix {
3    rows: usize,
4    cols: usize,
5    data: Vec<f64>,
6}
7
8impl Matrix {
9
10    /// Returns a new Matrix given the rows and columns. The matrix is full of 0's
11    /// # Arguments
12    /// * `rows` - The amount of rows of the matrix
13    /// * `cols` - The amount of columns of the matrix
14    /// # Examples
15    /// ```
16    /// use tinymatrix::Matrix;
17    /// let matrix = Matrix::new(2, 2);
18    /// ```
19    pub fn new(rows: usize, cols: usize) -> Self {
20        Matrix {
21            rows,
22            cols,
23            data: vec![0.0; rows * cols],
24        }
25    }
26
27    /// Returns a new Matrix given the rows, columns and all the data inside the matrix.
28    /// # Arguments
29    /// * `rows` - The amount of rows of the matrix
30    /// * `cols` - The amount of columns of the matrix
31    /// * `values` - The data that the Matrix is created from (needs to be an 1D Vector)
32    /// # Examples
33    /// ```
34    /// use tinymatrix::Matrix;
35    /// let vector = vec![
36    ///     1.0, 2.0,
37    ///     3.0, 4.0
38    /// ];
39    /// let matrix = Matrix::from_vector(2, 2, vector);
40    /// ```
41    pub fn from_vector(rows: usize, cols: usize, values: Vec<f64>) -> Self {
42        if rows * cols != values.len() {
43            panic!("The vector input needs to match with the amount of columns and rows!");
44        } else {
45            Matrix {
46                rows,
47                cols,
48                data: values,
49            }
50        }
51    }
52
53    /// Returns the data given the coordinates (row and column)
54    /// # Arguments
55    /// * `row` - The row where the data is located
56    /// * `col` - The column where the data is located
57    /// # Examples
58    /// ```
59    /// use tinymatrix::Matrix;
60    /// let vector = vec![
61    ///     1.0, 2.0,
62    ///     3.0, 4.0
63    /// ];
64    /// let matrix = Matrix::from_vector(2, 2, vector);
65    /// matrix.get(0, 0);
66    /// ```
67    /// # Returns
68    /// The value on that coordinate
69    pub fn get(&self, row: usize, col: usize) -> f64 {
70        self.data[row * self.cols + col]
71    }
72
73    /// Sets a value given the coordinates (row and column) and the value
74    /// # Arguments
75    /// * `row` - The row where the data is located
76    /// * `col` - The column where the data is located
77    /// * `value` - The value that is being set.
78    /// # Examples
79    /// ```
80    /// use tinymatrix::Matrix;
81    /// let vector = vec![
82    ///     1.0, 2.0,
83    ///     3.0, 4.0
84    /// ];
85    /// let mut matrix = Matrix::from_vector(2, 2, vector);
86    /// matrix.set(0, 0, 1.5);
87    /// ```
88    pub fn set(&mut self, row: usize, col: usize, value: f64) {
89        self.data[row * self.cols + col] = value;
90    }
91
92    /// Returns if the matrix is squared (e.g the amount of columns is equal to the amount of rows)
93    /// # Examples
94    /// ```
95    /// use tinymatrix::Matrix;
96    /// let vector = vec![
97    ///     1.0, 2.0,
98    ///     3.0, 4.0
99    /// ];
100    /// let matrix = Matrix::from_vector(2, 2, vector);
101    /// matrix.is_squared(); // Should return true
102    /// ```
103    /// # Returns
104    /// A boolean based on the result.
105    pub fn is_squared(&self) -> bool {
106        if self.cols == self.rows {
107            return true;
108        } else {
109            return false;
110        }
111    }
112    /// Returns the main diagonal, all items above and below the main diagonal of a Matrix (only if it's squared)
113    /// # Examples
114    /// ```
115    /// use tinymatrix::Matrix;
116    /// let vector = vec![
117    ///     1.0, 2.0,
118    ///     3.0, 4.0
119    /// ];
120    /// let matrix = Matrix::from_vector(2, 2, vector);
121    /// let (main_diagonal, above_diagonal, below_diagonal) = matrix.main_diagonal();
122    /// ```
123    /// # Returns
124    /// The main diagonal, all numbers above the main diagoan and all numbers below the main diagonal. All diagonals are Vec<f64> (Vectors)
125    pub fn main_diagonal(&self) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
126        if self.is_squared() {
127            let mut m_diagonal: Vec<f64> = Vec::new();
128            let mut a_diagonal: Vec<f64> = Vec::new();
129            let mut b_diagonal: Vec<f64> = Vec::new();
130
131            for i in 0..self.rows {
132                for j in 0..self.cols {
133                    if i == j {
134                        m_diagonal.push(self.get(i, i));
135                    } else if i > j {
136                        b_diagonal.push(self.get(i, j));
137                    } else {
138                        a_diagonal.push(self.get(i, j));
139                    }
140                }
141            }
142            (m_diagonal, a_diagonal, b_diagonal)
143        } else {
144            panic!("The matrix needs to be squared!")
145        }
146    }
147
148    /// Returns if the matrix is upper triangular (e.g the matrix is squared and all numbers below the main diagonal are zero)
149    /// # Examples
150    /// ```
151    /// use tinymatrix::Matrix;
152    /// let vector = vec![
153    ///     1.0, 2.0,
154    ///     0.0, 4.0
155    /// ];
156    /// let matrix = Matrix::from_vector(2, 2, vector);
157    /// matrix.is_u_triangular(); // Should return true
158    /// ```
159    /// # Returns
160    /// A boolean based on the result.
161    pub fn is_u_triangular(&self) -> bool {
162        if !self.is_squared() {
163            return false;
164        } else {
165            let (_, _, b_diagonal) = self.main_diagonal();
166            let mut z = 0;
167            for i in 0..b_diagonal.len() {
168                if b_diagonal[i] == 0.0 {
169                    z += 1;
170                }
171            }
172            if z == b_diagonal.len() {
173                return true;
174            } else {
175                return false;
176            }
177        }
178    }
179
180    /// Returns if the matrix is lower triangular (e.g the matrix is squared and all numbers above the main diagonal are zero)
181    /// # Examples
182    /// ```
183    /// use tinymatrix::Matrix;
184    /// let vector = vec![
185    ///     1.0, 0.0,
186    ///     1.0, 4.0
187    /// ];
188    /// let matrix = Matrix::from_vector(2, 2, vector);
189    /// matrix.is_l_triangular(); // Should return true
190    /// ```
191    /// # Returns
192    /// A boolean based on the result.
193    pub fn is_l_triangular(&self) -> bool {
194        if !self.is_squared() {
195            return false;
196        } else {
197            let (_, a_diagonal, _) = self.main_diagonal();
198            let mut z = 0;
199            for i in 0..a_diagonal.len() {
200                if a_diagonal[i] == 0.0 {
201                    z += 1;
202                }
203            }
204            if z == a_diagonal.len() {
205                return true;
206            } else {
207                return false;
208            }
209        }
210    }
211
212    /// Returns a new Matrix when given two matrices and both matrices have the same number of rows, join all rows 
213    /// # Arguments
214    /// * `other` - The matrix that is concatenating all rows.
215    /// # Examples
216    /// ```
217    /// use tinymatrix::Matrix;
218    /// let vector1 = vec![
219    ///     1.0, 2.0,
220    ///     3.0, 4.0
221    /// ];
222    /// let vector2 = vec![
223    ///     5.0, 6.0,
224    ///     7.0, 8.0
225    /// ];
226    /// let matrix1 = Matrix::from_vector(2, 2, vector1);
227    /// let matrix2 = Matrix::from_vector(2, 2, vector2);
228    /// let joined_rows = matrix1.concat_rows(&matrix2);
229    /// ```
230    /// # Returns
231    /// A new matrix with all rows joint.
232    pub fn concat_rows(&self, other: &Self) -> Self {
233        if self.rows != other.rows {
234            panic!("Both matrices need to have the same amount of rows!");
235        } else {
236            let mut result: Vec<f64> = Vec::with_capacity(self.data.len() + other.data.len());
237
238            for i in 0..self.rows {
239                let s_index1 = i * self.cols;
240                let e_index1 = s_index1 + self.cols;
241                result.extend_from_slice(&self.data[s_index1..e_index1]);
242
243                let s_index2 = i * other.cols;
244                let e_index2 = s_index2 + other.cols;
245                result.extend_from_slice(&other.data[s_index2..e_index2]);
246            }
247
248            Matrix {
249                rows: self.rows,
250                cols: self.cols * 2,
251                data: result,
252            }
253        }
254    }
255
256    /// Returns a new Matrix when given two matrices and they have the same amount of columns, join all columns 
257    /// # Arguments
258    /// * `other` - The matrix that is concatenating all columns.
259    /// # Examples
260    /// ```
261    /// use tinymatrix::Matrix;
262    /// let vector1 = vec![
263    ///     1.0, 2.0,
264    ///     3.0, 4.0
265    /// ];
266    /// let vector2 = vec![
267    ///     5.0, 6.0,
268    ///     7.0, 8.0
269    /// ];
270    /// let matrix1 = Matrix::from_vector(2, 2, vector1);
271    /// let matrix2 = Matrix::from_vector(2, 2, vector2);
272    /// let joined_rows = matrix1.concat_cols(&matrix2);
273    /// ```
274    /// # Returns
275    /// A new matrix with all columns joint.
276    pub fn concat_cols(&self, other: &Self) -> Self {
277        if self.cols != other.cols {
278            panic!("Both matrices needs to have the same amount of columns!");
279        } else {
280            let mut result: Vec<f64> = Vec::with_capacity(self.data.len() + other.data.len());
281
282            for i in 0..self.rows {
283                for j in 0..self.cols {
284                    result.push(self.data[i * self.cols + j]);
285                }
286
287                for j in 0..other.cols {
288                    result.push(other.data[i * other.cols + j]);
289                }
290            }
291
292            Matrix {
293                rows: self.rows,
294                cols: self.cols + other.cols,
295                data: result,
296            }
297        }
298    }
299
300    pub fn transpose(&self) -> Self {
301        let mut result = Matrix::new(self.cols, self.rows);
302        for i in 0..self.rows {
303            for j in 0..self.cols {
304                result.set(j, i, self.get(i, j));
305            }
306        }
307        result
308    }
309    /// Returns the identity matrix of a matrix. 
310    /// # Examples
311    /// ```
312    /// use tinymatrix::Matrix;
313    /// let vector = vec![
314    ///     1.0, 2.0,
315    ///     3.0, 4.0
316    /// ];
317    /// let matrix = Matrix::from_vector(2, 2, vector);
318    /// let identity = matrix.identity();
319    /// ```
320    /// # Returns
321    /// A new matrix with all the main diagonal 1 and all the other elements zero.
322    pub fn identity(&self) -> Self {
323        let mut result = Matrix::new(self.rows, self.cols);
324        for i in 0..self.rows {
325            result.data[i * self.rows + i] = 1.0;
326        }
327        result
328    }
329
330    pub fn determinant(&self) -> Self {
331        unimplemented!("The determinant of a matrix");
332    }
333
334    pub fn lu_decomposition(&self) -> Self {
335        unimplemented!("The LU decomposition of a matrix");   
336    }
337
338    /// Prints the matrix in a nice way. Useful for debugging.
339    /// # Examples
340    /// ```
341    /// use tinymatrix::Matrix;
342    /// let vector = vec![
343    ///     1.0, 2.0,
344    ///     3.0, 4.0
345    /// ];
346    /// let matrix = Matrix::from_vector(2, 2, vector);
347    /// matrix.print_matrix();
348    /// ```
349    pub fn print_matrix(&self) {
350        for i in 0..self.rows {
351            for j in 0..self.cols {
352                print!("{:.2} ", self.get(i, j))
353            }
354            println!();
355        }
356        println!();
357    }
358}
359
360impl std::ops::Mul<Matrix> for Matrix {
361    type Output = Matrix;
362    fn mul(self, other: Matrix) -> Self::Output {
363        if self.cols != other.rows {
364            panic!(
365                "The amount of columns needs to be equal to the amount of rows for multiplication!"
366            )
367        } else {
368            let mut result = Matrix::new(self.rows, other.cols);
369            for i in 0..self.rows {
370                for j in 0..other.cols {
371                    let mut sum = 0.0;
372                    for k in 0..self.cols {
373                        sum += self.get(i, k) * other.get(k, j);
374                    }
375                    result.set(i, j, sum);
376                }
377            }
378            result
379        }
380    }
381}
382
383impl std::ops::Mul<i32> for Matrix {
384    type Output = Matrix;
385    fn mul(mut self, other: i32) -> Self::Output {
386        for i in self.data.iter_mut() {
387            *i *= other as f64;
388        }
389        self
390    }
391}
392
393impl std::ops::Div<i32> for Matrix {
394    type Output = Matrix;
395    fn div(mut self, other: i32) -> Self::Output {
396        for i in self.data.iter_mut() {
397            *i /= other as f64;
398        }
399        self
400    }
401}
402
403
404impl std::ops::Add<Matrix> for Matrix {
405    type Output = Matrix;
406
407    fn add(self, other: Matrix) -> Self::Output {
408        if self.cols != other.rows {
409            panic!(
410                "The amount of columns needs to be equal to the amount of rows for multiplication!"
411            )
412        } else {
413            Matrix {
414                rows: self.rows,
415                cols: self.cols,
416                data: self
417                    .data
418                    .iter()
419                    .zip(other.data.iter())
420                    .map(|(&a, &b)| a + b)
421                    .collect(),
422            }
423        }
424    }
425}
426
427impl std::ops::Sub<Matrix> for Matrix {
428    type Output = Matrix;
429
430    fn sub(self, other: Matrix) -> Self::Output {
431        if self.cols != other.rows {
432            panic!(
433                "The amount of columns needs to be equal to the amount of rows for multiplication!"
434            )
435        } else {
436            Matrix {
437                rows: self.rows,
438                cols: self.cols,
439                data: self
440                    .data
441                    .iter()
442                    .zip(other.data.iter())
443                    .map(|(&a, &b)| a - b)
444                    .collect(),
445            }
446        }
447    }
448}