1#![allow(
2 clippy::cast_precision_loss,
3 clippy::cast_possible_truncation,
4 clippy::cast_sign_loss,
5 clippy::float_cmp
6)]
7
8use std::collections::HashMap;
9use std::hash::Hash;
10use std::sync::atomic::AtomicUsize;
11use std::sync::{Arc, RwLock};
12use std::time::{Duration, Instant};
13
14static STATS_CACHE: std::sync::LazyLock<StatisticsCache> =
16 std::sync::LazyLock::new(StatisticsCache::new);
17static DIST_CACHE: std::sync::LazyLock<DistributionCache> =
18 std::sync::LazyLock::new(DistributionCache::new);
19
20pub struct TtlCache<K, V> {
22 data: Arc<RwLock<HashMap<K, CacheEntry<V>>>>,
23 ttl: Duration,
24 hit_count: Arc<AtomicUsize>,
25 miss_count: Arc<AtomicUsize>,
26}
27
28struct CacheEntry<V> {
29 value: V,
30 created_at: Instant,
31}
32
33impl<K, V> TtlCache<K, V>
34where
35 K: Hash + Eq + Clone,
36 V: Clone,
37{
38 #[must_use]
40 pub fn new(ttl: Duration) -> Self {
41 Self {
42 data: Arc::new(RwLock::new(HashMap::new())),
43 ttl,
44 hit_count: Arc::new(AtomicUsize::new(0)),
45 miss_count: Arc::new(AtomicUsize::new(0)),
46 }
47 }
48
49 pub fn get(&self, key: &K) -> Option<V> {
51 let cache = self.data.read().ok()?;
52 let entry = cache.get(key)?;
53
54 if entry.created_at.elapsed() < self.ttl {
55 self.hit_count
56 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
57 Some(entry.value.clone())
58 } else {
59 self.miss_count
60 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
61 None
62 }
63 }
64
65 pub fn insert(&self, key: K, value: V) {
67 if let Ok(mut cache) = self.data.write() {
68 cache.insert(
69 key,
70 CacheEntry {
71 value,
72 created_at: Instant::now(),
73 },
74 );
75 }
76 }
77
78 pub fn get_or_compute<F>(&self, key: K, compute_fn: F) -> V
80 where
81 F: FnOnce() -> V,
82 {
83 if let Some(cached) = self.get(&key) {
84 return cached;
85 }
86
87 self.miss_count
88 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
89 let value = compute_fn();
90 self.insert(key, value.clone());
91 value
92 }
93
94 pub fn cleanup_expired(&self) {
96 if let Ok(mut cache) = self.data.write() {
97 cache.retain(|_, entry| entry.created_at.elapsed() < self.ttl);
98 }
99 }
100
101 pub fn clear(&self) {
103 if let Ok(mut cache) = self.data.write() {
104 cache.clear();
105 }
106 }
107
108 #[must_use]
110 pub fn len(&self) -> usize {
111 self.data.read().map_or(0, |cache| cache.len())
112 }
113
114 #[must_use]
116 pub fn is_empty(&self) -> bool {
117 self.len() == 0
118 }
119
120 #[must_use]
122 pub fn hit_rate(&self) -> f64 {
123 let hits = self.hit_count.load(std::sync::atomic::Ordering::Relaxed);
124 let misses = self.miss_count.load(std::sync::atomic::Ordering::Relaxed);
125 let total = hits + misses;
126 if total == 0 {
127 0.0
128 } else {
129 (hits as f64 / total as f64) * 100.0
130 }
131 }
132
133 #[must_use]
135 pub fn cache_stats(&self) -> CacheStats {
136 let hits = self.hit_count.load(std::sync::atomic::Ordering::Relaxed);
137 let misses = self.miss_count.load(std::sync::atomic::Ordering::Relaxed);
138 CacheStats { hits, misses }
139 }
140
141 pub fn reset_stats(&self) {
143 self.hit_count
144 .store(0, std::sync::atomic::Ordering::Relaxed);
145 self.miss_count
146 .store(0, std::sync::atomic::Ordering::Relaxed);
147 }
148}
149
150impl<K, V> Default for TtlCache<K, V>
151where
152 K: Hash + Eq + Clone,
153 V: Clone,
154{
155 fn default() -> Self {
156 Self::new(Duration::from_secs(300)) }
158}
159
160pub struct StatisticsCache {
162 expected_value: TtlCache<(uuid::Uuid, usize), f64>,
163 variance: TtlCache<(uuid::Uuid, usize), f64>,
164 std_dev: TtlCache<(uuid::Uuid, usize), f64>,
165 skewness: TtlCache<(uuid::Uuid, usize), f64>,
166 kurtosis: TtlCache<(uuid::Uuid, usize), f64>,
167 confidence_intervals: TtlCache<(uuid::Uuid, usize, u64), (f64, f64)>, cdf: TtlCache<(uuid::Uuid, usize, u64), f64>, quantiles: TtlCache<(uuid::Uuid, usize, u64), f64>, }
171
172impl StatisticsCache {
173 #[must_use]
175 pub fn new() -> Self {
176 let ttl = Duration::from_secs(300); Self {
178 expected_value: TtlCache::new(ttl),
179 variance: TtlCache::new(ttl),
180 std_dev: TtlCache::new(ttl),
181 skewness: TtlCache::new(ttl),
182 kurtosis: TtlCache::new(ttl),
183 confidence_intervals: TtlCache::new(ttl),
184 cdf: TtlCache::new(ttl),
185 quantiles: TtlCache::new(ttl),
186 }
187 }
188
189 pub fn get_or_compute_expected_value<F>(
191 &self,
192 id: uuid::Uuid,
193 sample_count: usize,
194 compute: F,
195 ) -> f64
196 where
197 F: FnOnce() -> f64,
198 {
199 self.expected_value
200 .get_or_compute((id, sample_count), compute)
201 }
202
203 pub fn get_or_compute_variance<F>(&self, id: uuid::Uuid, sample_count: usize, compute: F) -> f64
205 where
206 F: FnOnce() -> f64,
207 {
208 self.variance.get_or_compute((id, sample_count), compute)
209 }
210
211 pub fn get_or_compute_std_dev<F>(&self, id: uuid::Uuid, sample_count: usize, compute: F) -> f64
213 where
214 F: FnOnce() -> f64,
215 {
216 self.std_dev.get_or_compute((id, sample_count), compute)
217 }
218
219 pub fn get_or_compute_skewness<F>(&self, id: uuid::Uuid, sample_count: usize, compute: F) -> f64
221 where
222 F: FnOnce() -> f64,
223 {
224 self.skewness.get_or_compute((id, sample_count), compute)
225 }
226
227 pub fn get_or_compute_kurtosis<F>(&self, id: uuid::Uuid, sample_count: usize, compute: F) -> f64
229 where
230 F: FnOnce() -> f64,
231 {
232 self.kurtosis.get_or_compute((id, sample_count), compute)
233 }
234
235 pub fn get_or_compute_confidence_interval<F>(
237 &self,
238 id: uuid::Uuid,
239 sample_count: usize,
240 confidence: f64,
241 compute: F,
242 ) -> (f64, f64)
243 where
244 F: FnOnce() -> (f64, f64),
245 {
246 let confidence_key = Self::quantize_float(confidence, 0.001);
247 self.confidence_intervals
248 .get_or_compute((id, sample_count, confidence_key), compute)
249 }
250
251 pub fn get_or_compute_cdf<F>(
253 &self,
254 id: uuid::Uuid,
255 sample_count: usize,
256 value: f64,
257 compute: F,
258 ) -> f64
259 where
260 F: FnOnce() -> f64,
261 {
262 let value_key = Self::quantize_float(value, 0.001);
263 self.cdf
264 .get_or_compute((id, sample_count, value_key), compute)
265 }
266
267 pub fn get_or_compute_quantile<F>(
269 &self,
270 id: uuid::Uuid,
271 sample_count: usize,
272 q: f64,
273 compute: F,
274 ) -> f64
275 where
276 F: FnOnce() -> f64,
277 {
278 let q_key = Self::quantize_float(q, 0.001);
279 self.quantiles
280 .get_or_compute((id, sample_count, q_key), compute)
281 }
282
283 pub fn clear_all(&self) {
285 self.expected_value.clear();
286 self.variance.clear();
287 self.std_dev.clear();
288 self.skewness.clear();
289 self.kurtosis.clear();
290 self.confidence_intervals.clear();
291 self.cdf.clear();
292 self.quantiles.clear();
293 }
294
295 pub fn cleanup_all_expired(&self) {
297 self.expected_value.cleanup_expired();
298 self.variance.cleanup_expired();
299 self.std_dev.cleanup_expired();
300 self.skewness.cleanup_expired();
301 self.kurtosis.cleanup_expired();
302 self.confidence_intervals.cleanup_expired();
303 self.cdf.cleanup_expired();
304 self.quantiles.cleanup_expired();
305 }
306
307 fn quantize_float(value: f64, precision: f64) -> u64 {
310 (value / precision).round() as u64
311 }
312
313 #[must_use]
315 pub fn overall_stats(&self) -> CacheStats {
316 let mut total_hits = 0;
317 let mut total_misses = 0;
318
319 let stats = [
320 self.expected_value.cache_stats(),
321 self.variance.cache_stats(),
322 self.std_dev.cache_stats(),
323 self.skewness.cache_stats(),
324 self.kurtosis.cache_stats(),
325 self.confidence_intervals.cache_stats(),
326 self.cdf.cache_stats(),
327 self.quantiles.cache_stats(),
328 ];
329
330 for stat in &stats {
331 total_hits += stat.hits;
332 total_misses += stat.misses;
333 }
334
335 CacheStats {
336 hits: total_hits,
337 misses: total_misses,
338 }
339 }
340}
341
342impl Default for StatisticsCache {
343 fn default() -> Self {
344 Self::new()
345 }
346}
347
348pub struct DistributionCache {
350 samples: TtlCache<(uuid::Uuid, usize), Vec<f64>>,
351 pdf_kde: TtlCache<(uuid::Uuid, usize, u64, u64), f64>, }
353
354impl DistributionCache {
355 #[must_use]
357 pub fn new() -> Self {
358 let ttl = Duration::from_secs(300); Self {
360 samples: TtlCache::new(ttl),
361 pdf_kde: TtlCache::new(ttl),
362 }
363 }
364
365 pub fn get_or_compute_samples<F>(
367 &self,
368 id: uuid::Uuid,
369 sample_count: usize,
370 compute: F,
371 ) -> Vec<f64>
372 where
373 F: FnOnce() -> Vec<f64>,
374 {
375 self.samples.get_or_compute((id, sample_count), compute)
376 }
377
378 pub fn get_or_compute_pdf_kde<F>(
380 &self,
381 id: uuid::Uuid,
382 sample_count: usize,
383 x: f64,
384 bandwidth: f64,
385 compute: F,
386 ) -> f64
387 where
388 F: FnOnce() -> f64,
389 {
390 let x_key = StatisticsCache::quantize_float(x, 0.001);
391 let bandwidth_key = StatisticsCache::quantize_float(bandwidth, 0.0001);
392 self.pdf_kde
393 .get_or_compute((id, sample_count, x_key, bandwidth_key), compute)
394 }
395
396 pub fn clear_all(&self) {
398 self.samples.clear();
399 self.pdf_kde.clear();
400 }
401
402 pub fn cleanup_all_expired(&self) {
404 self.samples.cleanup_expired();
405 self.pdf_kde.cleanup_expired();
406 }
407
408 #[must_use]
410 pub fn overall_stats(&self) -> CacheStats {
411 let samples_stats = self.samples.cache_stats();
412 let pdf_stats = self.pdf_kde.cache_stats();
413
414 CacheStats {
415 hits: samples_stats.hits + pdf_stats.hits,
416 misses: samples_stats.misses + pdf_stats.misses,
417 }
418 }
419}
420
421impl Default for DistributionCache {
422 fn default() -> Self {
423 Self::new()
424 }
425}
426
427#[derive(Debug, Clone, Copy)]
429pub struct CacheStats {
430 pub hits: usize,
431 pub misses: usize,
432}
433
434impl CacheStats {
435 #[must_use]
436 pub fn hit_rate(&self) -> f64 {
437 let total = self.hits + self.misses;
438 if total == 0 {
439 0.0
440 } else {
441 (self.hits as f64 / total as f64) * 100.0
442 }
443 }
444}
445
446#[must_use]
448pub fn stats_cache() -> &'static StatisticsCache {
449 &STATS_CACHE
450}
451
452#[must_use]
454pub fn dist_cache() -> &'static DistributionCache {
455 &DIST_CACHE
456}
457
458pub fn cleanup_global_caches() {
460 STATS_CACHE.cleanup_all_expired();
461 DIST_CACHE.cleanup_all_expired();
462}
463
464pub fn clear_global_caches() {
466 STATS_CACHE.clear_all();
467 DIST_CACHE.clear_all();
468}
469
470#[must_use]
472pub fn global_cache_stats() -> (CacheStats, CacheStats) {
473 (STATS_CACHE.overall_stats(), DIST_CACHE.overall_stats())
474}
475
476pub fn print_cache_report() {
478 let (stats_stats, dist_stats) = global_cache_stats();
479
480 println!("=== Cache Performance Report ===");
481 println!("\nStatistics Cache:");
482 println!(" Hits: {}", stats_stats.hits);
483 println!(" Misses: {}", stats_stats.misses);
484 println!(" Hit Rate: {:.2}%", stats_stats.hit_rate());
485
486 println!("\nDistribution Cache:");
487 println!(" Hits: {}", dist_stats.hits);
488 println!(" Misses: {}", dist_stats.misses);
489 println!(" Hit Rate: {:.2}%", dist_stats.hit_rate());
490
491 let total_hits = stats_stats.hits + dist_stats.hits;
492 let total_misses = stats_stats.misses + dist_stats.misses;
493 let total_requests = total_hits + total_misses;
494
495 if total_requests > 0 {
496 let overall_hit_rate = (total_hits as f64 / total_requests as f64) * 100.0;
497 println!("\nOverall:");
498 println!(" Total Requests: {total_requests}");
499 println!(" Overall Hit Rate: {overall_hit_rate:.2}%");
500 }
501}
502
503#[cfg(test)]
504mod tests {
505 use super::*;
506 use std::thread;
507 use std::time::Duration;
508
509 #[test]
510 fn test_ttl_cache_basic() {
511 let cache = TtlCache::new(Duration::from_millis(100));
512
513 cache.insert("key1", "value1");
514 assert_eq!(cache.get(&"key1"), Some("value1"));
515
516 thread::sleep(Duration::from_millis(150));
517 assert_eq!(cache.get(&"key1"), None);
518 }
519
520 #[test]
521 fn test_ttl_cache_get_or_compute() {
522 let cache = TtlCache::new(Duration::from_secs(1));
523 let mut call_count = 0;
524
525 let result = cache.get_or_compute("test", || {
526 call_count += 1;
527 42
528 });
529 assert_eq!(result, 42);
530 assert_eq!(call_count, 1);
531
532 let result2 = cache.get_or_compute("test", || {
533 call_count += 1;
534 99
535 });
536 assert_eq!(result2, 42);
537 assert_eq!(call_count, 1);
538 }
539
540 #[test]
541 fn test_statistics_cache() {
542 let cache = StatisticsCache::new();
543 let test_id = uuid::Uuid::new_v4();
544
545 let result = cache.get_or_compute_expected_value(test_id, 1000, || 42.0);
546 assert_eq!(result, 42.0);
547
548 let result2 = cache.get_or_compute_expected_value(test_id, 1000, || 99.0);
549 assert_eq!(result2, 42.0);
550 }
551
552 #[test]
553 fn test_distribution_cache() {
554 let cache = DistributionCache::new();
555 let test_id = uuid::Uuid::new_v4();
556
557 let result = cache.get_or_compute_samples(test_id, 100, || vec![1.0, 2.0, 3.0]);
558 assert_eq!(result, vec![1.0, 2.0, 3.0]);
559
560 let result2 = cache.get_or_compute_samples(test_id, 100, || vec![4.0, 5.0, 6.0]);
561 assert_eq!(result2, vec![1.0, 2.0, 3.0]);
562 }
563
564 #[test]
565 fn test_cache_cleanup() {
566 let cache = TtlCache::new(Duration::from_millis(50));
567
568 cache.insert("key1", "value1");
569 cache.insert("key2", "value2");
570 assert_eq!(cache.len(), 2);
571
572 thread::sleep(Duration::from_millis(100));
573 cache.cleanup_expired();
574 assert_eq!(cache.len(), 0);
575 }
576
577 #[test]
578 fn test_cache_clear() {
579 let cache = TtlCache::new(Duration::from_secs(1));
580
581 cache.insert("key1", "value1");
582 cache.insert("key2", "value2");
583 assert_eq!(cache.len(), 2);
584
585 cache.clear();
586 assert_eq!(cache.len(), 0);
587 assert!(cache.is_empty());
588 }
589
590 #[test]
591 fn test_ttl_cache_default() {
592 let cache = TtlCache::<String, i32>::default();
593
594 cache.insert("test".to_string(), 42);
595 assert_eq!(cache.get(&"test".to_string()), Some(42));
596 assert!(!cache.is_empty());
597 }
598
599 #[test]
600 fn test_ttl_cache_is_empty() {
601 let cache = TtlCache::new(Duration::from_secs(1));
602
603 assert!(cache.is_empty());
604 assert_eq!(cache.len(), 0);
605
606 cache.insert("key", "value");
607 assert!(!cache.is_empty());
608 assert_eq!(cache.len(), 1);
609 }
610
611 #[test]
612 fn test_ttl_cache_concurrent_access() {
613 use std::sync::Arc;
614
615 let cache = Arc::new(TtlCache::new(Duration::from_secs(1)));
616 let mut handles = vec![];
617
618 for i in 0..10 {
619 let cache_clone = Arc::clone(&cache);
620 let handle = thread::spawn(move || {
621 cache_clone.insert(format!("key{i}"), i);
622 cache_clone.get(&format!("key{i}"))
623 });
624 handles.push(handle);
625 }
626
627 for handle in handles {
628 let result = handle.join().unwrap();
629 assert!(result.is_some());
630 }
631
632 assert_eq!(cache.len(), 10);
633 }
634
635 #[test]
636 fn test_statistics_cache_all_methods() {
637 let cache = StatisticsCache::new();
638 let test_id = uuid::Uuid::new_v4();
639
640 let variance = cache.get_or_compute_variance(test_id, 1000, || 25.0);
641 assert_eq!(variance, 25.0);
642 let variance2 = cache.get_or_compute_variance(test_id, 1000, || 50.0);
643 assert_eq!(variance2, 25.0); let std_dev = cache.get_or_compute_std_dev(test_id, 1000, || 5.0);
646 assert_eq!(std_dev, 5.0);
647
648 let skewness = cache.get_or_compute_skewness(test_id, 1000, || 0.5);
649 assert_eq!(skewness, 0.5);
650
651 let kurtosis = cache.get_or_compute_kurtosis(test_id, 1000, || 3.0);
652 assert_eq!(kurtosis, 3.0);
653
654 let ci = cache.get_or_compute_confidence_interval(test_id, 1000, 0.95, || (1.0, 2.0));
655 assert_eq!(ci, (1.0, 2.0));
656 let ci2 = cache.get_or_compute_confidence_interval(test_id, 1000, 0.95, || (3.0, 4.0));
657 assert_eq!(ci2, (1.0, 2.0)); let cdf = cache.get_or_compute_cdf(test_id, 1000, 1.5, || 0.75);
660 assert_eq!(cdf, 0.75);
661 let cdf2 = cache.get_or_compute_cdf(test_id, 1000, 1.5, || 0.85);
662 assert_eq!(cdf2, 0.75); let quantile = cache.get_or_compute_quantile(test_id, 1000, 0.5, || 1.0);
665 assert_eq!(quantile, 1.0);
666 let quantile2 = cache.get_or_compute_quantile(test_id, 1000, 0.5, || 2.0);
667 assert_eq!(quantile2, 1.0); }
669
670 #[test]
671 fn test_statistics_cache_clear_and_cleanup() {
672 let cache = StatisticsCache::new();
673 let test_id = uuid::Uuid::new_v4();
674
675 cache.get_or_compute_expected_value(test_id, 1000, || 42.0);
676 cache.get_or_compute_variance(test_id, 1000, || 25.0);
677 cache.get_or_compute_confidence_interval(test_id, 1000, 0.95, || (1.0, 2.0));
678
679 cache.clear_all();
680
681 let result = cache.get_or_compute_expected_value(test_id, 1000, || 99.0);
682 assert_eq!(result, 99.0);
683
684 cache.cleanup_all_expired();
685 let result2 = cache.get_or_compute_expected_value(test_id, 1000, || 88.0);
686 assert_eq!(result2, 99.0); }
688
689 #[test]
690 fn test_statistics_cache_default() {
691 let cache = StatisticsCache::default();
692 let test_id = uuid::Uuid::new_v4();
693
694 let result = cache.get_or_compute_expected_value(test_id, 1000, || 42.0);
695 assert_eq!(result, 42.0);
696 }
697
698 #[test]
699 fn test_distribution_cache_all_methods() {
700 let cache = DistributionCache::new();
701 let test_id = uuid::Uuid::new_v4();
702
703 let samples = cache.get_or_compute_samples(test_id, 100, || vec![1.0, 2.0, 3.0]);
704 assert_eq!(samples, vec![1.0, 2.0, 3.0]);
705
706 let pdf = cache.get_or_compute_pdf_kde(test_id, 100, 1.5, 0.1, || 0.25);
707 assert_eq!(pdf, 0.25);
708 let pdf2 = cache.get_or_compute_pdf_kde(test_id, 100, 1.5, 0.1, || 0.50);
709 assert_eq!(pdf2, 0.25);
710
711 let pdf3 = cache.get_or_compute_pdf_kde(test_id, 100, 1.6, 0.1, || 0.30);
712 assert_eq!(pdf3, 0.30);
713
714 let pdf4 = cache.get_or_compute_pdf_kde(test_id, 100, 1.5, 0.2, || 0.35);
715 assert_eq!(pdf4, 0.35);
716 }
717
718 #[test]
719 fn test_distribution_cache_clear_and_cleanup() {
720 let cache = DistributionCache::new();
721 let test_id = uuid::Uuid::new_v4();
722
723 cache.get_or_compute_samples(test_id, 100, || vec![1.0, 2.0]);
724 cache.get_or_compute_pdf_kde(test_id, 100, 1.5, 0.1, || 0.25);
725
726 cache.clear_all();
727
728 let samples = cache.get_or_compute_samples(test_id, 100, || vec![3.0, 4.0]);
729 assert_eq!(samples, vec![3.0, 4.0]);
730
731 let pdf = cache.get_or_compute_pdf_kde(test_id, 100, 1.5, 0.1, || 0.50);
732 assert_eq!(pdf, 0.50);
733
734 cache.cleanup_all_expired();
735 let samples2 = cache.get_or_compute_samples(test_id, 100, || vec![5.0, 6.0]);
736 assert_eq!(samples2, vec![3.0, 4.0]);
737 }
738
739 #[test]
740 fn test_distribution_cache_default() {
741 let cache = DistributionCache::default();
742 let test_id = uuid::Uuid::new_v4();
743
744 let samples = cache.get_or_compute_samples(test_id, 100, || vec![1.0, 2.0]);
745 assert_eq!(samples, vec![1.0, 2.0]);
746 }
747
748 #[test]
749 fn test_global_cache_functions() {
750 let stats = stats_cache();
751 let dist = dist_cache();
752
753 let test_id = uuid::Uuid::new_v4();
754
755 let result = stats.get_or_compute_expected_value(test_id, 1000, || 42.0);
756 assert_eq!(result, 42.0);
757
758 let samples = dist.get_or_compute_samples(test_id, 100, || vec![1.0, 2.0]);
759 assert_eq!(samples, vec![1.0, 2.0]);
760
761 cleanup_global_caches();
762
763 let result2 = stats.get_or_compute_expected_value(test_id, 1000, || 99.0);
764 assert_eq!(result2, 42.0);
765
766 clear_global_caches();
767
768 let result3 = stats.get_or_compute_expected_value(test_id, 1000, || 99.0);
769 assert_eq!(result3, 99.0);
770
771 let samples2 = dist.get_or_compute_samples(test_id, 100, || vec![3.0, 4.0]);
772 assert_eq!(samples2, vec![3.0, 4.0]);
773 }
774
775 #[test]
776 fn test_cache_key_precision_handling() {
777 let cache = StatisticsCache::new();
778 let test_id = uuid::Uuid::new_v4();
779
780 let ci1 = cache.get_or_compute_confidence_interval(test_id, 1000, 0.95, || (1.0, 2.0));
781 let ci2 = cache.get_or_compute_confidence_interval(test_id, 1000, 0.96, || (3.0, 4.0));
782
783 assert_eq!(ci1, (1.0, 2.0));
784 assert_eq!(ci2, (3.0, 4.0));
785
786 let ci3 = cache.get_or_compute_confidence_interval(test_id, 1000, 0.95, || (5.0, 6.0));
787 assert_eq!(ci3, (1.0, 2.0));
788 }
789}