tatara_core/domain/
multi_distance.rs1use serde::{Deserialize, Serialize};
8use std::collections::BTreeMap;
9
10use super::convergence_state::SubstrateType;
11
12#[derive(Debug, Clone, Serialize, Deserialize, Default)]
14pub struct MultiDimensionalDistance {
15 pub distances: BTreeMap<SubstrateType, f64>,
17}
18
19impl MultiDimensionalDistance {
20 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn set(&mut self, substrate: SubstrateType, distance: f64) {
27 self.distances.insert(substrate, distance);
28 }
29
30 pub fn get(&self, substrate: &SubstrateType) -> f64 {
32 self.distances.get(substrate).copied().unwrap_or(1.0)
33 }
34
35 pub fn overall(&self) -> f64 {
38 self.distances.values().copied().fold(0.0_f64, f64::max)
39 }
40
41 pub fn worst_substrate(&self) -> Option<(SubstrateType, f64)> {
43 self.distances
44 .iter()
45 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
46 .map(|(s, d)| (*s, *d))
47 }
48
49 pub fn is_converged(&self) -> bool {
51 !self.distances.is_empty() && self.distances.values().all(|d| *d == 0.0)
52 }
53
54 pub fn substrate_count(&self) -> usize {
56 self.distances.len()
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
62#[serde(rename_all = "snake_case")]
63pub enum ConvergenceBandwidth {
64 Instant,
66 Seconds(u64),
68 Minutes(u64),
70 Hours(u64),
72 Days(u64),
74 Unbounded,
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn test_empty_distance() {
84 let d = MultiDimensionalDistance::new();
85 assert_eq!(d.overall(), 0.0);
86 assert!(!d.is_converged()); }
88
89 #[test]
90 fn test_single_substrate() {
91 let mut d = MultiDimensionalDistance::new();
92 d.set(SubstrateType::Compute, 0.5);
93 assert_eq!(d.overall(), 0.5);
94 assert!(!d.is_converged());
95 }
96
97 #[test]
98 fn test_all_converged() {
99 let mut d = MultiDimensionalDistance::new();
100 d.set(SubstrateType::Compute, 0.0);
101 d.set(SubstrateType::Network, 0.0);
102 d.set(SubstrateType::Security, 0.0);
103 assert_eq!(d.overall(), 0.0);
104 assert!(d.is_converged());
105 }
106
107 #[test]
108 fn test_worst_substrate() {
109 let mut d = MultiDimensionalDistance::new();
110 d.set(SubstrateType::Compute, 0.1);
111 d.set(SubstrateType::Network, 0.8);
112 d.set(SubstrateType::Security, 0.3);
113 let (worst, dist) = d.worst_substrate().unwrap();
114 assert_eq!(worst, SubstrateType::Network);
115 assert_eq!(dist, 0.8);
116 assert_eq!(d.overall(), 0.8);
117 }
118
119 #[test]
120 fn test_overall_is_max_not_average() {
121 let mut d = MultiDimensionalDistance::new();
122 d.set(SubstrateType::Compute, 0.0);
123 d.set(SubstrateType::Security, 1.0);
124 assert_eq!(d.overall(), 1.0);
126 }
127
128 #[test]
129 fn test_unknown_substrate_defaults_to_1() {
130 let d = MultiDimensionalDistance::new();
131 assert_eq!(d.get(&SubstrateType::Financial), 1.0);
132 }
133
134 #[test]
135 fn test_bandwidth_serde() {
136 for bw in [
137 ConvergenceBandwidth::Instant,
138 ConvergenceBandwidth::Seconds(30),
139 ConvergenceBandwidth::Minutes(5),
140 ConvergenceBandwidth::Hours(2),
141 ConvergenceBandwidth::Days(7),
142 ConvergenceBandwidth::Unbounded,
143 ] {
144 let json = serde_json::to_string(&bw).unwrap();
145 let parsed: ConvergenceBandwidth = serde_json::from_str(&json).unwrap();
146 assert_eq!(bw, parsed);
147 }
148 }
149}