sorting_race/models/
session.rs

1//! Session state management for multiple sorting races
2
3use crate::models::{
4    configuration::{ConfigurationState, DistributionType},
5    config::FairnessMode,
6    traits::Sorter,
7};
8use std::time::Instant;
9
10/// Algorithm type identifier
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum AlgorithmType {
13    BubbleSort,
14    SelectionSort,
15    InsertionSort,
16    MergeSort,
17    QuickSort,
18    HeapSort,
19    ShellSort,
20}
21
22impl AlgorithmType {
23    /// Get all available algorithm types
24    pub fn all() -> Vec<AlgorithmType> {
25        vec![
26            AlgorithmType::BubbleSort,
27            AlgorithmType::SelectionSort,
28            AlgorithmType::InsertionSort,
29            AlgorithmType::MergeSort,
30            AlgorithmType::QuickSort,
31            AlgorithmType::HeapSort,
32            AlgorithmType::ShellSort,
33        ]
34    }
35
36    /// Get algorithm type from index
37    pub fn from_index(index: usize) -> Option<AlgorithmType> {
38        let algorithms = Self::all();
39        algorithms.get(index).copied()
40    }
41
42    /// Get index of algorithm type
43    pub fn to_index(self) -> usize {
44        match self {
45            AlgorithmType::BubbleSort => 0,
46            AlgorithmType::SelectionSort => 1,
47            AlgorithmType::InsertionSort => 2,
48            AlgorithmType::MergeSort => 3,
49            AlgorithmType::QuickSort => 4,
50            AlgorithmType::HeapSort => 5,
51            AlgorithmType::ShellSort => 6,
52        }
53    }
54}
55
56impl std::fmt::Display for AlgorithmType {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        match self {
59            AlgorithmType::BubbleSort => write!(f, "Bubble Sort"),
60            AlgorithmType::SelectionSort => write!(f, "Selection Sort"),
61            AlgorithmType::InsertionSort => write!(f, "Insertion Sort"),
62            AlgorithmType::MergeSort => write!(f, "Merge Sort"),
63            AlgorithmType::QuickSort => write!(f, "Quick Sort"),
64            AlgorithmType::HeapSort => write!(f, "Heap Sort"),
65            AlgorithmType::ShellSort => write!(f, "Shell Sort"),
66        }
67    }
68}
69
70/// Result of a single sorting race
71#[derive(Debug, Clone)]
72pub struct RaceResult {
73    /// Array size used for this race
74    pub array_size: u32,
75    /// Distribution type used
76    pub distribution: DistributionType,
77    /// Fairness mode used
78    pub fairness_mode: FairnessMode,
79    /// Completion times for each algorithm (None if didn't complete)
80    pub completion_times: Vec<Option<std::time::Duration>>,
81    /// Memory usage for each algorithm
82    pub memory_usage: Vec<usize>,
83    /// Algorithm type names for reference
84    pub algorithm_names: Vec<String>,
85    /// Winner algorithm (first to complete)
86    pub winner: Option<AlgorithmType>,
87    /// Race start timestamp
88    pub race_start: Instant,
89    /// Race end timestamp
90    pub race_end: Option<Instant>,
91    /// Total race duration
92    pub total_duration: Option<std::time::Duration>,
93}
94
95impl RaceResult {
96    /// Create a new race result
97    pub fn new(
98        array_size: u32,
99        distribution: DistributionType,
100        fairness_mode: FairnessMode,
101        algorithm_count: usize,
102    ) -> Self {
103        Self {
104            array_size,
105            distribution,
106            fairness_mode,
107            completion_times: vec![None; algorithm_count],
108            memory_usage: vec![0; algorithm_count],
109            algorithm_names: Vec::new(),
110            winner: None,
111            race_start: Instant::now(),
112            race_end: None,
113            total_duration: None,
114        }
115    }
116
117    /// Mark race as completed
118    pub fn complete(&mut self) {
119        self.race_end = Some(Instant::now());
120        self.total_duration = Some(self.race_start.elapsed());
121    }
122
123    /// Set completion time for an algorithm
124    pub fn set_completion_time(&mut self, algorithm_index: usize, duration: std::time::Duration) {
125        if algorithm_index < self.completion_times.len() {
126            self.completion_times[algorithm_index] = Some(duration);
127            
128            // Set winner if this is the first completion
129            if self.winner.is_none() {
130                self.winner = AlgorithmType::from_index(algorithm_index);
131            }
132        }
133    }
134
135    /// Set memory usage for an algorithm
136    pub fn set_memory_usage(&mut self, algorithm_index: usize, memory: usize) {
137        if algorithm_index < self.memory_usage.len() {
138            self.memory_usage[algorithm_index] = memory;
139        }
140    }
141
142    /// Check if race is complete
143    pub fn is_complete(&self) -> bool {
144        self.race_end.is_some()
145    }
146}
147
148/// Session state managing multiple sorting races
149#[derive(Debug, Clone)]
150pub struct SessionState {
151    /// Current configuration settings
152    pub current_config: ConfigurationState,
153    /// History of completed race results
154    pub run_history: Vec<RaceResult>,
155    /// When the session started
156    pub session_start_time: Instant,
157    /// Total number of races run in this session
158    pub total_races_run: u32,
159    /// Current race result (if race is in progress)
160    current_race: Option<RaceResult>,
161}
162
163impl SessionState {
164    /// Create a new session state
165    pub fn new() -> Self {
166        Self {
167            current_config: ConfigurationState::new(),
168            run_history: Vec::new(),
169            session_start_time: Instant::now(),
170            total_races_run: 0,
171            current_race: None,
172        }
173    }
174
175    /// Start a new race with current configuration
176    pub fn start_new_race(&mut self) -> Result<(), anyhow::Error> {
177        // Validate configuration
178        self.current_config.validate()?;
179
180        // Create new race result
181        let algorithm_count = AlgorithmType::all().len();
182        let mut race_result = RaceResult::new(
183            self.current_config.array_size,
184            self.current_config.distribution,
185            self.current_config.fairness_mode.clone(),
186            algorithm_count,
187        );
188
189        // Set algorithm names
190        race_result.algorithm_names = AlgorithmType::all()
191            .iter()
192            .map(|alg| alg.to_string())
193            .collect();
194
195        self.current_race = Some(race_result);
196        Ok(())
197    }
198
199    /// Complete the current race and add it to history
200    pub fn complete_current_race(&mut self) {
201        if let Some(mut race) = self.current_race.take() {
202            race.complete();
203            self.run_history.push(race);
204            self.total_races_run += 1;
205        }
206    }
207
208    /// Update current race progress
209    pub fn update_race_progress(&mut self, algorithms: &[Box<dyn Sorter>]) {
210        if let Some(ref mut race) = self.current_race {
211            for (i, algorithm) in algorithms.iter().enumerate() {
212                // Update memory usage
213                let memory = algorithm.get_memory_usage();
214                race.set_memory_usage(i, memory);
215
216                // Check if algorithm completed and set completion time
217                if algorithm.is_complete() && race.completion_times[i].is_none() {
218                    let completion_time = race.race_start.elapsed();
219                    race.set_completion_time(i, completion_time);
220                }
221            }
222        }
223    }
224
225    /// Check if there's a current race in progress
226    pub fn has_current_race(&self) -> bool {
227        self.current_race.is_some()
228    }
229
230    /// Get current race result (if any)
231    pub fn get_current_race(&self) -> Option<&RaceResult> {
232        self.current_race.as_ref()
233    }
234
235    /// Get mutable reference to current race result
236    pub fn get_current_race_mut(&mut self) -> Option<&mut RaceResult> {
237        self.current_race.as_mut()
238    }
239
240    /// Get session duration
241    pub fn get_session_duration(&self) -> std::time::Duration {
242        self.session_start_time.elapsed()
243    }
244
245    /// Get race statistics
246    pub fn get_race_statistics(&self) -> SessionStatistics {
247        SessionStatistics::from_session(self)
248    }
249
250    /// Update current configuration
251    pub fn update_configuration(&mut self, config: ConfigurationState) {
252        self.current_config = config;
253    }
254
255    /// Clear session history
256    pub fn clear_history(&mut self) {
257        self.run_history.clear();
258        self.total_races_run = 0;
259        self.current_race = None;
260    }
261
262    /// Get average race duration
263    pub fn get_average_race_duration(&self) -> Option<std::time::Duration> {
264        if self.run_history.is_empty() {
265            return None;
266        }
267
268        let total_duration: std::time::Duration = self.run_history
269            .iter()
270            .filter_map(|result| result.total_duration)
271            .sum();
272
273        Some(total_duration / self.run_history.len() as u32)
274    }
275
276    /// Get most common winner
277    pub fn get_most_common_winner(&self) -> Option<AlgorithmType> {
278        use std::collections::HashMap;
279
280        let mut winner_counts: HashMap<AlgorithmType, u32> = HashMap::new();
281        
282        for result in &self.run_history {
283            if let Some(winner) = result.winner {
284                *winner_counts.entry(winner).or_insert(0) += 1;
285            }
286        }
287
288        winner_counts
289            .into_iter()
290            .max_by_key(|(_, count)| *count)
291            .map(|(algorithm, _)| algorithm)
292    }
293}
294
295impl Default for SessionState {
296    fn default() -> Self {
297        Self::new()
298    }
299}
300
301/// Session statistics summary
302#[derive(Debug, Clone)]
303pub struct SessionStatistics {
304    pub total_races: u32,
305    pub session_duration: std::time::Duration,
306    pub average_race_duration: Option<std::time::Duration>,
307    pub most_common_winner: Option<AlgorithmType>,
308    pub win_counts: std::collections::HashMap<AlgorithmType, u32>,
309    pub total_comparisons: u64,
310    pub total_moves: u64,
311    pub average_array_size: f32,
312}
313
314impl SessionStatistics {
315    /// Generate statistics from a session state
316    pub fn from_session(session: &SessionState) -> Self {
317        use std::collections::HashMap;
318
319        let mut win_counts: HashMap<AlgorithmType, u32> = HashMap::new();
320        let total_comparisons = 0u64;
321        let total_moves = 0u64;
322        let mut total_array_size = 0u64;
323
324        for result in &session.run_history {
325            if let Some(winner) = result.winner {
326                *win_counts.entry(winner).or_insert(0) += 1;
327            }
328            total_array_size += result.array_size as u64;
329        }
330
331        let most_common_winner = win_counts
332            .iter()
333            .max_by_key(|(_, count)| *count)
334            .map(|(algorithm, _)| *algorithm);
335
336        let average_array_size = if session.run_history.is_empty() {
337            0.0
338        } else {
339            total_array_size as f32 / session.run_history.len() as f32
340        };
341
342        Self {
343            total_races: session.total_races_run,
344            session_duration: session.get_session_duration(),
345            average_race_duration: session.get_average_race_duration(),
346            most_common_winner,
347            win_counts,
348            total_comparisons,
349            total_moves,
350            average_array_size,
351        }
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    use super::*;
358
359    #[test]
360    fn test_algorithm_type_conversions() {
361        assert_eq!(AlgorithmType::from_index(0), Some(AlgorithmType::BubbleSort));
362        assert_eq!(AlgorithmType::from_index(6), Some(AlgorithmType::ShellSort));
363        assert_eq!(AlgorithmType::from_index(7), None);
364
365        assert_eq!(AlgorithmType::BubbleSort.to_index(), 0);
366        assert_eq!(AlgorithmType::ShellSort.to_index(), 6);
367
368        assert_eq!(AlgorithmType::all().len(), 7);
369    }
370
371    #[test]
372    fn test_race_result_creation() {
373        let result = RaceResult::new(100, DistributionType::Shuffled, FairnessMode::WallTime { slice_ms: 50 }, 7);
374        
375        assert_eq!(result.array_size, 100);
376        assert_eq!(result.distribution, DistributionType::Shuffled);
377        assert_eq!(result.completion_times.len(), 7);
378        assert_eq!(result.memory_usage.len(), 7);
379        assert_eq!(result.winner, None);
380        assert!(!result.is_complete());
381    }
382
383    #[test]
384    fn test_race_result_completion() {
385        let mut result = RaceResult::new(50, DistributionType::Reversed, FairnessMode::WallTime { slice_ms: 50 }, 3);
386        
387        result.set_completion_time(1, std::time::Duration::from_millis(100));
388        assert_eq!(result.winner, Some(AlgorithmType::SelectionSort));
389        
390        result.complete();
391        assert!(result.is_complete());
392        assert!(result.total_duration.is_some());
393    }
394
395    #[test]
396    fn test_session_state_creation() {
397        let session = SessionState::new();
398        
399        assert_eq!(session.total_races_run, 0);
400        assert!(session.run_history.is_empty());
401        assert!(!session.has_current_race());
402        assert!(session.session_start_time.elapsed().as_millis() < 100); // Recently created
403    }
404
405    #[test]
406    fn test_session_race_lifecycle() {
407        let mut session = SessionState::new();
408        
409        // Start new race
410        assert!(session.start_new_race().is_ok());
411        assert!(session.has_current_race());
412        
413        // Complete race
414        session.complete_current_race();
415        assert!(!session.has_current_race());
416        assert_eq!(session.total_races_run, 1);
417        assert_eq!(session.run_history.len(), 1);
418    }
419
420    #[test]
421    fn test_session_statistics() {
422        let mut session = SessionState::new();
423
424        // Run a couple of races
425        session.start_new_race().unwrap();
426        std::thread::sleep(std::time::Duration::from_millis(1)); // Ensure time passes
427        session.complete_current_race();
428
429        session.start_new_race().unwrap();
430        std::thread::sleep(std::time::Duration::from_millis(1)); // Ensure time passes
431        session.complete_current_race();
432
433        let stats = session.get_race_statistics();
434        assert_eq!(stats.total_races, 2);
435        assert!(stats.session_duration.as_millis() > 0);
436    }
437
438    #[test]
439    fn test_winner_tracking() {
440        let session = SessionState::new();
441        assert_eq!(session.get_most_common_winner(), None);
442        
443        // Would need to set up actual race results to test winner tracking fully
444    }
445
446    #[test]
447    fn test_configuration_validation() {
448        let mut session = SessionState::new();
449        
450        // Default config should be valid
451        assert!(session.start_new_race().is_ok());
452        
453        // Invalid config should fail
454        session.current_config.array_size = 0; // Invalid
455        assert!(session.start_new_race().is_err());
456    }
457}