1#[cfg(test)]
2mod tests {
3 use crate::core::{Expression, Number};
4 use crate::matrices::{CoreMatrixOps, Matrix};
5 use num_traits::{One, Zero};
6
7 const EPSILON: f64 = 1e-10;
8
9 fn is_approx_zero(expr: &Expression) -> bool {
10 match expr {
11 Expression::Number(Number::Integer(n)) => *n == 0,
12 Expression::Number(Number::Float(f)) => f.abs() < EPSILON,
13 Expression::Number(Number::Rational(r)) => r.is_zero(),
14 _ => expr.is_zero(),
15 }
16 }
17
18 fn is_approx_one(expr: &Expression) -> bool {
19 match expr {
20 Expression::Number(Number::Integer(n)) => *n == 1,
21 Expression::Number(Number::Float(f)) => (*f - 1.0).abs() < EPSILON,
22 Expression::Number(Number::Rational(r)) => r.is_one(),
23 _ => false,
24 }
25 }
26
27 #[test]
29 fn test_identity_inverse() {
30 let identity = Matrix::identity(3);
31 let inverse = identity.inverse();
32
33 assert!(matches!(inverse, Matrix::Identity(_)));
34
35 let product = identity
36 .multiply(&inverse)
37 .expect("identity * identity should succeed");
38 assert!(matches!(product, Matrix::Identity(_)));
39 }
40
41 #[test]
43 fn test_2x2_gauss_jordan_inverse() {
44 let matrix = Matrix::dense(vec![
45 vec![Expression::integer(1), Expression::integer(2)],
46 vec![Expression::integer(3), Expression::integer(4)],
47 ]);
48
49 let inverse = matrix.inverse();
50
51 let product = matrix
52 .multiply(&inverse)
53 .expect("matrix * inverse should succeed");
54
55 match product {
57 Matrix::Identity(ref id) => {
58 assert_eq!(id.size, 2, "Identity matrix should be 2x2");
59 }
60 Matrix::Diagonal(ref diag) => {
61 assert_eq!(diag.diagonal_elements.len(), 2);
62 for (i, elem) in diag.diagonal_elements.iter().enumerate() {
63 assert!(
64 is_approx_one(elem),
65 "Diagonal element {} = {:?} should be ~1",
66 i,
67 elem
68 );
69 }
70 }
71 Matrix::Dense(ref data) => {
72 assert_eq!(data.rows.len(), 2);
73 assert_eq!(data.rows[0].len(), 2);
74 assert!(
75 is_approx_one(&data.rows[0][0]),
76 "Diagonal [0,0] should be ~1"
77 );
78 assert!(
79 is_approx_zero(&data.rows[0][1]),
80 "Off-diagonal [0,1] should be ~0"
81 );
82 assert!(
83 is_approx_zero(&data.rows[1][0]),
84 "Off-diagonal [1,0] should be ~0"
85 );
86 assert!(
87 is_approx_one(&data.rows[1][1]),
88 "Diagonal [1,1] should be ~1"
89 );
90 }
91 _ => panic!(
92 "Expected identity, diagonal or dense matrix result, got {:?}",
93 product
94 ),
95 }
96 }
97
98 #[test]
100 fn test_3x3_gauss_jordan_inverse() {
101 let matrix = Matrix::dense(vec![
102 vec![
103 Expression::integer(2),
104 Expression::integer(1),
105 Expression::integer(0),
106 ],
107 vec![
108 Expression::integer(1),
109 Expression::integer(2),
110 Expression::integer(1),
111 ],
112 vec![
113 Expression::integer(0),
114 Expression::integer(1),
115 Expression::integer(2),
116 ],
117 ]);
118
119 let inverse = matrix.inverse();
120
121 let product = matrix
122 .multiply(&inverse)
123 .expect("matrix * inverse should succeed");
124
125 match product {
127 Matrix::Identity(ref id) => {
128 assert_eq!(id.size, 3, "Identity matrix should be 3x3");
129 }
130 Matrix::Diagonal(ref diag) => {
131 assert_eq!(diag.diagonal_elements.len(), 3);
132 for (i, elem) in diag.diagonal_elements.iter().enumerate() {
133 assert!(
134 is_approx_one(elem),
135 "Diagonal element {} = {:?} should be ~1",
136 i,
137 elem
138 );
139 }
140 }
141 Matrix::Dense(ref data) => {
142 assert_eq!(data.rows.len(), 3);
143 assert_eq!(data.rows[0].len(), 3);
144 for i in 0..3 {
145 for j in 0..3 {
146 if i == j {
147 assert!(
148 is_approx_one(&data.rows[i][j]),
149 "Diagonal [{},{}] = {:?} should be ~1",
150 i,
151 j,
152 data.rows[i][j]
153 );
154 } else {
155 assert!(
156 is_approx_zero(&data.rows[i][j]),
157 "Off-diagonal [{},{}] = {:?} should be ~0",
158 i,
159 j,
160 data.rows[i][j]
161 );
162 }
163 }
164 }
165 }
166 _ => panic!(
167 "Expected identity, diagonal or dense matrix result, got {:?}",
168 product
169 ),
170 }
171 }
172
173 #[test]
175 fn test_singular_matrix_detection() {
176 let singular = Matrix::dense(vec![
177 vec![Expression::integer(1), Expression::integer(2)],
178 vec![Expression::integer(2), Expression::integer(4)],
179 ]);
180
181 let inverse = singular.inverse();
182
183 if let Matrix::Dense(ref data) = inverse {
184 for row in &data.rows {
185 for elem in row {
186 assert!(elem.is_zero());
187 }
188 }
189 }
190 }
191
192 #[test]
194 fn test_inverse_of_inverse() {
195 let matrix = Matrix::dense(vec![
196 vec![Expression::integer(1), Expression::integer(2)],
197 vec![Expression::integer(3), Expression::integer(5)],
198 ]);
199
200 let inverse = matrix.inverse();
201 let double_inverse = inverse.inverse();
202
203 let product = matrix
204 .multiply(&double_inverse)
205 .expect("matrix * inverse should succeed");
206 let a_squared = matrix
207 .multiply(&matrix)
208 .expect("matrix * matrix should succeed");
209
210 if let (Matrix::Dense(prod_data), Matrix::Dense(a2_data)) = (&product, &a_squared) {
211 assert_eq!(prod_data.rows.len(), a2_data.rows.len());
212 assert_eq!(prod_data.rows[0].len(), a2_data.rows[0].len());
213 }
214 }
215
216 #[test]
218 fn test_inverse_product_property() {
219 let a = Matrix::dense(vec![
220 vec![Expression::integer(1), Expression::integer(2)],
221 vec![Expression::integer(0), Expression::integer(1)],
222 ]);
223 let b = Matrix::dense(vec![
224 vec![Expression::integer(2), Expression::integer(0)],
225 vec![Expression::integer(1), Expression::integer(1)],
226 ]);
227
228 let ab = a.multiply(&b).expect("a * b should succeed");
229 let ab_inv = ab.inverse();
230
231 let a_inv = a.inverse();
232 let b_inv = b.inverse();
233 let b_inv_a_inv = b_inv
234 .multiply(&a_inv)
235 .expect("b_inv * a_inv should succeed");
236
237 let test1 = ab.multiply(&ab_inv).expect("ab * ab_inv should succeed");
238 let test2 = ab
239 .multiply(&b_inv_a_inv)
240 .expect("ab * b_inv_a_inv should succeed");
241
242 if let (Matrix::Dense(t1_data), Matrix::Dense(t2_data)) = (&test1, &test2) {
243 assert_eq!(t1_data.rows.len(), t2_data.rows.len());
244 assert_eq!(t1_data.rows[0].len(), t2_data.rows[0].len());
245 }
246 }
247
248 #[test]
250 fn test_inverse_structure_preservation() {
251 let matrix = Matrix::dense(vec![
252 vec![Expression::integer(3), Expression::integer(1)],
253 vec![Expression::integer(2), Expression::integer(1)],
254 ]);
255
256 let inverse = matrix.inverse();
257
258 assert!(matches!(inverse, Matrix::Dense(_)));
259
260 let (orig_rows, orig_cols) = matrix.dimensions();
261 let (inv_rows, inv_cols) = inverse.dimensions();
262 assert_eq!(orig_rows, inv_rows);
263 assert_eq!(orig_cols, inv_cols);
264
265 let product = matrix
266 .multiply(&inverse)
267 .expect("matrix * inverse should succeed");
268 let (prod_rows, prod_cols) = product.dimensions();
269 assert_eq!(prod_rows, 2);
270 assert_eq!(prod_cols, 2);
271 }
272}