1use std::cmp::Ordering;
5use std::mem;
6use std::ops::{Mul, Neg, Sub};
7
8use num_traits::Zero;
9
10use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::decomposition::pivoting::{Markowitz, PivotRule};
11use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::LUDecomposition;
12use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::permutation::{FullPermutation, Permutation, SwapPermutation};
13use crate::algorithm::two_phase::tableau::inverse_maintenance::ops;
14
15mod pivoting;
16
17impl<F> LUDecomposition<F>
18where
19 F: ops::Field + ops::FieldHR,
20{
21 #[must_use]
27 pub fn rows(mut rows: Vec<Vec<(usize, F)>>) -> Self {
28 debug_assert!(rows.iter().all(|row| row.iter().is_sorted_by_key(|&(i, _)| i)));
29
30 let m = rows.len();
32 debug_assert!(m > 1);
33
34 let mut row_permutation = (0..m).collect::<Vec<_>>();
38 let mut column_permutation = (0..m).collect::<Vec<_>>();
39 let mut lower_triangular_row_major = vec![Vec::new(); m - 1];
40
41 let (mut nnz_row, mut nnz_column) = count_non_zeros(&rows);
43 let pivot_rule = Markowitz::new();
44 for k in 0..m {
45 let (pivot_row, pivot_column) = pivot_rule.choose_pivot(&nnz_row, &nnz_column, &rows, k);
46 swap(
48 pivot_row, pivot_column,
49 k,
50 &mut row_permutation, &mut column_permutation,
51 &mut nnz_row, &mut nnz_column,
52 &mut rows,
53 &mut lower_triangular_row_major,
54 );
55
56 for &(j, _) in &rows[k] {
58 nnz_row[k] -= 1;
59 nnz_column[j] -= 1;
60 }
61
62 let (current_row, remaining_rows) = {
64 let (current_row, remaining_rows) = rows[k..].split_first_mut().unwrap();
65 (&*current_row, remaining_rows)
66 };
67 let pivot_value = ¤t_row[0].1;
68
69 let mut ratios_to_subtract = Vec::with_capacity(nnz_column[k]);
72 for (i, row) in remaining_rows.iter_mut().enumerate() {
73 debug_assert!(!row.is_empty(), "The first item exists (invertibility).");
74 if row[0].0 == k {
75 ratios_to_subtract.push((i, row.remove(0).1 / pivot_value));
76 nnz_row[k + 1 + i] -= 1;
77 nnz_column[k] -= 1;
78 }
79 }
80
81 for (i, ratio) in ratios_to_subtract {
83 let (nnz_row_net_difference, nnz_column_removed, nnz_column_added) = subtract_multiple_of_row_from_other_row(
84 &mut remaining_rows[i],
85 &ratio,
86 ¤t_row[1..],
87 );
88
89 nnz_row[k + 1 + i] -= nnz_row_net_difference.0;
90 nnz_row[k + 1 + i] += nnz_row_net_difference.1;
91 for column in nnz_column_removed {
92 nnz_column[column] -= 1;
93 }
94 for column in nnz_column_added {
95 nnz_column[column] += 1;
96 }
97
98 lower_triangular_row_major[k + 1 + i - 1].push((k, ratio));
99 }
100
101 debug_assert_eq!(nnz_row[k], 0);
102 debug_assert_eq!(nnz_column[k], 0);
103 debug_assert!(nnz_row[(k + 1)..].iter()
104 .enumerate()
105 .all(|(i, count)| rows[k + 1 + i].len() == *count));
106 }
107
108 let mut upper_triangular = vec![Vec::new(); m - 1];
110 let mut upper_diagonal = Vec::with_capacity(m);
111 for (i, row) in rows.into_iter().enumerate() {
112 let mut iter = row.into_iter();
113 let (index, diagonal) = iter.next().unwrap();
114 debug_assert_eq!(index, i);
115 upper_diagonal.push(diagonal);
116 for (j, value) in iter {
117 upper_triangular[j - 1].push((i, value));
118 }
119 }
120 let mut lower_triangular = vec![Vec::new(); m - 1];
121 for (i, row) in lower_triangular_row_major.into_iter().enumerate()
122 .map(|(i, v)| (i + 1, v)) {
124 for (j, v) in row {
125 lower_triangular[j].push((i, v));
126 }
127 }
128
129 let mut row_permutation = FullPermutation::new(row_permutation);
131 row_permutation.invert();
132 let mut column_permutation = FullPermutation::new(column_permutation);
133 column_permutation.invert();
134
135 Self {
136 row_permutation,
137 column_permutation,
138 lower_triangular,
139 upper_triangular,
140 upper_diagonal,
141 updates: Vec::new(),
142 }
143 }
144}
145
146fn subtract_multiple_of_row_from_other_row<T>(
147 to_edit: &mut Vec<(usize, T)>,
148 ratio: &T,
149 being_removed: &[(usize, T)],
150) -> ((usize, usize), Vec<usize>, Vec<usize>)
151where
152 for<'r> T: Mul<&'r T, Output=T> + Sub<T, Output=T> + Zero + Eq,
153 for<'r> &'r T: Neg<Output=T> + Mul<&'r T, Output=T>,
154{
155 debug_assert!(!ratio.is_zero());
156
157 let mut column_nnz_added = Vec::new();
158 if to_edit.is_empty() {
159 let row_nnz_added = being_removed.len();
161 for (j, v) in being_removed {
162 to_edit.push((*j, -ratio * v));
163 column_nnz_added.push(*j);
164 }
165 ((0, row_nnz_added), Vec::with_capacity(0), column_nnz_added)
166 } else {
167 let old_row = mem::replace(to_edit, Vec::with_capacity(0));
168 let old_row_nnz = old_row.len();
169
170 let mut column_nnz_removed = Vec::new();
171 let mut new = Vec::new();
172
173 let mut index = 0;
174 for (j, old_value) in old_row {
175 while index < being_removed.len() && being_removed[index].0 < j {
176 new.push((being_removed[index].0, -ratio * &being_removed[index].1));
177 column_nnz_added.push(being_removed[index].0);
178 index += 1;
179 }
180
181 if index < being_removed.len() && being_removed[index].0 == j {
182 let product = ratio * &being_removed[index].1;
183 if product != old_value {
184 new.push((j, old_value - product));
185 } else {
186 column_nnz_removed.push(j);
187 }
188 index += 1;
189 } else {
190 new.push((j, old_value));
191 }
192 }
193 while index < being_removed.len() {
194 new.push((being_removed[index].0, -ratio * &being_removed[index].1));
195 column_nnz_added.push(being_removed[index].0);
196 index += 1;
197 }
198
199 let new_row_nnz = new.len();
200 *to_edit = new;
201
202 let (row_nnz_net_removed, row_nnz_net_added) = match new_row_nnz.cmp(&old_row_nnz) {
203 Ordering::Less => (old_row_nnz - new_row_nnz, 0),
204 Ordering::Equal => (0, 0),
205 Ordering::Greater => (0, new_row_nnz - old_row_nnz),
206 };
207
208 ((row_nnz_net_removed, row_nnz_net_added), column_nnz_removed, column_nnz_added)
209 }
210}
211
212fn swap<T>(
225 pivot_row: usize, pivot_column: usize,
226 k: usize,
227 row_permutation: &mut [usize], column_permutation: &mut [usize],
228 nnz_row: &mut [usize], nnz_column: &mut [usize],
229 rows: &mut [Vec<(usize, T)>],
230 lower_triangular_row_major: &mut [Vec<(usize, T)>],
231) {
232 let n = rows.len();
233 debug_assert!(pivot_row < n);
234 debug_assert!(pivot_column < n);
235 debug_assert!(k < n);
236 debug_assert_eq!(row_permutation.len(), n);
237 debug_assert_eq!(column_permutation.len(), n);
238 debug_assert_eq!(nnz_row.len(), n);
239 debug_assert_eq!(nnz_column.len(), n);
240 debug_assert_eq!(rows.len(), n);
241 debug_assert_eq!(lower_triangular_row_major.len(), n - 1);
242
243 debug_assert!(pivot_row >= k && pivot_column >= k);
245
246 if pivot_row != k {
247 row_permutation.swap(pivot_row, k);
248 nnz_row.swap(pivot_row, k);
249 rows.swap(pivot_row, k);
250
251 if pivot_row == 0 && k > 0 {
252 debug_assert!(lower_triangular_row_major[k - 1].is_empty());
253 }
254 if pivot_row > 0 && k == 0 {
255 debug_assert!(lower_triangular_row_major[pivot_row - 1].is_empty());
256 }
257 if pivot_row > 0 && k > 0 {
258 lower_triangular_row_major.swap(pivot_row - 1, k - 1);
260 }
261 }
262
263 if pivot_column != k {
264 column_permutation.swap(pivot_column, k);
265 nnz_column.swap(pivot_column, k);
266
267 let swap = SwapPermutation::new((pivot_column, k), n);
269 for row in rows {
270 swap.forward_sorted(row);
271 }
272 }
273}
274
275fn count_non_zeros<T>(rows: &[Vec<(usize, T)>]) -> (Vec<usize>, Vec<usize>) {
279 let n = rows.len();
280 debug_assert!(n > 0);
281 debug_assert!(rows.iter().all(|row| row.iter().all(|&(i, _)| i < n)));
282
283 let nnz_row = rows.iter()
284 .map(Vec::len)
285 .collect::<Vec<_>>();
286
287 let nnz_column = {
288 let mut counts = vec![0; n];
289 for column in rows {
290 for &(i, _) in column {
291 counts[i] += 1;
292 }
293 }
294 counts
295 };
296
297 debug_assert_eq!(nnz_column.len(), n);
298 debug_assert_eq!(nnz_row.len(), n);
299 debug_assert!(nnz_column.iter().all(|&count| count > 0));
301 debug_assert!(nnz_row.iter().all(|&count| count > 0));
302
303 (nnz_row, nnz_column)
304}
305
306#[cfg(test)]
307mod test {
308 use relp_num::{RB, R8, RationalBig, NonZero, Rational8};
309
310 use crate::algorithm::two_phase::matrix_provider::column::{Column, SparseSliceIterator, DenseSliceIterator};
311 use crate::algorithm::two_phase::matrix_provider::column::identity::IdentityColumn;
312 use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::BasisInverse;
313 use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::LUDecomposition;
314 use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::permutation::FullPermutation;
315 use crate::algorithm::two_phase::tableau::inverse_maintenance::ColumnComputationInfo;
316 use crate::data::linear_algebra::vector::{SparseVector, Vector};
317 use std::collections::VecDeque;
318
319 #[test]
320 fn identity_2() {
321 let rows = vec![vec![(0, RB!(1))], vec![(1, RB!(1))]];
322 let result = LUDecomposition::rows(rows);
323 let expected = LUDecomposition {
324 row_permutation: FullPermutation::identity(2),
325 column_permutation: FullPermutation::identity(2),
326 lower_triangular: vec![vec![]],
327 upper_triangular: vec![vec![]],
328 upper_diagonal: vec![RB!(1), RB!(1)],
329 updates: vec![],
330 };
331
332 assert_eq!(result, expected);
333 }
334
335 #[test]
336 fn identity_3() {
337 let rows = vec![vec![(0, RB!(1))], vec![(1, RB!(1))], vec![(2, RB!(1))]];
338 let result = LUDecomposition::rows(rows);
339 let expected = LUDecomposition {
340 row_permutation: FullPermutation::identity(3),
341 column_permutation: FullPermutation::identity(3),
342 lower_triangular: vec![vec![], vec![]],
343 upper_triangular: vec![vec![], vec![]],
344 upper_diagonal: vec![RB!(1), RB!(1), RB!(1)],
345 updates: vec![],
346 };
347
348 assert_eq!(result, expected);
349 }
350
351 #[test]
352 fn offdiagonal_2_upper() {
353 let rows = vec![vec![(0, RB!(1)), (1, RB!(1))], vec![(1, RB!(1))]];
354 let result = LUDecomposition::rows(rows);
355 let expected = LUDecomposition {
356 row_permutation: FullPermutation::identity(2),
357 column_permutation: FullPermutation::identity(2),
358 lower_triangular: vec![vec![]],
359 upper_triangular: vec![vec![(0, RB!(1))]],
360 upper_diagonal: vec![RB!(1), RB!(1)],
361 updates: vec![],
362 };
363
364 assert_eq!(result, expected);
365 }
366
367 #[test]
368 fn offdiagonal_2_lower() {
369 let rows = vec![vec![(0, RB!(1))], vec![(0, RB!(1)), (1, RB!(1))]];
370 let result = LUDecomposition::rows(rows);
371 let expected = LUDecomposition {
372 row_permutation: FullPermutation::identity(2),
373 column_permutation: FullPermutation::identity(2),
374 lower_triangular: vec![vec![(1, RB!(1))]],
375 upper_triangular: vec![vec![]],
376 upper_diagonal: vec![RB!(1), RB!(1)],
377 updates: vec![],
378 };
379
380 assert_eq!(result, expected);
381 }
382
383 #[test]
384 fn offdiagonal_2_both() {
385 let rows = vec![vec![(0, RB!(1)), (1, RB!(1))], vec![(0, RB!(1))]];
386 let result = LUDecomposition::rows(rows);
387 let expected = LUDecomposition {
388 row_permutation: FullPermutation::new(vec![1, 0]),
389 column_permutation: FullPermutation::identity(2),
390 lower_triangular: vec![vec![(1, RB!(1))]],
391 upper_triangular: vec![vec![]],
392 upper_diagonal: vec![RB!(1), RB!(1)],
393 updates: vec![],
394 };
395
396 assert_eq!(result, expected);
397 }
398
399 #[test]
400 fn wikipedia_example() {
401 let columns = vec![vec![(0, RB!(4)), (1, RB!(3))], vec![(0, RB!(6)), (1, RB!(3))]];
402 let result = LUDecomposition::rows(columns);
403 let expected = LUDecomposition {
404 row_permutation: FullPermutation::identity(2),
405 column_permutation: FullPermutation::identity(2),
406 lower_triangular: vec![vec![(1, RB!(3, 2))]],
407 upper_triangular: vec![vec![(0, RB!(3))]],
408 upper_diagonal: vec![RB!(4), RB!(-3, 2)],
409 updates: vec![],
410 };
411
412 assert_eq!(result, expected);
413 }
414
415 #[test]
416 fn wikipedia_example2() {
417 let rows = vec![vec![(0, RB!(-1)), (1, RB!(3, 2))], vec![(0, RB!(1)), (1, RB!(-1))]];
418 let result = LUDecomposition::rows(rows);
419 let expected = LUDecomposition {
420 row_permutation: FullPermutation::identity(2),
421 column_permutation: FullPermutation::identity(2),
422 lower_triangular: vec![vec![(1, RB!(-1))]],
423 upper_triangular: vec![vec![(0, RB!(3, 2))]],
424 upper_diagonal: vec![RB!(-1), RB!(1, 2)],
425 updates: vec![],
426 };
427
428 assert_eq!(result, expected);
429
430 assert_eq!(
431 expected.left_multiply_by_basis_inverse(IdentityColumn::new(0).iter()).into_column(),
432 SparseVector::new(vec![(0, RB!(2)), (1, RB!(2))], 2),
433 );
434 assert_eq!(
435 expected.left_multiply_by_basis_inverse(IdentityColumn::new(1).iter()).into_column(),
436 SparseVector::new(vec![(0, RB!(3)), (1, RB!(2))], 2),
437 );
438 }
439
440 pub fn to_columns<const M: usize>(rows: &[[i32; M]; M]) -> VecDeque<Vec<(usize, Rational8)>> {
441 let mut columns = vec![vec![]; M].into_iter().collect::<VecDeque<_>>();
442
443 for (i, row) in rows.into_iter().enumerate() {
444 for (j, v) in row.into_iter().enumerate() {
445 if v.is_not_zero() {
446 columns[j].push((i, R8!(*v)));
447 }
448 }
449 }
450
451 columns
452 }
453
454 fn test_matrix<const M: usize>(rows: [[i32; M]; M]) {
455 let columns = to_columns(&rows);
456 let result = LUDecomposition::<RationalBig>::rows(
457 rows.iter().map(|row| {
458 row.iter().enumerate()
459 .filter(|(_, v)| v.is_not_zero())
460 .map(|(i, v)| (i, v.into()))
461 .collect()
462 }).collect()
463 );
464 for (j, column) in columns.iter().enumerate() {
465 assert_eq!(
466 result.left_multiply_by_basis_inverse(SparseSliceIterator::new(column)).into_column(),
467 SparseVector::standard_basis_vector(j, M),
468 "{}", j,
469 );
470 }
471 for (j, row) in rows.into_iter().enumerate() {
472 assert_eq!(
473 result.right_multiply_by_basis_inverse(DenseSliceIterator::new(&row)),
474 SparseVector::standard_basis_vector(j, M),
475 "{}", j,
476 );
477 }
478 }
479
480 #[test]
481 fn test_3x3() {
482 test_matrix([
483 [ 2, 3, 0],
484 [ 5, 0, 11],
485 [23, 29, 0],
486 ]);
487 }
488
489 #[test]
490 fn test_4x4_1() {
491 test_matrix([
492 [ 2, 3, 0, 5],
493 [ 5, 0, 11, 13],
494 [23, 29, 0, 57],
495 [31, 37, 41, 0],
496 ]);
497 }
498
499 #[test]
500 fn test_4x4_2() {
501 test_matrix([
502 [-101, 0, 0, -5],
503 [-110, -81, 0, 0],
504 [ 0, 0, 1, -111],
505 [ 0, 93, 69, 0],
506 ]);
507 }
508
509 #[test]
510 fn test_4x4_3() {
511 test_matrix([
512 [ 0, 0, -84, 122],
513 [ 0, 9, 0, 0],
514 [-39, 115, 0, 57],
515 [ 0, -12, 121, 0],
516 ]);
517 }
518
519 #[test]
520 fn test_5x5_banded() {
521 test_matrix([
522 [2, 3, 0, 0, 0],
523 [5, 7, 11, 0, 0],
524 [0, 29, 13, 57, 0],
525 [0, 0, 41, 17, 0],
526 [0, 0, 0, 53, 51],
527 ]);
528 }
529
530 #[test]
531 fn test_5x5_1() {
532 test_matrix([
533 [29, 23, 0, 19, 0],
534 [ 0, 0, 17, 13, 0],
535 [ 0, 0, 7, 0, 0],
536 [ 5, 0, 0, 3, 0],
537 [ 0, 0, 0, 0, 2],
538 ]);
539 }
540
541 #[test]
542 fn test_5x5_2() {
543 test_matrix([
544 [29, 23, 0, 19, 0],
545 [ 0, 0, 17, 13, 0],
546 [ 0, 11, 7, 0, 0],
547 [ 5, 0, 0, 3, 0],
548 [ 0, 0, 0, 0, 2],
549 ]);
550 }
551
552 #[test]
553 fn test_5x5_3() {
554 test_matrix([
555 [ 2, 3, 0, 5, 7],
556 [ 5, 0, 11, 13, 17],
557 [23, 29, 0, 57, 59],
558 [31, 37, 41, 0, 0],
559 [43, 0, 47, 53, 51],
560 ]);
561 }
562
563 #[test]
564 fn test_5x5_4() {
565 test_matrix([
566 [ 2, 3, 0, 5, 7],
567 [ 5, 0, 11, 13, 17],
568 [23, 29, 0, 57, 59],
569 [31, 37, 41, 0, 0],
570 [43, 0, 47, 53, 51],
571 ]);
572 }
573
574 #[test]
575 fn test_5x5_5() {
576 test_matrix([
577 [ 0, 54, 43, 0, 84],
578 [ 4, 0, 0, 0, 0],
579 [ 0, -111, -27, 0, -86],
580 [ -6, 0, 0, 17, -62],
581 [-109, 0, 0, 0, -104],
582 ]);
583 }
584
585 #[test]
586 fn test_5x5_6() {
587 test_matrix([
588 [ -71, -124, 0, 0, -108],
589 [ 0, 66, -121, -74, -53],
590 [ 0, 104, 0, 0, 0],
591 [ 0, 55, 0, 1, -3],
592 [ 93, 0, 0, 0, 104],
593 ]);
594 }
595
596 #[test]
597 fn test_6x6_1() {
598 test_matrix([
599 [ 0, 0, 0, -25, 0, 0],
600 [ -15, 79, 0, 0, 0, 0],
601 [ 0, 0, 0, 0, 0, 14],
602 [ 0, 0, 0, -114, -61, 0],
603 [ 0, 0, 109, 0, 0, -126],
604 [ 46, 0, 0, 50, 21, 0],
605 ]);
606 }
607
608 #[test]
609 fn test_6x6_2() {
610 test_matrix([
611 [ 0, 0, -26, -68, 84, 0],
612 [-125, 43, 0, 0, 0, -63],
613 [ 0, 0, 1, 90, 0, 0],
614 [ 0, -81, 0, 0, 0, 0],
615 [ -15, 0, 0, -81, 0, 0],
616 [ 0, -12, 0, 0, 0, 1],
617 ]);
618 }
619
620 #[test]
621 fn test_10x10_1() {
622 test_matrix([
623 [ 0, 0, 0, 0, 0, 0, -60, 0, -10, 0],
624 [ 0, 0, 0, 0, 0, 0, 0, -84, 0, 0],
625 [ 0, -105, 0, 0, 0, 0, 0, 0, 0, 0],
626 [ 0, 0, 0, -25, 0, 0, 0, 0, 116, 0],
627 [ 0, 0, 0, 0, -18, 0, 0, 0, 0, 0],
628 [ 0, 0, 0, -72, 0, 0, 0, 0, 0, 0],
629 [ 0, 0, 16, 48, 0, 0, 0, 0, 0, 0],
630 [ -57, 0, 0, -88, 107, 0, 0, 0, 0, 0],
631 [-122, -108, 0, 0, 0, 91, 0, 0, -127, 0],
632 [ 0, 85, 0, 0, 106, 0, 0, 0, 0, -121],
633 ]);
634 }
635
636 #[test]
637 fn test_11x11_1() {
638 test_matrix([
639 [ 0, 0, 0, 0, 0, 0, 0, 0, 0, -13, 0],
640 [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -122],
641 [ 0, 0, 0, 0, 0, 102, 82, 0, 0, 0, 13],
642 [ 0, 0, 0, 0, 0, -107, -39, 0, 0, 0, 0],
643 [ 0, 0, 0, 0, 0, 0, -39, 48, -113, 0, 0],
644 [ 24, 0, 0, 0, 0, 0, 0, 0, 0, -93, -120],
645 [-111, 0, 0, -81, 0, 0, 0, 0, 0, 0, 0],
646 [ 0, 0, 0, 0, 0, 82, 0, 0, 76, 0, 0],
647 [ 0, 0, -51, 0, 0, 0, 126, 0, 0, 0, -105],
648 [ 0, 118, 0, 0, 0, 0, 0, 0, 0, 0, 27],
649 [ 0, 0, 120, 0, -31, 0, 0, 0, 0, 0, 0],
650 ]);
651 }
652
653 mod subtract_multiple_of_column_from_other_column {
654 use relp_num::R32;
655
656 use crate::algorithm::two_phase::tableau::inverse_maintenance::carry::lower_upper::decomposition::subtract_multiple_of_row_from_other_row;
657
658 #[test]
659 fn empty() {
660 let mut column1 = vec![];
661 let column2 = vec![];
662 subtract_multiple_of_row_from_other_row(&mut column1, &R32!(1), &column2);
663 assert_eq!(column1, vec![]);
664 }
665
666 #[test]
667 fn edited_empty() {
668 let mut column1 = vec![];
669 let column2 = vec![(1, R32!(1))];
670 subtract_multiple_of_row_from_other_row(&mut column1, &R32!(1), &column2);
671 assert_eq!(column1, vec![(1, R32!(-1))]);
672 }
673
674 #[test]
675 fn other_empty() {
676 let mut column1 = vec![(1, R32!(1))];
677 let column2 = vec![];
678 subtract_multiple_of_row_from_other_row(&mut column1, &R32!(1), &column2);
679 assert_eq!(column1, vec![(1, R32!(1))]);
680 }
681
682 #[test]
683 fn single_before() {
684 let mut column1 = vec![(1, R32!(1))];
685 let column2 = vec![(2, R32!(3))];
686 subtract_multiple_of_row_from_other_row(&mut column1, &R32!(1), &column2);
687 assert_eq!(column1, vec![(1, R32!(1)), (2, R32!(-3))]);
688 }
689
690 #[test]
691 fn single_at() {
692 let mut column1 = vec![(1, R32!(1))];
693 let column2 = vec![(1, R32!(3))];
694 subtract_multiple_of_row_from_other_row(&mut column1, &R32!(1), &column2);
695 assert_eq!(column1, vec![(1, R32!(-2))]);
696 }
697
698 #[test]
699 fn single_at_make_zero() {
700 let mut column1 = vec![(1, R32!(1))];
701 let column2 = vec![(1, R32!(3))];
702 subtract_multiple_of_row_from_other_row(&mut column1, &R32!(1, 3), &column2);
703 assert_eq!(column1, vec![]);
704 }
705
706 #[test]
707 fn single_after() {
708 let mut column1 = vec![(1, R32!(1))];
709 let column2 = vec![(0, R32!(3))];
710 subtract_multiple_of_row_from_other_row(&mut column1, &R32!(1), &column2);
711 assert_eq!(column1, vec![(0, R32!(-3)), (1, R32!(1))]);
712 }
713 }
714}