Skip to main content

problemreductions/
config.rs

1//! Configuration utilities for problem solving.
2
3/// Convert a configuration index to a configuration vector.
4///
5/// The index is treated as a number in base `num_flavors`.
6pub fn index_to_config(index: usize, num_variables: usize, num_flavors: usize) -> Vec<usize> {
7    let mut config = vec![0; num_variables];
8    let mut remaining = index;
9    for i in (0..num_variables).rev() {
10        config[i] = remaining % num_flavors;
11        remaining /= num_flavors;
12    }
13    config
14}
15
16/// Convert a configuration vector to an index.
17///
18/// The configuration is treated as digits in base `num_flavors`.
19pub fn config_to_index(config: &[usize], num_flavors: usize) -> usize {
20    let mut index = 0;
21    for &value in config {
22        index = index * num_flavors + value;
23    }
24    index
25}
26
27/// Convert a binary configuration to a bitvec-style representation.
28#[cfg(test)]
29pub(crate) fn config_to_bits(config: &[usize]) -> Vec<bool> {
30    config.iter().map(|&v| v != 0).collect()
31}
32
33/// Convert a bitvec-style representation to a binary configuration.
34#[cfg(test)]
35pub(crate) fn bits_to_config(bits: &[bool]) -> Vec<usize> {
36    bits.iter().map(|&b| if b { 1 } else { 0 }).collect()
37}
38
39/// Iterator over all configurations for per-variable dimension sizes.
40///
41/// Supports different cardinalities per variable (e.g., `dims = [2, 3, 2]`).
42pub struct DimsIterator {
43    dims: Vec<usize>,
44    current: Option<Vec<usize>>,
45    total_configs: usize,
46    current_index: usize,
47}
48
49impl DimsIterator {
50    /// Create a new iterator from per-variable dimensions.
51    ///
52    /// For empty dims, produces exactly one configuration (the empty config).
53    /// If any dimension is 0, produces no configurations.
54    pub fn new(dims: Vec<usize>) -> Self {
55        let total_configs = if dims.is_empty() {
56            // No variables means exactly 1 configuration: the empty config
57            1
58        } else {
59            dims.iter()
60                .copied()
61                .try_fold(
62                    1usize,
63                    |acc, d| {
64                        if d == 0 {
65                            None
66                        } else {
67                            acc.checked_mul(d)
68                        }
69                    },
70                )
71                .unwrap_or(0)
72        };
73        let current = if total_configs == 0 {
74            None
75        } else {
76            Some(vec![0; dims.len()])
77        };
78        Self {
79            dims,
80            current,
81            total_configs,
82            current_index: 0,
83        }
84    }
85
86    /// Returns the total number of configurations.
87    pub fn total(&self) -> usize {
88        self.total_configs
89    }
90}
91
92impl Iterator for DimsIterator {
93    type Item = Vec<usize>;
94
95    fn next(&mut self) -> Option<Self::Item> {
96        let current = self.current.take()?;
97        let result = current.clone();
98
99        // Advance to next configuration
100        let mut next = current;
101        let mut carry = true;
102        for i in (0..self.dims.len()).rev() {
103            if carry {
104                next[i] += 1;
105                if next[i] >= self.dims[i] {
106                    next[i] = 0;
107                } else {
108                    carry = false;
109                }
110            }
111        }
112
113        self.current_index += 1;
114        if self.current_index < self.total_configs {
115            self.current = Some(next);
116        }
117
118        Some(result)
119    }
120
121    fn size_hint(&self) -> (usize, Option<usize>) {
122        let remaining = self.total_configs - self.current_index;
123        (remaining, Some(remaining))
124    }
125}
126
127impl ExactSizeIterator for DimsIterator {}
128
129#[cfg(test)]
130#[path = "unit_tests/config.rs"]
131mod tests;