forked from TheAlgorithms/Rust
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmatrix_ops.rs
186 lines (175 loc) · 7.83 KB
/
matrix_ops.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Basic matrix operations using row vectors wrapped in column vectors as matrices.
// Supports i32, should be interchangeable for other types.
// Wikipedia reference: https://www.wikiwand.com/en/Matrix_(mathematics)
pub fn matrix_add(summand0: &[Vec<i32>], summand1: &[Vec<i32>]) -> Vec<Vec<i32>> {
// Add two matrices of identical dimensions
let mut result: Vec<Vec<i32>> = vec![];
if summand0.len() != summand1.len() {
panic!("Matrix dimensions do not match");
}
for row in 0..summand0.len() {
if summand0[row].len() != summand1[row].len() {
panic!("Matrix dimensions do not match");
}
result.push(vec![]);
for column in 0..summand1[0].len() {
result[row].push(summand0[row][column] + summand1[row][column]);
}
}
result
}
pub fn matrix_subtract(minuend: &[Vec<i32>], subtrahend: &[Vec<i32>]) -> Vec<Vec<i32>> {
// Subtract one matrix from another. They need to have identical dimensions.
let mut result: Vec<Vec<i32>> = vec![];
if minuend.len() != subtrahend.len() {
panic!("Matrix dimensions do not match");
}
for row in 0..minuend.len() {
if minuend[row].len() != subtrahend[row].len() {
panic!("Matrix dimensions do not match");
}
result.push(vec![]);
for column in 0..subtrahend[0].len() {
result[row].push(minuend[row][column] - subtrahend[row][column]);
}
}
result
}
// Disable cargo clippy warnings about needless range loops.
// As the iterating variable is used as index while multiplying,
// using the item itself would defeat the variables purpose.
#[allow(clippy::needless_range_loop)]
pub fn matrix_multiply(multiplier: &[Vec<i32>], multiplicand: &[Vec<i32>]) -> Vec<Vec<i32>> {
// Multiply two matching matrices. The multiplier needs to have the same amount
// of columns as the multiplicand has rows.
let mut result: Vec<Vec<i32>> = vec![];
let mut temp;
// Using variable to compare lenghts of rows in multiplicand later
let row_right_length = multiplicand[0].len();
for row_left in 0..multiplier.len() {
if multiplier[row_left].len() != multiplicand.len() {
panic!("Matrix dimensions do not match");
}
result.push(vec![]);
for column_right in 0..multiplicand[0].len() {
temp = 0;
for row_right in 0..multiplicand.len() {
if row_right_length != multiplicand[row_right].len() {
// If row is longer than a previous row cancel operation with error
panic!("Matrix dimensions do not match");
}
temp += multiplier[row_left][row_right] * multiplicand[row_right][column_right];
}
result[row_left].push(temp);
}
}
result
}
pub fn matrix_transpose(matrix: &[Vec<i32>]) -> Vec<Vec<i32>> {
// Transpose a matrix of any size
let mut result: Vec<Vec<i32>> = vec![Vec::with_capacity(matrix.len()); matrix[0].len()];
for row in matrix {
for col in 0..row.len() {
result[col].push(row[col]);
}
}
result
}
pub fn matrix_scalar_multiplication(matrix: &[Vec<i32>], scalar: i32) -> Vec<Vec<i32>> {
// Multiply a matrix of any size with a scalar
let mut result: Vec<Vec<i32>> = vec![Vec::with_capacity(matrix.len()); matrix[0].len()];
for row in 0..matrix.len() {
for column in 0..matrix[row].len() {
result[row].push(scalar * matrix[row][column]);
}
}
result
}
#[cfg(test)]
mod tests {
use super::matrix_add;
use super::matrix_multiply;
use super::matrix_scalar_multiplication;
use super::matrix_subtract;
use super::matrix_transpose;
#[test]
fn test_add() {
let input0: Vec<Vec<i32>> = vec![vec![1, 0, 1], vec![0, 2, 0], vec![5, 0, 1]];
let input1: Vec<Vec<i32>> = vec![vec![1, 0, 0], vec![0, 1, 0], vec![0, 0, 1]];
let input_wrong0: Vec<Vec<i32>> = vec![vec![1, 0, 0, 4], vec![0, 1, 0], vec![0, 0, 1]];
let input_wrong1: Vec<Vec<i32>> =
vec![vec![1, 0, 0], vec![0, 1, 0], vec![0, 0, 1], vec![1, 1, 1]];
let input_wrong2: Vec<Vec<i32>> = vec![vec![]];
let exp_result: Vec<Vec<i32>> = vec![vec![2, 0, 1], vec![0, 3, 0], vec![5, 0, 2]];
assert_eq!(matrix_add(&input0, &input1), exp_result);
let result0 = std::panic::catch_unwind(|| matrix_add(&input0, &input_wrong0));
assert!(result0.is_err());
let result1 = std::panic::catch_unwind(|| matrix_add(&input0, &input_wrong1));
assert!(result1.is_err());
let result2 = std::panic::catch_unwind(|| matrix_add(&input0, &input_wrong2));
assert!(result2.is_err());
}
#[test]
fn test_subtract() {
let input0: Vec<Vec<i32>> = vec![vec![1, 0, 1], vec![0, 2, 0], vec![5, 0, 1]];
let input1: Vec<Vec<i32>> = vec![vec![1, 0, 0], vec![0, 1, 3], vec![0, 0, 1]];
let input_wrong0: Vec<Vec<i32>> = vec![vec![1, 0, 0, 4], vec![0, 1, 0], vec![0, 0, 1]];
let input_wrong1: Vec<Vec<i32>> =
vec![vec![1, 0, 0], vec![0, 1, 0], vec![0, 0, 1], vec![1, 1, 1]];
let input_wrong2: Vec<Vec<i32>> = vec![vec![]];
let exp_result: Vec<Vec<i32>> = vec![vec![0, 0, 1], vec![0, 1, -3], vec![5, 0, 0]];
assert_eq!(matrix_subtract(&input0, &input1), exp_result);
let result0 = std::panic::catch_unwind(|| matrix_subtract(&input0, &input_wrong0));
assert!(result0.is_err());
let result1 = std::panic::catch_unwind(|| matrix_subtract(&input0, &input_wrong1));
assert!(result1.is_err());
let result2 = std::panic::catch_unwind(|| matrix_subtract(&input0, &input_wrong2));
assert!(result2.is_err());
}
#[test]
fn test_multiply() {
let input0: Vec<Vec<i32>> =
vec![vec![1, 2, 3], vec![4, 2, 6], vec![3, 4, 1], vec![2, 4, 8]];
let input1: Vec<Vec<i32>> = vec![vec![1, 3, 3, 2], vec![7, 6, 2, 1], vec![3, 4, 2, 1]];
let input_wrong0: Vec<Vec<i32>> = vec![
vec![1, 3, 3, 2, 4, 6, 6],
vec![7, 6, 2, 1],
vec![3, 4, 2, 1],
];
let input_wrong1: Vec<Vec<i32>> = vec![
vec![1, 3, 3, 2],
vec![7, 6, 2, 1],
vec![3, 4, 2, 1],
vec![3, 4, 2, 1],
];
let exp_result: Vec<Vec<i32>> = vec![
vec![24, 27, 13, 7],
vec![36, 48, 28, 16],
vec![34, 37, 19, 11],
vec![54, 62, 30, 16],
];
assert_eq!(matrix_multiply(&input0, &input1), exp_result);
let result0 = std::panic::catch_unwind(|| matrix_multiply(&input0, &input_wrong0));
assert!(result0.is_err());
let result1 = std::panic::catch_unwind(|| matrix_multiply(&input0, &input_wrong1));
assert!(result1.is_err());
}
#[test]
fn test_transpose() {
let input0: Vec<Vec<i32>> = vec![vec![1, 0, 1], vec![0, 2, 0], vec![5, 0, 1]];
let input1: Vec<Vec<i32>> = vec![vec![3, 4, 2], vec![0, 1, 3], vec![3, 1, 1]];
let exp_result1: Vec<Vec<i32>> = vec![vec![1, 0, 5], vec![0, 2, 0], vec![1, 0, 1]];
let exp_result2: Vec<Vec<i32>> = vec![vec![3, 0, 3], vec![4, 1, 1], vec![2, 3, 1]];
assert_eq!(matrix_transpose(&input0), exp_result1);
assert_eq!(matrix_transpose(&input1), exp_result2);
}
#[test]
fn test_matrix_scalar_multiplication() {
let input0: Vec<Vec<i32>> = vec![vec![3, 2, 2], vec![0, 2, 0], vec![5, 4, 1]];
let input1: Vec<Vec<i32>> = vec![vec![1, 0, 0], vec![0, 1, 0], vec![0, 0, 1]];
let exp_result1: Vec<Vec<i32>> = vec![vec![9, 6, 6], vec![0, 6, 0], vec![15, 12, 3]];
let exp_result2: Vec<Vec<i32>> = vec![vec![3, 0, 0], vec![0, 3, 0], vec![0, 0, 3]];
assert_eq!(matrix_scalar_multiplication(&input0, 3), exp_result1);
assert_eq!(matrix_scalar_multiplication(&input1, 3), exp_result2);
}
}