1use std::fmt;
2
3use crate::{
4 AtlasDecodeLimits, CertificateLimits, Diagram, PersistenceProgram, RipsParams,
5 SparseDistanceMatrix,
6};
7
8use super::{decode, encode, verification};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct ProgramArtifactError {
13 message: String,
14}
15
16impl ProgramArtifactError {
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 ProgramArtifactError {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 write!(f, "program artifact: {}", self.message)
32 }
33}
34
35impl std::error::Error for ProgramArtifactError {}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39#[non_exhaustive]
40pub struct ProgramDecodeLimits {
41 pub max_bytes: usize,
43 pub max_vertices: usize,
45 pub max_atoms: usize,
47 pub max_atom_vertices: usize,
49 pub max_atom_edges: usize,
51 pub max_bars: usize,
53 pub max_atlas_bytes: usize,
55 pub atlas: AtlasDecodeLimits,
57}
58
59impl Default for ProgramDecodeLimits {
60 fn default() -> Self {
61 Self {
62 max_bytes: 1 << 30,
63 max_vertices: 1_000_000,
64 max_atoms: 50_000_000,
65 max_atom_vertices: 100_000_000,
66 max_atom_edges: 100_000_000,
67 max_bars: 100_000_000,
68 max_atlas_bytes: 1 << 30,
69 atlas: AtlasDecodeLimits::default(),
70 }
71 }
72}
73
74#[derive(Debug, Clone)]
76pub struct ProgramAtomArtifact {
77 pub(super) id: usize,
78 pub(super) vertices: Vec<usize>,
79 pub(super) edges: Vec<crate::EdgeKey>,
80 pub(super) atlas: crate::AtlasArtifact,
81}
82
83impl ProgramAtomArtifact {
84 pub fn id(&self) -> usize {
86 self.id
87 }
88
89 pub fn vertices(&self) -> &[usize] {
91 &self.vertices
92 }
93
94 pub fn edges(&self) -> &[crate::EdgeKey] {
96 &self.edges
97 }
98
99 pub fn atlas(&self) -> &crate::AtlasArtifact {
101 &self.atlas
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct ProgramArtifact {
108 pub(super) vertex_count: usize,
109 pub(super) threshold: Option<f64>,
110 pub(super) modulus: u32,
111 pub(super) input_digest: [u8; 32],
112 pub(super) diagram: Diagram,
113 pub(super) atoms: Vec<ProgramAtomArtifact>,
114}
115
116impl ProgramArtifact {
117 pub fn from_program(
122 program: &PersistenceProgram,
123 ) -> std::result::Result<Self, ProgramArtifactError> {
124 Self::capture(program.current_graph(), program)
125 }
126
127 pub fn build(
129 input: &SparseDistanceMatrix,
130 params: &RipsParams,
131 certificate_limits: CertificateLimits,
132 ) -> std::result::Result<Self, ProgramArtifactError> {
133 Self::compile(input, params, certificate_limits).map(|(artifact, _)| artifact)
134 }
135
136 pub fn compile(
138 input: &SparseDistanceMatrix,
139 params: &RipsParams,
140 certificate_limits: CertificateLimits,
141 ) -> std::result::Result<(Self, PersistenceProgram), ProgramArtifactError> {
142 let program = PersistenceProgram::compile(input, params, certificate_limits)
143 .map_err(|error| ProgramArtifactError::new(error.to_string()))?;
144 let artifact = Self::capture(input, &program)?;
145 Ok((artifact, program))
146 }
147
148 pub(crate) fn capture(
149 input: &SparseDistanceMatrix,
150 program: &PersistenceProgram,
151 ) -> std::result::Result<Self, ProgramArtifactError> {
152 let atoms = program
153 .states()
154 .iter()
155 .map(|state| ProgramAtomArtifact {
156 id: state.info_index,
157 vertices: state.vertices.clone(),
158 edges: state.edges.clone(),
159 atlas: state.artifact.clone(),
160 })
161 .collect();
162 let artifact = Self {
163 vertex_count: input.len(),
164 threshold: program.params().threshold,
165 modulus: program.params().modulus,
166 input_digest: verification::program_graph_digest(input, program.params().threshold),
167 diagram: program.result().diagram.clone(),
168 atoms,
169 };
170 artifact.check_structure(ProgramDecodeLimits::default())?;
171 Ok(artifact)
172 }
173
174 pub fn diagram(&self) -> &Diagram {
176 &self.diagram
177 }
178
179 pub fn vertex_count(&self) -> usize {
181 self.vertex_count
182 }
183
184 pub fn threshold(&self) -> Option<f64> {
186 self.threshold
187 }
188
189 pub fn modulus(&self) -> u32 {
191 self.modulus
192 }
193
194 pub fn atoms(&self) -> &[ProgramAtomArtifact] {
196 &self.atoms
197 }
198
199 pub fn encode(&self) -> std::result::Result<Vec<u8>, ProgramArtifactError> {
201 self.check_structure(ProgramDecodeLimits::default())?;
202 let nested = encode::encode_atlases(&self.atoms)?;
203 let mut out = Vec::new();
204 encode::encode_program_header(&mut out, self)?;
205 encode::encode_bars(&mut out, &self.diagram.bars)?;
206 for (atom, atlas) in self.atoms.iter().zip(nested) {
207 encode::encode_atom(&mut out, atom, &atlas)?;
208 }
209 Ok(out)
210 }
211
212 pub fn decode(
214 bytes: &[u8],
215 limits: ProgramDecodeLimits,
216 certificate_limits: CertificateLimits,
217 ) -> std::result::Result<Self, ProgramArtifactError> {
218 decode::check_envelope_size(bytes, limits.max_bytes)?;
219 let mut reader = super::primitives::Reader::new(bytes);
220 let header = decode::decode_program_header(&mut reader, limits)?;
221 decode::check_minimum_record_bytes(&reader, header.bar_count, header.atom_count)?;
222 let bars = decode::decode_bars(&mut reader, header.bar_count)?;
223 let atoms =
224 decode::decode_atoms(&mut reader, header.atom_count, limits, certificate_limits)?;
225 decode::check_no_trailing_bytes(&reader)?;
226 let artifact = Self {
227 vertex_count: header.vertex_count,
228 threshold: header.threshold,
229 modulus: header.modulus,
230 input_digest: header.input_digest,
231 diagram: Diagram { bars },
232 atoms,
233 };
234 artifact.check_structure(limits)?;
235 Ok(artifact)
236 }
237
238 pub fn verify(
240 &self,
241 input: &SparseDistanceMatrix,
242 certificate_limits: CertificateLimits,
243 ) -> std::result::Result<PersistenceProgram, ProgramArtifactError> {
244 self.check_structure(ProgramDecodeLimits::default())?;
245 verification::check_input_binding(self, input)?;
246 let mut params = RipsParams::new(1).with_modulus(self.modulus);
247 params.threshold = self.threshold;
248 let (_, blocks) = crate::factorization::program_blocks(input, self.threshold)
249 .map_err(|error| ProgramArtifactError::new(error.to_string()))?;
250 let infos = crate::program::atom_infos(input, &blocks);
251 let topology: Vec<_> = input
252 .edges()
253 .map(|(u, v, _)| crate::EdgeKey::new(u, v))
254 .collect();
255 let states =
256 verification::verify_atom_states(self, input, &infos, &topology, certificate_limits)?;
257 let result = crate::program::compose_result(input, ¶ms, &states)
258 .map_err(|error| ProgramArtifactError::new(error.to_string()))?;
259 verification::check_composed_diagram(&result.diagram, &self.diagram)?;
260 Ok(PersistenceProgram::from_verified_parts(
261 input,
262 ¶ms,
263 certificate_limits,
264 infos,
265 states,
266 result,
267 ))
268 }
269
270 fn check_structure(
271 &self,
272 limits: ProgramDecodeLimits,
273 ) -> std::result::Result<(), ProgramArtifactError> {
274 verification::check_program_limits(self, limits)?;
275 verification::check_program_diagram(&self.diagram)?;
276 verification::check_program_atoms(self, limits)?;
277 Ok(())
278 }
279}