measures/
matrix_utils.rs

1use crate::traits::ArithmeticOps;
2
3// It receives a matrix of numbers, a string to be display as suffix unit of measurement,
4// and a number of spaces to be used as indentation,
5// and it returns a string in which the matrix is formatted in lines, with its columns aligned.
6pub fn format_matrix<const ROW_COUNT: usize, const COLUMN_COUNT: usize, Number: ArithmeticOps>(
7    matrix: &[[Number; COLUMN_COUNT]; ROW_COUNT],
8    unit_suffix: &str,
9    indent: usize,
10) -> String {
11    const EMPTY_STRING: String = String::new();
12    let mut rows = [EMPTY_STRING; ROW_COUNT];
13    for column_index in 0..COLUMN_COUNT {
14        let column = format_column::<ROW_COUNT, COLUMN_COUNT, Number>(matrix, column_index);
15        for row_index in 0..ROW_COUNT {
16            if column_index == 0 {
17                rows[row_index] += &" ".repeat(indent);
18                rows[row_index] += if row_index == 0 {
19                    "⎡ "
20                } else if row_index == ROW_COUNT - 1 {
21                    "⎣ "
22                } else {
23                    "⎢ "
24                };
25            }
26            rows[row_index] += &column[row_index];
27            rows[row_index] += if column_index < COLUMN_COUNT - 1 {
28                " "
29            } else if row_index == 0 {
30                " ⎤"
31            } else if row_index == ROW_COUNT - 1 {
32                " ⎦"
33            } else {
34                " ⎥"
35            };
36            if row_index == 0 && column_index == COLUMN_COUNT - 1 {
37                rows[row_index] += unit_suffix;
38            }
39        }
40    }
41    rows.join("\n")
42}
43
44// It receives a matrix of numbers, and a column index,
45// and it returns an array of strings
46// displaying the lined up formatted numbers of the specified column.
47fn format_column<const ROW_COUNT: usize, const COLUMN_COUNT: usize, Number: ArithmeticOps>(
48    matrix: &[[Number; COLUMN_COUNT]; ROW_COUNT],
49    column_index: usize,
50) -> [String; ROW_COUNT] {
51    const EMPTY_STRING: String = String::new();
52    let mut padded_cells = [EMPTY_STRING; ROW_COUNT];
53    let mut max_dot_pos = 0;
54    let mut max_fractional_len = 0;
55    let mut formatted_cells = [EMPTY_STRING; ROW_COUNT];
56    let mut dot_positions = [0; ROW_COUNT];
57    for row_index in 0..ROW_COUNT {
58        formatted_cells[row_index] = format!("{}", matrix[row_index][column_index]);
59        dot_positions[row_index] =
60            if let Some(pos) = formatted_cells[row_index].bytes().position(|c| c == b'.') {
61                pos
62            } else {
63                formatted_cells[row_index].len()
64            };
65        max_dot_pos = core::cmp::max(max_dot_pos, dot_positions[row_index]);
66        max_fractional_len = core::cmp::max(
67            max_fractional_len,
68            formatted_cells[row_index].len() - dot_positions[row_index],
69        );
70    }
71    for row_index in 0..ROW_COUNT {
72        padded_cells[row_index] = format!(
73            "{}{}{}",
74            " ".repeat(max_dot_pos - dot_positions[row_index]),
75            formatted_cells[row_index],
76            " ".repeat(
77                max_fractional_len + dot_positions[row_index] - formatted_cells[row_index].len()
78            ),
79        );
80    }
81    padded_cells
82}