honeycomb_core/cmap/dim2/basic_ops.rs
1//! Basic operations implementation
2//!
3//! This module contains code used to implement basic operations of combinatorial maps, such as
4//! (but not limited to):
5//!
6//! - Dart addition / insertion / removal
7//! - Beta function interfaces
8//! - i-cell computations
9
10use std::collections::{HashSet, VecDeque};
11
12use crate::attributes::UnknownAttributeStorage;
13use crate::cmap::{
14 CMap2, DartIdType, EdgeIdType, FaceIdType, Orbit2, OrbitPolicy, VertexIdType, NULL_DART_ID,
15};
16use crate::geometry::CoordsFloat;
17use crate::stm::{atomically, StmClosureResult, Transaction};
18
19/// **Dart-related methods**
20impl<T: CoordsFloat> CMap2<T> {
21 // --- read
22
23 /// Return the current number of darts.
24 #[must_use = "unused return value"]
25 pub fn n_darts(&self) -> usize {
26 self.n_darts
27 }
28
29 /// Return the current number of unused darts.
30 #[must_use = "unused return value"]
31 pub fn n_unused_darts(&self) -> usize {
32 self.unused_darts.iter().filter(|v| v.read_atomic()).count()
33 }
34
35 /// Return whether a given dart is unused or not.
36 #[must_use = "unused return value"]
37 pub fn is_unused(&self, d: DartIdType) -> bool {
38 self.unused_darts[d].read_atomic()
39 }
40
41 /// Return whether a given dart is unused or not.
42 ///
43 /// # Errors
44 ///
45 /// This method is meant to be called in a context where the returned `Result` is used to
46 /// validate the transaction passed as argument. Errors should not be processed manually,
47 /// only processed via the `?` operator.
48 #[must_use = "unused return value"]
49 pub fn is_unused_transac(
50 &self,
51 trans: &mut Transaction,
52 d: DartIdType,
53 ) -> StmClosureResult<bool> {
54 self.unused_darts[d].read(trans)
55 }
56
57 // --- edit
58
59 /// Add a new free dart to the map.
60 ///
61 /// # Return
62 ///
63 /// Return the ID of the new dart.
64 pub fn add_free_dart(&mut self) -> DartIdType {
65 let new_id = self.n_darts as DartIdType;
66 self.n_darts += 1;
67 self.betas.extend(1);
68 self.unused_darts.extend(1);
69 self.vertices.extend(1);
70 self.attributes.extend_storages(1);
71 new_id
72 }
73
74 /// Add `n_darts` new free darts to the map.
75 ///
76 /// # Return
77 ///
78 /// Return the ID of the first new dart. Other IDs are in the range `ID..ID+n_darts`.
79 pub fn add_free_darts(&mut self, n_darts: usize) -> DartIdType {
80 let new_id = self.n_darts as DartIdType;
81 self.n_darts += n_darts;
82 self.betas.extend(n_darts);
83 self.unused_darts.extend(n_darts);
84 self.vertices.extend(n_darts);
85 self.attributes.extend_storages(n_darts);
86 new_id
87 }
88
89 /// Insert a new free dart in the map.
90 ///
91 /// The dart may be inserted into an unused spot of the existing dart list. If no free spots
92 /// exist, it will be pushed to the end of the list.
93 ///
94 /// # Return
95 ///
96 /// Return the ID of the new dart.
97 pub fn insert_free_dart(&mut self) -> DartIdType {
98 if let Some((new_id, _)) = self
99 .unused_darts
100 .iter()
101 .enumerate()
102 .find(|(_, u)| u.read_atomic())
103 {
104 atomically(|trans| self.unused_darts[new_id as DartIdType].write(trans, false));
105 new_id as DartIdType
106 } else {
107 self.add_free_dart()
108 }
109 }
110
111 /// Remove a free dart from the map.
112 ///
113 /// The removed dart identifier is added to the list of free dart. This way of proceeding is
114 /// necessary as the structure relies on darts indexing for encoding data, making reordering of
115 /// any sort extremely costly.
116 ///
117 /// # Arguments
118 ///
119 /// - `dart_id: DartIdentifier` -- Identifier of the dart to remove.
120 ///
121 /// # Panics
122 ///
123 /// This method may panic if:
124 /// - the dart is not *i*-free for all *i*,
125 /// - the dart is already marked as unused.
126 pub fn remove_free_dart(&mut self, dart_id: DartIdType) {
127 atomically(|trans| {
128 assert!(self.is_free(dart_id)); // all beta images are 0
129 assert!(!self.unused_darts[dart_id as DartIdType].replace(trans, true)?);
130 Ok(())
131 });
132 }
133}
134
135/// **Beta-related methods**
136impl<T: CoordsFloat> CMap2<T> {
137 // --- read
138
139 /// Return β<sub>`I`</sub>(`dart_id`).
140 ///
141 /// # Panics
142 ///
143 /// The method will panic if `I` is not 0, 1 or 2.
144 #[must_use = "unused return value"]
145 pub fn beta<const I: u8>(&self, dart_id: DartIdType) -> DartIdType {
146 assert!(I < 3);
147 self.betas[(I, dart_id)].read_atomic()
148 }
149
150 /// Return β<sub>`i`</sub>(`dart_id`).
151 ///
152 /// # Panics
153 ///
154 /// The method will panic if `i` is not 0, 1 or 2.
155 #[must_use = "unused return value"]
156 pub fn beta_rt(&self, i: u8, dart_id: DartIdType) -> DartIdType {
157 assert!(i < 3);
158 match i {
159 0 => self.beta::<0>(dart_id),
160 1 => self.beta::<1>(dart_id),
161 2 => self.beta::<2>(dart_id),
162 _ => unreachable!(),
163 }
164 }
165
166 /// Return β<sub>`I`</sub>(`dart_id`).
167 ///
168 /// # Errors
169 ///
170 /// This method is meant to be called in a context where the returned `Result` is used to
171 /// validate the transaction passed as argument. Errors should not be processed manually,
172 /// only processed via the `?` operator.
173 ///
174 /// # Panics
175 ///
176 /// The method will panic if `I` is not 0, 1 or 2.
177 pub fn beta_transac<const I: u8>(
178 &self,
179 trans: &mut Transaction,
180 dart_id: DartIdType,
181 ) -> StmClosureResult<DartIdType> {
182 assert!(I < 3);
183 self.betas[(I, dart_id)].read(trans)
184 }
185
186 /// Return β<sub>`i`</sub>(`dart_id`).
187 ///
188 /// # Errors
189 ///
190 /// This method is meant to be called in a context where the returned `Result` is used to
191 /// validate the transaction passed as argument. Errors should not be processed manually,
192 /// only processed via the `?` operator.
193 ///
194 /// # Panics
195 ///
196 /// The method will panic if `i` is not 0, 1 or 2.
197 pub fn beta_rt_transac(
198 &self,
199 trans: &mut Transaction,
200 i: u8,
201 dart_id: DartIdType,
202 ) -> StmClosureResult<DartIdType> {
203 assert!(i < 3);
204 match i {
205 0 => self.beta_transac::<0>(trans, dart_id),
206 1 => self.beta_transac::<1>(trans, dart_id),
207 2 => self.beta_transac::<2>(trans, dart_id),
208 _ => unreachable!(),
209 }
210 }
211
212 /// Check if a given dart is `I`-free.
213 ///
214 /// # Return
215 ///
216 /// Return a boolean indicating if the dart is `I`-free, i.e.:
217 /// - `true` if β<sub>`I`</sub>(`dart_id`) = `NULL_DART_ID`,
218 /// - `false` else.
219 ///
220 /// # Panics
221 ///
222 /// The function will panic if *I* is not 0, 1 or 2.
223 ///
224 #[must_use = "unused return value"]
225 pub fn is_i_free<const I: u8>(&self, dart_id: DartIdType) -> bool {
226 self.beta::<I>(dart_id) == NULL_DART_ID
227 }
228
229 /// Check if a given dart is `i`-free, for all `i`.
230 ///
231 /// # Return
232 ///
233 /// Return a boolean indicating if the dart is 0-free, 1-free **and** 2-free.
234 #[must_use = "unused return value"]
235 pub fn is_free(&self, dart_id: DartIdType) -> bool {
236 self.beta::<0>(dart_id) == NULL_DART_ID
237 && self.beta::<1>(dart_id) == NULL_DART_ID
238 && self.beta::<2>(dart_id) == NULL_DART_ID
239 }
240}
241
242/// **I-cell-related methods**
243impl<T: CoordsFloat> CMap2<T> {
244 /// Compute the ID of the vertex a given dart is part of.
245 ///
246 /// This corresponds to the minimum dart ID among darts composing the 0-cell orbit.
247 #[must_use = "unused return value"]
248 pub fn vertex_id(&self, dart_id: DartIdType) -> VertexIdType {
249 atomically(|trans| self.vertex_id_transac(trans, dart_id))
250 }
251
252 /// Compute the ID of the vertex a given dart is part of.
253 ///
254 /// This corresponds to the minimum dart ID among darts composing the 0-cell orbit.
255 ///
256 /// # Errors
257 ///
258 /// This method is meant to be called in a context where the returned `Result` is used to
259 /// validate the transaction passed as argument. Errors should not be processed manually,
260 /// only processed via the `?` operator.
261 pub fn vertex_id_transac(
262 &self,
263 trans: &mut Transaction,
264 dart_id: DartIdType,
265 ) -> StmClosureResult<VertexIdType> {
266 // min encountered / current dart
267 let mut min = dart_id;
268 let mut marked = HashSet::new();
269 marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit
270 marked.insert(dart_id); // we're starting here, so we mark it beforehand
271 let mut pending = VecDeque::from([dart_id]);
272
273 while let Some(d) = pending.pop_front() {
274 // THIS CODE IS ONLY VALID IN 2D
275 let (b2d, b0d) = (
276 self.beta_transac::<2>(trans, d)?,
277 self.beta_transac::<0>(trans, d)?,
278 );
279 let image1 = self.beta_transac::<1>(trans, b2d)?;
280 if marked.insert(image1) {
281 // if true, we did not see this dart yet
282 // i.e. we need to visit it later
283 min = min.min(image1);
284 pending.push_back(image1);
285 }
286 let image2 = self.beta_transac::<2>(trans, b0d)?;
287 if marked.insert(image2) {
288 // if true, we did not see this dart yet
289 // i.e. we need to visit it later
290 min = min.min(image2);
291 pending.push_back(image2);
292 }
293 }
294
295 Ok(min)
296 }
297
298 /// Compute the ID of the edge a given dart is part of.
299 ///
300 /// This corresponds to the minimum dart ID among darts composing the 1-cell orbit.
301 #[must_use = "unused return value"]
302 pub fn edge_id(&self, dart_id: DartIdType) -> EdgeIdType {
303 atomically(|trans| self.edge_id_transac(trans, dart_id))
304 }
305
306 /// Compute the ID of the edge a given dart is part of.
307 ///
308 /// This corresponds to the minimum dart ID among darts composing the 1-cell orbit.
309 ///
310 /// # Errors
311 ///
312 /// This method is meant to be called in a context where the returned `Result` is used to
313 /// validate the transaction passed as argument. Errors should not be processed manually,
314 /// only processed via the `?` operator.
315 pub fn edge_id_transac(
316 &self,
317 trans: &mut Transaction,
318 dart_id: DartIdType,
319 ) -> StmClosureResult<EdgeIdType> {
320 // optimizing this one bc I'm tired
321 let b2 = self.beta_transac::<2>(trans, dart_id)?;
322 if b2 == NULL_DART_ID {
323 Ok(dart_id as EdgeIdType)
324 } else {
325 Ok(b2.min(dart_id) as EdgeIdType)
326 }
327 }
328
329 /// Compute the ID of the face a given dart is part of.
330 ///
331 /// This corresponds to the minimum dart ID among darts composing the 2-cell orbit.
332 #[must_use = "unused return value"]
333 pub fn face_id(&self, dart_id: DartIdType) -> FaceIdType {
334 atomically(|trans| self.face_id_transac(trans, dart_id))
335 }
336
337 /// Compute the ID of the face a given dart is part of.
338 ///
339 /// This corresponds to the minimum dart ID among darts composing the 2-cell orbit.
340 ///
341 /// # Errors
342 ///
343 /// This method is meant to be called in a context where the returned `Result` is used to
344 /// validate the transaction passed as argument. Errors should not be processed manually,
345 /// only processed via the `?` operator.
346 pub fn face_id_transac(
347 &self,
348 trans: &mut Transaction,
349 dart_id: DartIdType,
350 ) -> StmClosureResult<FaceIdType> {
351 // min encountered / current dart
352 let mut min = dart_id;
353 let mut marked = HashSet::new();
354 marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit
355 marked.insert(dart_id); // we're starting here, so we mark it beforehand
356 let mut pending = VecDeque::from([dart_id]);
357
358 while let Some(d) = pending.pop_front() {
359 // THIS CODE IS ONLY VALID IN 2D
360 let image1 = self.beta_transac::<1>(trans, d)?;
361 if marked.insert(image1) {
362 // if true, we did not see this dart yet
363 // i.e. we need to visit it later
364 min = min.min(image1);
365 pending.push_back(image1);
366 }
367 let image2 = self.beta_transac::<0>(trans, d)?;
368 if marked.insert(image2) {
369 // if true, we did not see this dart yet
370 // i.e. we need to visit it later
371 min = min.min(image2);
372 pending.push_back(image2);
373 }
374 }
375
376 Ok(min)
377 }
378
379 /// Return the orbit defined by a dart and its `I`-cell.
380 ///
381 /// # Usage
382 ///
383 /// The [`Orbit2`] can be iterated upon to retrieve all dart member of the cell. Note that
384 /// **the dart passed as an argument is included as the first element of the returned orbit**.
385 ///
386 /// # Panics
387 ///
388 /// The method will panic if *I* is not 0, 1 or 2.
389 #[must_use = "unused return value"]
390 pub fn i_cell<const I: u8>(&self, dart_id: DartIdType) -> Orbit2<T> {
391 assert!(I < 3);
392 match I {
393 0 => Orbit2::<'_, T>::new(self, OrbitPolicy::Vertex, dart_id),
394 1 => Orbit2::<'_, T>::new(self, OrbitPolicy::Edge, dart_id),
395 2 => Orbit2::<'_, T>::new(self, OrbitPolicy::Face, dart_id),
396 _ => unreachable!(),
397 }
398 }
399
400 /// Return an iterator over IDs of all the map's vertices.
401 #[must_use = "unused return value"]
402 pub fn iter_vertices(&self) -> impl Iterator<Item = VertexIdType> + '_ {
403 (1..self.n_darts() as DartIdType)
404 .zip(self.unused_darts.iter().skip(1))
405 .filter_map(
406 |(d, unused)| {
407 if unused.read_atomic() {
408 None
409 } else {
410 Some(d)
411 }
412 },
413 )
414 .filter_map(|d| {
415 let vid = self.vertex_id(d);
416 if d == vid {
417 Some(vid)
418 } else {
419 None
420 }
421 })
422 }
423
424 /// Return an iterator over IDs of all the map's edges.
425 #[must_use = "unused return value"]
426 pub fn iter_edges(&self) -> impl Iterator<Item = EdgeIdType> + '_ {
427 (1..self.n_darts() as DartIdType)
428 .zip(self.unused_darts.iter().skip(1))
429 .filter_map(
430 |(d, unused)| {
431 if unused.read_atomic() {
432 None
433 } else {
434 Some(d)
435 }
436 },
437 )
438 .filter_map(|d| {
439 let eid = self.edge_id(d);
440 if d == eid {
441 Some(eid)
442 } else {
443 None
444 }
445 })
446 }
447
448 /// Return an iterator over IDs of all the map's faces.
449 #[must_use = "unused return value"]
450 pub fn iter_faces(&self) -> impl Iterator<Item = FaceIdType> + '_ {
451 (1..self.n_darts() as DartIdType)
452 .zip(self.unused_darts.iter().skip(1))
453 .filter_map(
454 |(d, unused)| {
455 if unused.read_atomic() {
456 None
457 } else {
458 Some(d)
459 }
460 },
461 )
462 .filter_map(|d| {
463 let fid = self.face_id(d);
464 if d == fid {
465 Some(fid)
466 } else {
467 None
468 }
469 })
470 }
471}