1use std::str::FromStr;
2
3use crate::{
4 matrix::traits::{
5 ArithmeticallyOperable, CheckedAdd, CheckedMul, CheckedSub, Identity, Matrix, Parseable,
6 Zero,
7 },
8 result::{MathError, Result},
9};
10
11use super::f32::MatrixF32;
12
13impl ArithmeticallyOperable<Result<MatrixF32>> for MatrixF32 {}
14
15impl FromStr for MatrixF32 {
16 type Err = MathError;
17
18 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
20 Self::parse(s, 1e-12)
21 }
22}
23
24impl PartialEq for MatrixF32 {
25 fn eq(&self, other: &Self) -> bool {
26 for i in 0..self.rows() {
27 for j in 0..self.columns() {
28 if (self.get(i, j).unwrap() - other.get(i, j).unwrap()).abs() > self.tolerance() {
29 return false;
30 }
31 }
32 }
33 true
34 }
35}
36
37impl Zero for MatrixF32 {
38 fn zero(rows: usize, columns: usize, tolerance: f32) -> Self {
39 MatrixF32::new(vec![vec![f32::zero(0, 0, 0.0); columns]; rows], tolerance).unwrap()
40 }
41
42 fn is_zero(&self) -> bool {
43 self.content
44 .iter()
45 .all(|row| row.iter().all(|x| x.is_zero()))
46 }
47}
48
49impl Identity for MatrixF32 {
50 fn id(dimensions: usize, tolerance: f32) -> Self {
51 let mut result = vec![vec![f32::zero(0, 0, 0.0); dimensions]; dimensions];
52 for i in 0..dimensions {
53 result[i][i] = f32::id(0, 0.0);
54 }
55 MatrixF32::new(result, tolerance).unwrap()
56 }
57}
58
59impl CheckedAdd for MatrixF32 {
60 type Output = Result<MatrixF32>;
61 fn checked_add(&self, rhs: &Self) -> Self::Output {
62 if self.rows() != rhs.rows() || self.columns() != rhs.columns() {
63 return Err(MathError::MatrixError(
64 "Matrices must be of the same size".to_string(),
65 ));
66 } else {
67 let mut result: Vec<Vec<f32>> = vec![];
68 for i in 0..self.rows() {
69 let mut result_row: Vec<f32> = vec![];
70 for j in 0..self.columns() {
71 let left = self.get(i, j)?.to_owned();
72 let right = rhs.get(i, j)?.to_owned();
73 result_row.push(left.checked_add(&right)?)
74 }
75 result.push(result_row)
76 }
77 MatrixF32::new(result, self.tolerance())
78 }
79 }
80}
81
82impl CheckedSub for MatrixF32 {
83 type Output = Result<MatrixF32>;
84
85 fn checked_sub(&self, rhs: &Self) -> Self::Output {
86 if self.rows() != rhs.rows() || self.columns() != rhs.columns() {
87 return Err(MathError::MatrixError(
88 "Matrices must be of the same size".to_string(),
89 ));
90 } else {
91 let mut result: Vec<Vec<f32>> = vec![];
92 for i in 0..self.rows() {
93 let mut result_row: Vec<f32> = vec![];
94 for j in 0..self.columns() {
95 let left = self.get(i, j)?.to_owned();
96 let right = rhs.get(i, j)?.to_owned();
97 result_row.push(left - right)
98 }
99 result.push(result_row)
100 }
101 MatrixF32::new(result, self.tolerance())
102 }
103 }
104}
105
106impl CheckedMul for MatrixF32 {
107 type Output = Result<MatrixF32>;
108
109 fn checked_mul(&self, rhs: &Self) -> Self::Output {
110 if self.columns() != rhs.rows() {
111 return Err(MathError::MatrixError(format!(
112 "Cannot multiply matrices of dimensions {}x{} and {}x{}",
113 self.rows(),
114 self.columns(),
115 rhs.rows(),
116 rhs.columns()
117 )));
118 }
119 let rows = self.rows();
120 let columns = rhs.columns();
121 let mut result = Vec::with_capacity(rows);
122 for i in 0..rows {
123 let mut row = Vec::with_capacity(columns);
124 for j in 0..columns {
125 let mut sum = f32::from_str("0")
126 .map_err(|_| MathError::MatrixError("Cannot build T from 0".to_string()))?;
127 for k in 0..self.columns() {
128 sum = sum + self.get(i, k)?.clone() * rhs.get(k, j)?.clone()
129 }
130 row.push(sum);
131 }
132 result.push(row)
133 }
134 MatrixF32::new(result, self.tolerance())
135 }
136}
137
138#[cfg(test)]
139mod test {
140 use crate::matrix::float::f32::MatrixF32;
141 use crate::matrix::traits::{CheckedAdd, CheckedSub, Parseable, CheckedMul};
142 use crate::matrix_f32;
143
144 const TOL: f32 = 1e-12;
145
146 #[test]
147 fn add_2x2_f32() {
148 let mat_a = matrix_f32!("{{1,1},{1,1}}", TOL).unwrap();
149 let mat_b = matrix_f32!("{{2,2},{2,2}}", TOL).unwrap();
150 let computed = mat_a.checked_add(&mat_b).unwrap();
151 let expected = matrix_f32!("{{3,3},{3,3}}", TOL).unwrap();
152
153 pretty_assertions::assert_eq!(computed, expected)
154 }
155
156 #[test]
157 fn add_3x5_f32() {
158 let mat_a = matrix_f32!("{{1,1,1,1,1}, {2,2,2,2,2}, {3,3,3,3,3}}", TOL).unwrap();
159 let mat_b = matrix_f32!("{ {3,3,3,3,3},{2,2,2,2,2}, {1,1,1,1,1}}", TOL).unwrap();
160 let computed = mat_a.checked_add(&mat_b).unwrap();
161 let expected = matrix_f32!("{{4,4,4,4,4},{4,4,4,4,4},{4,4,4,4,4}}", TOL).unwrap();
162
163 pretty_assertions::assert_eq!(computed, expected)
164 }
165
166 #[test]
167 fn add_different_rows_should_fail() {
168 let mat_a = matrix_f32!("{{1,1},{1,1}}", TOL).unwrap();
169 let mat_b = matrix_f32!("{{2,2},{2,2}, {2,2}}", TOL).unwrap();
170 let computed = mat_a.checked_add(&mat_b);
171 assert!(computed.is_err())
172 }
173
174 #[test]
175 fn add_different_cols_should_fail() {
176 let mat_a = matrix_f32!("{{1,1,1},{1,1,1}}", TOL).unwrap();
177 let mat_b = matrix_f32!("{{2,2},{2,2}}", TOL).unwrap();
178 let computed = mat_a.checked_add(&mat_b);
179 assert!(computed.is_err())
180 }
181
182 #[test]
183 fn sub_2x2_f32() {
184 let mat_a = matrix_f32!("{{1,1},{1,1}}", TOL).unwrap();
185 let mat_b = matrix_f32!("{{2,2},{2,2}}", TOL).unwrap();
186 let computed = mat_a.checked_sub(&mat_b).unwrap();
187 let expected = matrix_f32!("{{-1,-1},{-1,-1}}", TOL).unwrap();
188
189 pretty_assertions::assert_eq!(computed, expected)
190 }
191
192 #[test]
193 fn sub_3x5_f32() {
194 let mat_a = matrix_f32!("{{1,1,1,1,1}, {2,2,2,2,2}, {3,3,3,3,3}}", TOL).unwrap();
195 let mat_b = matrix_f32!("{ {3,3,3,3,3},{2,2,2,2,2}, {1,1,1,1,1}}", TOL).unwrap();
196 let computed = mat_a.checked_sub(&mat_b).unwrap();
197 let expected = matrix_f32!("{{-2,-2,-2,-2,-2},{0,0,0,0,0},{2,2,2,2,2}}", TOL).unwrap();
198
199 pretty_assertions::assert_eq!(computed, expected)
200 }
201
202 #[test]
203 fn sub_different_rows_should_fail() {
204 let mat_a = matrix_f32!("{{1,1},{1,1}}", TOL).unwrap();
205 let mat_b = matrix_f32!("{{2,2},{2,2}, {2,2}}", TOL).unwrap();
206 let computed = mat_a.checked_sub(&mat_b);
207 assert!(computed.is_err())
208 }
209
210 #[test]
211 fn sub_different_cols_should_fail() {
212 let mat_a = matrix_f32!("{{1,1,1},{1,1,1}}", TOL).unwrap();
213 let mat_b = matrix_f32!("{{2,2},{2,2}}", TOL).unwrap();
214 let computed = mat_a.checked_sub(&mat_b);
215 assert!(computed.is_err())
216 }
217
218 #[test]
219 fn mul_2x2_f32() {
220 let mat_a = matrix_f32!("{{1,1},{1,1}}", TOL).unwrap();
221 let mat_b = matrix_f32!("{{2,2},{2,2}}", TOL).unwrap();
222 let computed = mat_a.checked_mul(&mat_b).unwrap();
223 let expected = matrix_f32!("{{4,4},{4,4}}", TOL).unwrap();
224
225 pretty_assertions::assert_eq!(computed, expected)
226 }
227
228 #[test]
229 fn mul_3x5x2_f32() {
230 let mat_a = matrix_f32!("{{1,2,1,2,1}, {-1,2,-3,2,1}, {0,1,-3,2,1}}", TOL).unwrap();
231 let mat_b = matrix_f32!("{{1,1}, {2,2}, {-1,-1}, {-2,-2}, {0,1}}", TOL).unwrap();
232 let computed = mat_a.checked_mul(&mat_b).unwrap();
233 let expected = matrix_f32!("{{0,1},{2,3},{1,2}}", TOL).unwrap();
234
235 pretty_assertions::assert_eq!(computed, expected)
236 }
237}