1use crate::models::config::{Distribution, FairnessMode};
4use anyhow::{Result, anyhow};
5
6#[derive(Debug, Clone, PartialEq)]
8pub struct ConfigurationState {
9 pub array_size: u32,
11 pub distribution: DistributionType,
13 pub fairness_mode: FairnessMode,
15 pub budget: Option<u32>,
17 pub alpha: Option<f32>,
19 pub beta: Option<f32>,
21 pub learning_rate: Option<f32>,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum DistributionType {
28 Shuffled,
29 Reversed,
30 NearlySorted,
31 FewUnique,
32}
33
34impl ConfigurationState {
35 pub fn new() -> Self {
37 Self {
38 array_size: 100,
39 distribution: DistributionType::Shuffled,
40 fairness_mode: FairnessMode::WallTime { slice_ms: 50 },
41 budget: None,
42 alpha: None,
43 beta: None,
44 learning_rate: None,
45 }
46 }
47
48 pub fn default_valid() -> Self {
50 Self::new()
51 }
52
53 pub fn from_run_config(config: &crate::models::config::RunConfiguration) -> Self {
55 Self {
56 array_size: config.array_size as u32,
57 distribution: config.distribution.clone().into(),
58 fairness_mode: config.fairness_mode.clone(),
59 budget: None,
60 alpha: None,
61 beta: None,
62 learning_rate: None,
63 }
64 }
65
66 pub fn get_available_array_sizes() -> Vec<u32> {
68 vec![10, 25, 50, 100, 200, 500, 1000]
69 }
70
71 pub fn get_available_distributions() -> Vec<DistributionType> {
73 vec![
74 DistributionType::Shuffled,
75 DistributionType::Reversed,
76 DistributionType::NearlySorted,
77 DistributionType::FewUnique,
78 ]
79 }
80
81 pub fn get_available_fairness_modes() -> Vec<FairnessMode> {
83 vec![
84 FairnessMode::ComparisonBudget { k: 16 },
85 FairnessMode::Weighted { alpha: 2.0, beta: 0.5 },
86 FairnessMode::WallTime { slice_ms: 50 },
87 FairnessMode::Adaptive { learning_rate: 0.3 },
88 ]
89 }
90
91 pub fn requires_budget_parameter(&self) -> bool {
93 matches!(self.fairness_mode, FairnessMode::ComparisonBudget { .. })
94 }
95
96 pub fn requires_weighted_parameters(&self) -> bool {
98 matches!(self.fairness_mode, FairnessMode::Weighted { .. })
99 }
100
101 pub fn requires_learning_rate_parameter(&self) -> bool {
103 matches!(self.fairness_mode, FairnessMode::Adaptive { .. })
104 }
105
106 pub fn validate(&self) -> Result<()> {
108 if self.array_size < 10 || self.array_size > 1000 {
110 return Err(anyhow!("Array size must be between 10 and 1000, got {}", self.array_size));
111 }
112
113 match &self.fairness_mode {
115 FairnessMode::ComparisonBudget { k } => {
116 if *k == 0 {
117 return Err(anyhow!("Budget parameter must be greater than 0, got {}", k));
118 }
119 },
120 FairnessMode::Weighted { alpha, beta } => {
121 if *alpha <= 0.0 {
122 return Err(anyhow!("Alpha parameter must be greater than 0.0, got {}", alpha));
123 }
124 if *beta <= 0.0 {
125 return Err(anyhow!("Beta parameter must be greater than 0.0, got {}", beta));
126 }
127 },
128 FairnessMode::Adaptive { learning_rate } => {
129 if *learning_rate < 0.1 || *learning_rate > 1.0 {
130 return Err(anyhow!("Learning rate must be between 0.1 and 1.0, got {}", learning_rate));
131 }
132 },
133 FairnessMode::WallTime { slice_ms } => {
134 if *slice_ms == 0 {
135 return Err(anyhow!("Wall time slice must be greater than 0, got {}", slice_ms));
136 }
137 },
138 _ => {}, }
140
141 Ok(())
142 }
143
144 pub fn is_valid(&self) -> bool {
146 self.validate().is_ok()
147 }
148
149 pub fn is_invalid(&self) -> bool {
151 !self.is_valid()
152 }
153
154 pub fn set_array_size(&mut self, size: u32) -> Result<()> {
156 if !(10..=1000).contains(&size) {
157 return Err(anyhow!("Array size must be between 10 and 1000, got {}", size));
158 }
159 self.array_size = size;
160 Ok(())
161 }
162
163 pub fn set_fairness_mode(&mut self, mode: FairnessMode) {
165 self.fairness_mode = mode.clone();
166
167 match &mode {
169 FairnessMode::ComparisonBudget { .. } => {
170 self.alpha = None;
171 self.beta = None;
172 self.learning_rate = None;
173 },
174 FairnessMode::Weighted { .. } => {
175 self.budget = None;
176 self.learning_rate = None;
177 },
178 FairnessMode::Adaptive { .. } => {
179 self.budget = None;
180 self.alpha = None;
181 self.beta = None;
182 },
183 FairnessMode::WallTime { .. } => {
184 self.budget = None;
185 self.alpha = None;
186 self.beta = None;
187 self.learning_rate = None;
188 },
189 _ => {
190 self.budget = None;
192 self.alpha = None;
193 self.beta = None;
194 self.learning_rate = None;
195 },
196 }
197 }
198
199 pub fn to_legacy_distribution(&self) -> Distribution {
201 match self.distribution {
202 DistributionType::Shuffled => Distribution::Shuffled,
203 DistributionType::Reversed => Distribution::Reversed,
204 DistributionType::NearlySorted => Distribution::NearlySorted,
205 DistributionType::FewUnique => Distribution::FewUnique,
206 }
207 }
208}
209
210impl Default for ConfigurationState {
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216impl From<DistributionType> for Distribution {
217 fn from(dist_type: DistributionType) -> Self {
218 match dist_type {
219 DistributionType::Shuffled => Distribution::Shuffled,
220 DistributionType::Reversed => Distribution::Reversed,
221 DistributionType::NearlySorted => Distribution::NearlySorted,
222 DistributionType::FewUnique => Distribution::FewUnique,
223 }
224 }
225}
226
227impl From<Distribution> for DistributionType {
228 fn from(distribution: Distribution) -> Self {
229 match distribution {
230 Distribution::Shuffled => DistributionType::Shuffled,
231 Distribution::Reversed => DistributionType::Reversed,
232 Distribution::NearlySorted => DistributionType::NearlySorted,
233 Distribution::FewUnique => DistributionType::FewUnique,
234 Distribution::Sorted => DistributionType::Shuffled, Distribution::WithDuplicates => DistributionType::FewUnique, }
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn test_configuration_state_creation() {
246 let config = ConfigurationState::new();
247 assert_eq!(config.array_size, 100);
248 assert_eq!(config.distribution, DistributionType::Shuffled);
249 assert!(matches!(config.fairness_mode, FairnessMode::WallTime { .. }));
250 assert_eq!(config.budget, None);
251 assert_eq!(config.alpha, None);
252 assert_eq!(config.beta, None);
253 assert_eq!(config.learning_rate, None);
254 }
255
256 #[test]
257 fn test_array_size_validation() {
258 let mut config = ConfigurationState::new();
259
260 assert!(config.set_array_size(50).is_ok());
262 assert_eq!(config.array_size, 50);
263
264 assert!(config.set_array_size(10).is_ok());
265 assert!(config.set_array_size(1000).is_ok());
266
267 assert!(config.set_array_size(5).is_err());
269 assert!(config.set_array_size(1001).is_err());
270 }
271
272 #[test]
273 fn test_fairness_mode_parameter_requirements() {
274 let mut config = ConfigurationState::new();
275
276 config.set_fairness_mode(FairnessMode::ComparisonBudget { k: 16 });
277 assert!(config.requires_budget_parameter());
278 assert!(!config.requires_weighted_parameters());
279 assert!(!config.requires_learning_rate_parameter());
280
281 config.set_fairness_mode(FairnessMode::Weighted { alpha: 2.0, beta: 0.5 });
282 assert!(!config.requires_budget_parameter());
283 assert!(config.requires_weighted_parameters());
284 assert!(!config.requires_learning_rate_parameter());
285
286 config.set_fairness_mode(FairnessMode::Adaptive { learning_rate: 0.3 });
287 assert!(!config.requires_budget_parameter());
288 assert!(!config.requires_weighted_parameters());
289 assert!(config.requires_learning_rate_parameter());
290 }
291
292 #[test]
293 fn test_configuration_validation() {
294 let mut config = ConfigurationState::new();
295 assert!(config.is_valid());
296
297 config.array_size = 5;
299 assert!(config.is_invalid());
300
301 config.array_size = 100; assert!(config.is_valid());
303
304 config.fairness_mode = FairnessMode::ComparisonBudget { k: 0 };
306 assert!(config.is_invalid());
307 }
308
309 #[test]
310 fn test_available_options() {
311 let sizes = ConfigurationState::get_available_array_sizes();
312 assert_eq!(sizes, vec![10, 25, 50, 100, 200, 500, 1000]);
313
314 let distributions = ConfigurationState::get_available_distributions();
315 assert_eq!(distributions.len(), 4);
316 assert!(distributions.contains(&DistributionType::Shuffled));
317
318 let fairness_modes = ConfigurationState::get_available_fairness_modes();
319 assert_eq!(fairness_modes.len(), 4);
320 }
321
322 #[test]
323 fn test_distribution_conversion() {
324 let dist_type = DistributionType::Reversed;
325 let legacy_dist: Distribution = dist_type.into();
326 assert_eq!(legacy_dist, Distribution::Reversed);
327
328 let config = ConfigurationState::new();
329 let legacy = config.to_legacy_distribution();
330 assert_eq!(legacy, Distribution::Shuffled);
331 }
332}