1use std::fmt;
2
3use crate::{
4 AtlasArtifact, AtlasDecodeLimits, AtlasEvaluation, CertificateLimits, RipsParams,
5 SparseDistanceMatrix, TopologyEvent, UpdateMode,
6};
7
8use super::{decode, encode, verification};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct TrajectoryError {
13 message: String,
14}
15
16impl TrajectoryError {
17 pub(super) fn new(message: impl Into<String>) -> Self {
18 Self {
19 message: message.into(),
20 }
21 }
22
23 pub fn message(&self) -> &str {
25 &self.message
26 }
27}
28
29impl fmt::Display for TrajectoryError {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 write!(f, "trajectory artifact: {}", self.message)
32 }
33}
34
35impl std::error::Error for TrajectoryError {}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39#[non_exhaustive]
40pub struct TrajectoryDecodeLimits {
41 pub max_bytes: usize,
43 pub max_steps: usize,
45 pub max_vertices: usize,
47 pub max_total_edges: usize,
49 pub max_total_events: usize,
51 pub max_atlas_bytes: usize,
53}
54
55impl Default for TrajectoryDecodeLimits {
56 fn default() -> Self {
57 Self {
58 max_bytes: 1 << 30,
59 max_steps: 10_000_000,
60 max_vertices: 1_000_000,
61 max_total_edges: 200_000_000,
62 max_total_events: 100_000_000,
63 max_atlas_bytes: 1 << 30,
64 }
65 }
66}
67
68#[derive(Debug, Clone)]
70pub struct TrajectoryStep {
71 pub(super) input: SparseDistanceMatrix,
72 pub(super) mode: UpdateMode,
73 pub(super) events: Vec<TopologyEvent>,
74 pub(super) checkpoint: Option<AtlasArtifact>,
75}
76
77impl TrajectoryStep {
78 pub fn input(&self) -> &SparseDistanceMatrix {
80 &self.input
81 }
82
83 pub fn mode(&self) -> UpdateMode {
85 self.mode
86 }
87
88 pub fn events(&self) -> &[TopologyEvent] {
90 &self.events
91 }
92
93 pub fn checkpoint(&self) -> Option<&AtlasArtifact> {
95 self.checkpoint.as_ref()
96 }
97}
98
99#[derive(Debug, Clone)]
101pub struct TrajectoryArtifact {
102 pub(super) initial_input: SparseDistanceMatrix,
103 pub(super) initial_atlas: AtlasArtifact,
104 pub(super) steps: Vec<TrajectoryStep>,
105}
106
107impl TrajectoryArtifact {
108 pub fn build(
110 initial: &SparseDistanceMatrix,
111 updates: &[SparseDistanceMatrix],
112 params: &RipsParams,
113 certificate_limits: CertificateLimits,
114 ) -> std::result::Result<Self, TrajectoryError> {
115 let (initial_atlas, mut atlas) =
116 AtlasArtifact::compile(initial, params, certificate_limits)
117 .map_err(|error| TrajectoryError::new(error.to_string()))?;
118 let mut steps = Vec::with_capacity(updates.len());
119 for input in updates {
120 let events = atlas.events(input);
121 if events.is_empty() {
122 atlas
123 .evaluate(input)
124 .map_err(|error| TrajectoryError::new(error.to_string()))?;
125 steps.push(TrajectoryStep {
126 input: input.clone(),
127 mode: UpdateMode::Reused,
128 events,
129 checkpoint: None,
130 });
131 } else {
132 let (checkpoint, next) = AtlasArtifact::compile(input, params, certificate_limits)
133 .map_err(|error| TrajectoryError::new(error.to_string()))?;
134 atlas = next;
135 steps.push(TrajectoryStep {
136 input: input.clone(),
137 mode: UpdateMode::Recomputed,
138 events,
139 checkpoint: Some(checkpoint),
140 });
141 }
142 }
143 Ok(Self {
144 initial_input: initial.clone(),
145 initial_atlas,
146 steps,
147 })
148 }
149
150 pub fn initial_input(&self) -> &SparseDistanceMatrix {
152 &self.initial_input
153 }
154
155 pub fn initial_atlas(&self) -> &AtlasArtifact {
157 &self.initial_atlas
158 }
159
160 pub fn steps(&self) -> &[TrajectoryStep] {
162 &self.steps
163 }
164
165 pub fn encode(&self) -> std::result::Result<Vec<u8>, TrajectoryError> {
167 self.check_shape()?;
168 let initial_atlas = encode::encode_atlas(&self.initial_atlas)?;
169 let checkpoints = encode::encode_checkpoints(&self.steps)?;
170 let mut out = Vec::new();
171 encode::encode_trajectory_header(&mut out, self.steps.len(), initial_atlas.len())?;
172 encode::encode_graph(&mut out, &self.initial_input)?;
173 out.extend_from_slice(&initial_atlas);
174 for (step, checkpoint) in self.steps.iter().zip(checkpoints) {
175 encode::encode_trajectory_step(&mut out, step, checkpoint.as_deref())?;
176 }
177 Ok(out)
178 }
179
180 pub fn decode(
182 bytes: &[u8],
183 limits: TrajectoryDecodeLimits,
184 atlas_limits: AtlasDecodeLimits,
185 certificate_limits: CertificateLimits,
186 ) -> std::result::Result<Self, TrajectoryError> {
187 decode::check_envelope_size(bytes, limits.max_bytes)?;
188 let mut reader = super::primitives::Reader::new(bytes);
189 let header = decode::decode_trajectory_header(&mut reader, limits)?;
190 let mut total_edges = 0usize;
191 let initial_input = decode::decode_graph(&mut reader, limits, &mut total_edges)?;
192 let initial_atlas = decode::decode_nested_atlas(
193 &mut reader,
194 header.initial_atlas_bytes,
195 limits,
196 atlas_limits,
197 certificate_limits,
198 )?;
199 let mut total_events = 0usize;
200 let mut context = decode::TrajectoryDecodeContext {
201 limits,
202 atlas_limits,
203 certificate_limits,
204 total_edges: &mut total_edges,
205 total_events: &mut total_events,
206 };
207 let steps = decode::decode_trajectory_steps(&mut reader, header.step_count, &mut context)?;
208 decode::check_no_trailing_bytes(&reader)?;
209 let artifact = Self {
210 initial_input,
211 initial_atlas,
212 steps,
213 };
214 artifact.check_shape()?;
215 Ok(artifact)
216 }
217
218 pub fn verify(
220 &self,
221 certificate_limits: CertificateLimits,
222 ) -> std::result::Result<VerifiedTrajectory, TrajectoryError> {
223 self.check_shape()?;
224 let mut atlas = verification::verify_atlas(
225 &self.initial_atlas,
226 &self.initial_input,
227 certificate_limits,
228 )?;
229 let initial = atlas
230 .evaluate(&self.initial_input)
231 .map_err(|error| TrajectoryError::new(error.to_string()))?;
232 let mut verified_steps = Vec::with_capacity(self.steps.len());
233 for (index, step) in self.steps.iter().enumerate() {
234 verified_steps.push(verification::verify_trajectory_step(
235 &mut atlas,
236 step,
237 index,
238 certificate_limits,
239 )?);
240 }
241 Ok(VerifiedTrajectory {
242 initial,
243 steps: verified_steps,
244 })
245 }
246
247 fn check_shape(&self) -> std::result::Result<(), TrajectoryError> {
248 for (index, step) in self.steps.iter().enumerate() {
249 let checkpoint_matches = matches!(
250 (step.mode, step.checkpoint.is_some()),
251 (UpdateMode::Reused, false) | (UpdateMode::Recomputed, true)
252 );
253 if !checkpoint_matches {
254 return Err(TrajectoryError::new(format!(
255 "step {index} checkpoint does not match its mode"
256 )));
257 }
258 if (step.mode == UpdateMode::Reused) != step.events.is_empty() {
259 return Err(TrajectoryError::new(format!(
260 "step {index} event count does not match its mode"
261 )));
262 }
263 }
264 Ok(())
265 }
266}
267
268#[derive(Debug, Clone)]
270pub struct VerifiedTrajectoryStep {
271 pub mode: UpdateMode,
273 pub events: Vec<TopologyEvent>,
275 pub evaluation: AtlasEvaluation,
277}
278
279#[derive(Debug, Clone)]
281pub struct VerifiedTrajectory {
282 pub initial: AtlasEvaluation,
284 pub steps: Vec<VerifiedTrajectoryStep>,
286}