1use crate::models::session::AlgorithmType;
4use std::collections::HashMap;
5use std::time::Instant;
6
7#[derive(Debug, Clone, PartialEq)]
9pub struct MemoryMetrics {
10 pub algorithm_type: AlgorithmType,
12 pub current_usage_bytes: usize,
14 pub peak_usage_bytes: usize,
16 pub last_updated: Instant,
18}
19
20impl MemoryMetrics {
21 pub fn new(algorithm_type: AlgorithmType) -> Self {
23 Self {
24 algorithm_type,
25 current_usage_bytes: 0,
26 peak_usage_bytes: 0,
27 last_updated: Instant::now(),
28 }
29 }
30
31 pub fn update(&mut self, current_bytes: usize) {
33 self.current_usage_bytes = current_bytes;
34 if current_bytes > self.peak_usage_bytes {
35 self.peak_usage_bytes = current_bytes;
36 }
37 self.last_updated = Instant::now();
38 }
39
40 pub fn reset(&mut self) {
42 self.current_usage_bytes = 0;
43 self.peak_usage_bytes = 0;
44 self.last_updated = Instant::now();
45 }
46
47 pub fn age(&self) -> std::time::Duration {
49 self.last_updated.elapsed()
50 }
51
52 pub fn is_stale(&self, threshold: std::time::Duration) -> bool {
54 self.age() > threshold
55 }
56
57 pub fn format_current(&self) -> String {
59 Self::format_bytes(self.current_usage_bytes)
60 }
61
62 pub fn format_peak(&self) -> String {
64 Self::format_bytes(self.peak_usage_bytes)
65 }
66
67 pub fn format_bytes(bytes: usize) -> String {
69 const UNITS: &[&str] = &["B", "KB", "MB", "GB"];
70 let mut size = bytes as f64;
71 let mut unit_index = 0;
72
73 while size >= 1024.0 && unit_index < UNITS.len() - 1 {
74 size /= 1024.0;
75 unit_index += 1;
76 }
77
78 if unit_index == 0 {
79 format!("{}B", bytes)
80 } else {
81 format!("{:.1}{}", size, UNITS[unit_index])
82 }
83 }
84
85 pub fn efficiency_ratio(&self) -> f64 {
87 if self.peak_usage_bytes == 0 {
88 1.0
89 } else {
90 self.current_usage_bytes as f64 / self.peak_usage_bytes as f64
91 }
92 }
93
94 pub fn is_at_peak(&self) -> bool {
96 self.current_usage_bytes == self.peak_usage_bytes
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct MemoryMetricsCollection {
103 metrics: HashMap<AlgorithmType, MemoryMetrics>,
105 global_peak: usize,
107 start_time: Instant,
109 real_time_enabled: bool,
111 update_frequency: std::time::Duration,
113}
114
115impl MemoryMetricsCollection {
116 pub fn new() -> Self {
118 Self {
119 metrics: HashMap::new(),
120 global_peak: 0,
121 start_time: Instant::now(),
122 real_time_enabled: true,
123 update_frequency: std::time::Duration::from_millis(100), }
125 }
126
127 pub fn with_algorithms(algorithms: &[AlgorithmType]) -> Self {
129 let mut collection = Self::new();
130 for &algorithm in algorithms {
131 collection.add_algorithm(algorithm);
132 }
133 collection
134 }
135
136 pub fn add_algorithm(&mut self, algorithm_type: AlgorithmType) {
138 self.metrics.insert(algorithm_type, MemoryMetrics::new(algorithm_type));
139 }
140
141 pub fn remove_algorithm(&mut self, algorithm_type: AlgorithmType) -> Option<MemoryMetrics> {
143 self.metrics.remove(&algorithm_type)
144 }
145
146 pub fn update_algorithm(&mut self, algorithm_type: AlgorithmType, current_bytes: usize) {
148 if let Some(metrics) = self.metrics.get_mut(&algorithm_type) {
149 metrics.update(current_bytes);
150
151 if current_bytes > self.global_peak {
153 self.global_peak = current_bytes;
154 }
155 } else {
156 let mut metrics = MemoryMetrics::new(algorithm_type);
158 metrics.update(current_bytes);
159 self.metrics.insert(algorithm_type, metrics);
160
161 if current_bytes > self.global_peak {
162 self.global_peak = current_bytes;
163 }
164 }
165 }
166
167 pub fn get_metrics(&self, algorithm_type: AlgorithmType) -> Option<&MemoryMetrics> {
169 self.metrics.get(&algorithm_type)
170 }
171
172 pub fn get_metrics_mut(&mut self, algorithm_type: AlgorithmType) -> Option<&mut MemoryMetrics> {
174 self.metrics.get_mut(&algorithm_type)
175 }
176
177 pub fn get_all_metrics(&self) -> &HashMap<AlgorithmType, MemoryMetrics> {
179 &self.metrics
180 }
181
182 pub fn get_sorted_metrics(&self) -> Vec<(AlgorithmType, &MemoryMetrics)> {
184 let mut metrics: Vec<_> = self.metrics.iter().map(|(&alg, metrics)| (alg, metrics)).collect();
185 metrics.sort_by_key(|(alg, _)| alg.to_index());
186 metrics
187 }
188
189 pub fn get_global_peak(&self) -> usize {
191 self.global_peak
192 }
193
194 pub fn get_total_current_usage(&self) -> usize {
196 self.metrics.values().map(|m| m.current_usage_bytes).sum()
197 }
198
199 pub fn get_total_peak_usage(&self) -> usize {
201 self.metrics.values().map(|m| m.peak_usage_bytes).sum()
202 }
203
204 pub fn clear(&mut self) {
206 self.metrics.clear();
207 self.global_peak = 0;
208 }
209
210 pub fn reset_all(&mut self) {
212 for metrics in self.metrics.values_mut() {
213 metrics.reset();
214 }
215 self.global_peak = 0;
216 }
217
218 pub fn set_real_time_enabled(&mut self, enabled: bool) {
220 self.real_time_enabled = enabled;
221 }
222
223 pub fn is_real_time_enabled(&self) -> bool {
225 self.real_time_enabled
226 }
227
228 pub fn set_update_frequency(&mut self, frequency: std::time::Duration) {
230 self.update_frequency = frequency;
231 }
232
233 pub fn get_update_frequency(&self) -> std::time::Duration {
235 self.update_frequency
236 }
237
238 pub fn get_stale_algorithms(&self, threshold: std::time::Duration) -> Vec<AlgorithmType> {
240 self.metrics
241 .iter()
242 .filter_map(|(&alg, metrics)| {
243 if metrics.is_stale(threshold) {
244 Some(alg)
245 } else {
246 None
247 }
248 })
249 .collect()
250 }
251
252 pub fn get_memory_display_values(&self) -> Vec<(AlgorithmType, crate::models::display_mode::MemoryDisplayValue)> {
254 self.get_sorted_metrics()
255 .into_iter()
256 .map(|(alg, metrics)| {
257 let display_value = if metrics.current_usage_bytes > 0 {
258 crate::models::display_mode::MemoryDisplayValue::Bytes(metrics.current_usage_bytes)
259 } else {
260 crate::models::display_mode::MemoryDisplayValue::NotAvailable
261 };
262 (alg, display_value)
263 })
264 .collect()
265 }
266
267 pub fn age(&self) -> std::time::Duration {
269 self.start_time.elapsed()
270 }
271
272 pub fn get_statistics(&self) -> MemoryStatistics {
274 MemoryStatistics::from_collection(self)
275 }
276
277 pub fn has_active_usage(&self) -> bool {
279 self.metrics.values().any(|m| m.current_usage_bytes > 0)
280 }
281
282 pub fn get_highest_current_usage(&self) -> Option<(AlgorithmType, usize)> {
284 self.metrics
285 .iter()
286 .max_by_key(|(_, metrics)| metrics.current_usage_bytes)
287 .map(|(&alg, metrics)| (alg, metrics.current_usage_bytes))
288 }
289
290 pub fn get_highest_peak_usage(&self) -> Option<(AlgorithmType, usize)> {
292 self.metrics
293 .iter()
294 .max_by_key(|(_, metrics)| metrics.peak_usage_bytes)
295 .map(|(&alg, metrics)| (alg, metrics.peak_usage_bytes))
296 }
297}
298
299impl Default for MemoryMetricsCollection {
300 fn default() -> Self {
301 Self::new()
302 }
303}
304
305#[derive(Debug, Clone)]
307pub struct MemoryStatistics {
308 pub algorithm_count: usize,
310 pub total_current: usize,
312 pub total_peak: usize,
314 pub global_peak: usize,
316 pub average_current: usize,
318 pub average_peak: usize,
320 pub most_efficient: Option<AlgorithmType>,
322 pub least_efficient: Option<AlgorithmType>,
324}
325
326impl MemoryStatistics {
327 pub fn from_collection(collection: &MemoryMetricsCollection) -> Self {
329 let algorithm_count = collection.metrics.len();
330 let total_current = collection.get_total_current_usage();
331 let total_peak = collection.get_total_peak_usage();
332 let global_peak = collection.get_global_peak();
333
334 let average_current = if algorithm_count > 0 { total_current / algorithm_count } else { 0 };
335 let average_peak = if algorithm_count > 0 { total_peak / algorithm_count } else { 0 };
336
337 let mut most_efficient = None;
339 let mut least_efficient = None;
340 let mut best_efficiency = 0.0f64;
341 let mut worst_efficiency = f64::INFINITY;
342
343 for (&alg, metrics) in &collection.metrics {
344 let efficiency = metrics.efficiency_ratio();
345 if efficiency > best_efficiency {
346 best_efficiency = efficiency;
347 most_efficient = Some(alg);
348 }
349 if efficiency < worst_efficiency {
350 worst_efficiency = efficiency;
351 least_efficient = Some(alg);
352 }
353 }
354
355 Self {
356 algorithm_count,
357 total_current,
358 total_peak,
359 global_peak,
360 average_current,
361 average_peak,
362 most_efficient,
363 least_efficient,
364 }
365 }
366}
367
368#[cfg(test)]
369mod tests {
370 use super::*;
371
372 #[test]
373 fn test_memory_metrics_creation() {
374 let metrics = MemoryMetrics::new(AlgorithmType::BubbleSort);
375
376 assert_eq!(metrics.algorithm_type, AlgorithmType::BubbleSort);
377 assert_eq!(metrics.current_usage_bytes, 0);
378 assert_eq!(metrics.peak_usage_bytes, 0);
379 assert!(metrics.age().as_millis() < 100); }
381
382 #[test]
383 fn test_memory_metrics_update() {
384 let mut metrics = MemoryMetrics::new(AlgorithmType::QuickSort);
385
386 metrics.update(1024);
387 assert_eq!(metrics.current_usage_bytes, 1024);
388 assert_eq!(metrics.peak_usage_bytes, 1024);
389
390 metrics.update(512);
391 assert_eq!(metrics.current_usage_bytes, 512);
392 assert_eq!(metrics.peak_usage_bytes, 1024); metrics.update(2048);
395 assert_eq!(metrics.current_usage_bytes, 2048);
396 assert_eq!(metrics.peak_usage_bytes, 2048); }
398
399 #[test]
400 fn test_memory_formatting() {
401 assert_eq!(MemoryMetrics::format_bytes(512), "512B");
402 assert_eq!(MemoryMetrics::format_bytes(1024), "1.0KB");
403 assert_eq!(MemoryMetrics::format_bytes(1536), "1.5KB");
404 assert_eq!(MemoryMetrics::format_bytes(1048576), "1.0MB");
405
406 let metrics = MemoryMetrics::new(AlgorithmType::MergeSort);
407 assert_eq!(metrics.format_current(), "0B");
408 }
409
410 #[test]
411 fn test_memory_efficiency() {
412 let mut metrics = MemoryMetrics::new(AlgorithmType::HeapSort);
413
414 metrics.update(1024);
415 assert_eq!(metrics.efficiency_ratio(), 1.0); assert!(metrics.is_at_peak());
417
418 metrics.update(512);
419 assert_eq!(metrics.efficiency_ratio(), 0.5); assert!(!metrics.is_at_peak());
421 }
422
423 #[test]
424 fn test_memory_collection_creation() {
425 let collection = MemoryMetricsCollection::new();
426
427 assert_eq!(collection.metrics.len(), 0);
428 assert_eq!(collection.global_peak, 0);
429 assert!(collection.is_real_time_enabled());
430
431 let algorithms = vec![AlgorithmType::BubbleSort, AlgorithmType::QuickSort];
432 let collection_with_algs = MemoryMetricsCollection::with_algorithms(&algorithms);
433 assert_eq!(collection_with_algs.metrics.len(), 2);
434 }
435
436 #[test]
437 fn test_memory_collection_operations() {
438 let mut collection = MemoryMetricsCollection::new();
439
440 collection.add_algorithm(AlgorithmType::BubbleSort);
442 assert!(collection.get_metrics(AlgorithmType::BubbleSort).is_some());
443
444 collection.update_algorithm(AlgorithmType::BubbleSort, 1024);
446 let metrics = collection.get_metrics(AlgorithmType::BubbleSort).unwrap();
447 assert_eq!(metrics.current_usage_bytes, 1024);
448 assert_eq!(collection.get_global_peak(), 1024);
449
450 let removed = collection.remove_algorithm(AlgorithmType::BubbleSort);
452 assert!(removed.is_some());
453 assert!(collection.get_metrics(AlgorithmType::BubbleSort).is_none());
454 }
455
456 #[test]
457 fn test_memory_collection_totals() {
458 let mut collection = MemoryMetricsCollection::new();
459
460 collection.update_algorithm(AlgorithmType::BubbleSort, 1024);
461 collection.update_algorithm(AlgorithmType::QuickSort, 2048);
462
463 assert_eq!(collection.get_total_current_usage(), 3072);
464 assert_eq!(collection.get_total_peak_usage(), 3072);
465
466 collection.update_algorithm(AlgorithmType::BubbleSort, 512);
468 assert_eq!(collection.get_total_current_usage(), 2560);
469 assert_eq!(collection.get_total_peak_usage(), 3072); }
471
472 #[test]
473 fn test_memory_statistics() {
474 let mut collection = MemoryMetricsCollection::new();
475
476 collection.update_algorithm(AlgorithmType::BubbleSort, 1024);
477 collection.update_algorithm(AlgorithmType::QuickSort, 2048);
478
479 let stats = collection.get_statistics();
480 assert_eq!(stats.algorithm_count, 2);
481 assert_eq!(stats.total_current, 3072);
482 assert_eq!(stats.average_current, 1536);
483 }
484
485 #[test]
486 fn test_memory_display_values() {
487 let mut collection = MemoryMetricsCollection::new();
488
489 collection.update_algorithm(AlgorithmType::BubbleSort, 1024);
490 collection.add_algorithm(AlgorithmType::QuickSort); let display_values = collection.get_memory_display_values();
493 assert_eq!(display_values.len(), 2);
494
495 let bubble_entry = display_values.iter()
497 .find(|(alg, _)| *alg == AlgorithmType::BubbleSort)
498 .unwrap();
499 assert_eq!(bubble_entry.1, crate::models::display_mode::MemoryDisplayValue::Bytes(1024));
500
501 let quick_entry = display_values.iter()
503 .find(|(alg, _)| *alg == AlgorithmType::QuickSort)
504 .unwrap();
505 assert_eq!(quick_entry.1, crate::models::display_mode::MemoryDisplayValue::NotAvailable);
506 }
507
508 #[test]
509 fn test_highest_usage_algorithms() {
510 let mut collection = MemoryMetricsCollection::new();
511
512 collection.update_algorithm(AlgorithmType::BubbleSort, 1024);
513 collection.update_algorithm(AlgorithmType::QuickSort, 2048);
514 collection.update_algorithm(AlgorithmType::MergeSort, 512);
515
516 let highest_current = collection.get_highest_current_usage();
517 assert_eq!(highest_current, Some((AlgorithmType::QuickSort, 2048)));
518
519 let highest_peak = collection.get_highest_peak_usage();
520 assert_eq!(highest_peak, Some((AlgorithmType::QuickSort, 2048)));
521 }
522}