1use serde::{Deserialize, Serialize};
6
7pub use crate::zhc::ZhcConsensus;
8pub use crate::beam::{BeamSolver, JointEquilibrium, MultiSegmentBeam, JointState};
9pub use crate::pythagorean48::{TrustTopology, TrustVector};
10pub use crate::graph::{FleetGraph, RigidityResult};
11pub use crate::tile::{FleetTile, TileCoordination};
12pub use crate::emergence::{EmergenceDetector, EmergenceResult};
13
14#[derive(Clone, Debug, Serialize, Deserialize)]
16pub struct Config {
17 pub equilibrium_tolerance: f64,
18 pub zhc_tolerance: f64,
19 pub max_iterations: usize,
20 pub trust_bits: f64,
21}
22
23impl Default for Config {
24 fn default() -> Self {
25 Self {
26 equilibrium_tolerance: 1e-6,
27 zhc_tolerance: 0.5,
28 max_iterations: 500,
29 trust_bits: 5.58496,
30 }
31 }
32}
33
34#[derive(Clone)]
36pub struct FleetCoordinate {
37 config: Config,
38 graph: FleetGraph,
39 zhc: ZhcConsensus,
40 beam: BeamSolver,
41 trust: TrustTopology,
42 tiles: Vec<TileCoordination>,
43}
44
45impl FleetCoordinate {
46 pub fn new(config: Config) -> Self {
47 Self {
48 config: config.clone(),
49 graph: FleetGraph::new(),
50 zhc: ZhcConsensus::new(config.zhc_tolerance),
51 beam: BeamSolver::new(config.equilibrium_tolerance),
52 trust: TrustTopology::new(),
53 tiles: Vec::new(),
54 }
55 }
56
57 pub fn add_agent(&mut self, id: u64, position: [f64; 2], capabilities: Vec<String>) {
58 self.graph.add_agent(id, position, capabilities);
59 self.zhc.add_tile(id, [position[0], position[1], 0.0], vec![]);
60 }
61
62 pub fn add_trust_edge(&mut self, a: u64, b: u64) {
63 self.graph.add_edge(a, b);
64 self.zhc.add_tile(a, [0.0, 0.0, 0.0], vec![b]);
66 self.zhc.add_tile(b, [0.0, 0.0, 0.0], vec![a]);
67 }
68
69 pub fn analyze(&self) -> FleetAnalysisReport {
70 let rigidity = self.graph.check_laman_rigidity();
71 let zhc_consensus = self.zhc.run_consensus();
72 let emergence = EmergenceDetector::detect(rigidity.V, rigidity.E, 1);
73 let is_self = rigidity.is_rigid && zhc_consensus.is_consistent && !emergence.emergence_detected;
74
75 FleetAnalysisReport {
76 rigidity: rigidity.clone(),
77 zhc_consensus,
78 emergence: emergence.clone(),
79 config: self.config.clone(),
80 fleet_theorem: FleetTheoremResult {
81 is_laman_rigid: is_self,
82 requires_voting: false,
83 requires_central_coordinator: false,
84 drift_free: true,
85 },
86 }
87 }
88
89 pub fn trust_encoder(&self) -> &TrustTopology {
90 &self.trust
91 }
92
93 pub fn beam_solver(&self) -> &BeamSolver {
94 &self.beam
95 }
96
97 pub fn add_tile(&mut self, room: String, tile: FleetTile) {
98 if let Some(c) = self.tiles.iter_mut().find(|c| c.room == room) {
99 c.add_tile(tile);
100 } else {
101 let mut coord = TileCoordination::new(room);
102 coord.add_tile(tile);
103 self.tiles.push(coord);
104 }
105 }
106
107 pub fn agent_count(&self) -> usize {
108 self.graph.V()
109 }
110
111 pub fn edge_count(&self) -> usize {
112 self.graph.E()
113 }
114}
115
116impl Default for FleetCoordinate {
117 fn default() -> Self {
118 Self::new(Config::default())
119 }
120}
121
122#[derive(Clone, Debug, Serialize, Deserialize)]
124pub struct FleetAnalysisReport {
125 pub rigidity: RigidityResult,
126 pub zhc_consensus: crate::zhc::ConsensusResult,
127 pub emergence: EmergenceResult,
128 pub config: Config,
129 pub fleet_theorem: FleetTheoremResult,
130}
131
132#[derive(Clone, Debug, Serialize, Deserialize)]
134pub struct FleetTheoremResult {
135 pub is_laman_rigid: bool,
136 pub requires_voting: bool,
138 pub requires_central_coordinator: bool,
139 pub drift_free: bool,
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_three_agent_triangle_self_coordinating() {
148 let mut fleet = FleetCoordinate::new(Config::default());
149 fleet.add_agent(1, [0.0, 0.0], vec![]);
150 fleet.add_agent(2, [1.0, 0.0], vec![]);
151 fleet.add_agent(3, [0.5, 0.87], vec![]);
152 fleet.add_trust_edge(1, 2);
153 fleet.add_trust_edge(2, 3);
154 fleet.add_trust_edge(3, 1);
155 let report = fleet.analyze();
156 assert!(report.fleet_theorem.is_laman_rigid);
157 }
158
159 #[test]
160 fn test_trust_encoding_batch() {
161 let fleet = FleetCoordinate::new(Config::default());
162 let batch = fleet.trust_encoder().encode_batch(&[
163 [0.6, 0.8],
164 [0.8, 0.6],
165 [-0.6, 0.8],
166 ]);
167 assert_eq!(batch.len(), 3);
168 }
169}