domino_lib/validate/model/
helpers.rs

1//! This module provides utility functions and macros for sorting variable labels,
2//! formatting variable collections, and constructing constraint expressions.
3
4use super::variables::Variable;
5use std::cmp::Ordering;
6
7/// Compares two variable labels and determines their ordering.
8///
9/// The labels follow a specific format where:
10/// - The second character represents the tile index.
11/// - The third character represents the position.
12///
13/// This function parses those values and sorts labels first by tile index, then by position.
14///
15/// # Arguments
16///
17/// * `label1` - A reference to the first label as a `String`.
18/// * `label2` - A reference to the second label as a `String`.
19///
20/// # Returns
21///
22/// * `Ordering::Less` if `label1` should appear before `label2`.
23/// * `Ordering::Greater` if `label1` should appear after `label2`.
24/// * `Ordering::Equal` if they are identical.
25pub fn sorting_label(label1: &String, label2: &String, tileset_digits: usize, sequence_digits: usize) -> Ordering {
26    let parse_label = |label: &String| {
27        let (tile_index, position) = (
28            label[1..1+tileset_digits].parse::<usize>().unwrap(),
29            label[1+tileset_digits..1+tileset_digits+sequence_digits].parse::<usize>().unwrap(),
30        );
31        (tile_index, position)
32    };
33
34    let (l1tileindex, l1position) = parse_label(label1);
35    let (l2tileindex, l2position) = parse_label(label2);
36
37    // Compare tile index first, then position
38    match l1tileindex.cmp(&l2tileindex) {
39        Ordering::Equal => l1position.cmp(&l2position),
40        other => other,
41    }
42}
43
44/// A macro to concatenate a list of variable labels into a formatted string.
45///
46/// This macro accepts a list of labels and a separator, with an optional third argument to
47/// control line breaks. If a line length is provided, a newline is inserted after every `line_length` elements.
48///
49/// # Arguments
50///
51/// * `$labels` - A list of variable labels (`Vec<String>`).
52/// * `$separator` - A separator string to join the labels.
53/// * `$line_length` (optional) - An optional maximum number of labels per line.
54///
55/// # Returns
56///
57/// A formatted string where labels are concatenated with the given separator.
58#[macro_export]
59macro_rules! stringify_variables {
60    // Handle two arguments, setting the default to `None` for the third parameter
61    ($labels:expr, $separator:expr) => {
62        stringify_variables!($labels, $separator, Option::<usize>::None)
63    };
64    // Handle three arguments where $line_length may be `Some(usize)` or `None`
65    ($labels:expr, $separator:expr, $line_length:expr) => {{
66        let mut result = String::new();
67        let newline_each: usize = $line_length.unwrap_or($labels.len()); // Default to the length of labels if None
68
69        for (i, label) in $labels.iter().enumerate() {
70            result.push_str(label);
71
72            // Add a separator if this isn't the last label
73            if i < $labels.len() - 1 {
74                result.push_str($separator);
75            }
76
77            // Add newline every `newline_each` labels, except at the end
78            if (i + 1) % newline_each == 0 && i < $labels.len() - 1 {
79                result.push('\n');
80            }
81        }
82
83        result
84    }};
85}
86
87/// Collects variable labels from a slice of `Variable` structs.
88///
89/// This function extracts the `label` field from each `Variable` in the provided slice.
90///
91/// # Arguments
92///
93/// * `variables` - A slice of `Variable` references.
94///
95/// # Returns
96///
97/// A `Vec<String>` containing the labels of all variables.
98pub fn collect_labels(variables: &[Variable]) -> Vec<String> {
99    variables.iter().map(|var| var.label.clone()).collect()
100}
101
102/// Creates a constraint expression enforcing a sum of binary variables.
103///
104/// This function generates a constraint in the form of `"var1 var2 ... varn = 1"`, ensuring that
105/// exactly one of the listed variables is active in the solution.
106///
107/// # Arguments
108///
109/// * `labels` - A vector of variable labels (`Vec<String>`) that should be included in the constraint.
110///
111/// # Returns
112///
113/// A string representing the constraint expression.
114pub fn create_bound_string(labels: Vec<String>) -> String {
115    format!("{} = 1", stringify_variables!(labels, " "))
116}