honeycomb_core/cmap/dim3/
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::cell::RefCell;
11use std::collections::{HashSet, VecDeque};
12
13use crate::attributes::UnknownAttributeStorage;
14use crate::cmap::{
15    CMap3, DartIdType, EdgeIdType, FaceIdType, NULL_DART_ID, VertexIdType, VolumeIdType,
16};
17use crate::geometry::CoordsFloat;
18use crate::stm::{StmClosureResult, StmError, Transaction, atomically};
19
20// use thread local hashset and queue for orbit traversal of ID comp.
21// not applied to orbit currently bc they are lazily onsumed, and therefore require dedicated
22// instances to be robust
23thread_local! {
24    static AUXILIARIES: RefCell<(VecDeque<DartIdType>, HashSet<DartIdType>)> = RefCell::new((VecDeque::with_capacity(10), HashSet::with_capacity(10)));
25}
26
27/// **Dart-related methods**
28impl<T: CoordsFloat> CMap3<T> {
29    // --- read
30
31    /// Return the current number of darts.
32    #[must_use = "unused return value"]
33    pub fn n_darts(&self) -> usize {
34        self.unused_darts.len()
35    }
36
37    /// Return the current number of unused darts.
38    #[must_use = "unused return value"]
39    pub fn n_unused_darts(&self) -> usize {
40        self.unused_darts.iter().filter(|v| v.read_atomic()).count()
41    }
42
43    // --- edit
44
45    /// Add a new free dart to the map.
46    ///
47    /// # Return
48    ///
49    /// Returns the ID of the new dart.
50    pub fn add_free_dart(&mut self) -> DartIdType {
51        let new_id = self.n_darts() as DartIdType;
52        self.betas.extend(1);
53        self.unused_darts.extend(1);
54        self.vertices.extend(1);
55        self.attributes.extend_storages(1);
56        new_id
57    }
58
59    /// Add `n_darts` new free darts to the map.
60    ///
61    /// # Return
62    ///
63    /// Returns the ID of the first new dart. Other IDs are in the range `ID..ID+n_darts`.
64    pub fn add_free_darts(&mut self, n_darts: usize) -> DartIdType {
65        let new_id = self.n_darts() as DartIdType;
66        self.betas.extend(n_darts);
67        self.unused_darts.extend(n_darts);
68        self.vertices.extend(n_darts);
69        self.attributes.extend_storages(n_darts);
70        new_id
71    }
72
73    /// Insert a new free dart into the map.
74    ///
75    /// This method attempts to reuse an unused dart slot if available; otherwise, it adds a new one.
76    ///
77    /// # Return
78    ///
79    /// Returns the ID of the inserted dart.
80    pub fn insert_free_dart(&mut self) -> DartIdType {
81        if let Some((new_id, _)) = self
82            .unused_darts
83            .iter()
84            .enumerate()
85            .find(|(_, u)| u.read_atomic())
86        {
87            atomically(|trans| self.unused_darts[new_id as DartIdType].write(trans, false));
88            new_id as DartIdType
89        } else {
90            self.add_free_dart()
91        }
92    }
93
94    /// Remove a free dart from the map.
95    ///
96    /// The removed dart identifier is added to the list of free darts. This way of proceeding is
97    /// necessary as the structure relies on dart indexing for encoding data, making reordering of
98    /// any sort extremely costly.
99    ///
100    /// # Arguments
101    ///
102    /// - `dart_id: DartIdType` -- Identifier of the dart to remove.
103    ///
104    /// # Panics
105    ///
106    /// This method may panic if:
107    /// - the dart is not free for all *i*,
108    /// - the dart is already marked as unused.
109    pub fn remove_free_dart(&mut self, dart_id: DartIdType) {
110        atomically(|trans| {
111            assert!(self.is_free(dart_id)); // all beta images are 0
112            assert!(!self.unused_darts[dart_id as DartIdType].replace(trans, true)?);
113            Ok(())
114        });
115    }
116}
117
118/// **Beta-related methods**
119impl<T: CoordsFloat> CMap3<T> {
120    // --- read
121
122    /// Return β<sub>`I`</sub>(`dart_id`).
123    ///
124    /// # Errors
125    ///
126    /// This method is meant to be called in a context where the returned `Result` is used to
127    /// validate the transaction passed as argument. Errors should not be processed manually,
128    /// only processed via the `?` operator.
129    ///
130    /// # Panics
131    ///
132    /// The method will panic if `I` is not 0, 1, 2, or 3.
133    pub fn beta_transac<const I: u8>(
134        &self,
135        trans: &mut Transaction,
136        dart_id: DartIdType,
137    ) -> StmClosureResult<DartIdType> {
138        assert!(I < 4);
139        self.betas[(I, dart_id)].read(trans)
140    }
141
142    /// Return β<sub>`i`</sub>(`dart_id`).
143    ///
144    /// # Errors
145    ///
146    /// This method is meant to be called in a context where the returned `Result` is used to
147    /// validate the transaction passed as argument. Errors should not be processed manually,
148    /// only processed via the `?` operator.
149    ///
150    /// # Panics
151    ///
152    /// The method will panic if `i` is not 0, 1, 2, or 3.
153    pub fn beta_rt_transac(
154        &self,
155        trans: &mut Transaction,
156        i: u8,
157        dart_id: DartIdType,
158    ) -> StmClosureResult<DartIdType> {
159        assert!(i < 4);
160        match i {
161            0 => self.beta_transac::<0>(trans, dart_id),
162            1 => self.beta_transac::<1>(trans, dart_id),
163            2 => self.beta_transac::<2>(trans, dart_id),
164            3 => self.beta_transac::<3>(trans, dart_id),
165            _ => unreachable!(),
166        }
167    }
168
169    /// Return β<sub>`I`</sub>(`dart_id`).
170    ///
171    /// # Panics
172    ///
173    /// The method will panic if `I` is not 0, 1, 2, or 3.
174    #[must_use = "unused return value"]
175    pub fn beta<const I: u8>(&self, dart_id: DartIdType) -> DartIdType {
176        assert!(I < 4);
177        self.betas[(I, dart_id)].read_atomic()
178    }
179
180    /// Return β<sub>`i`</sub>(`dart_id`).
181    ///
182    /// # Panics
183    ///
184    /// The method will panic if `i` is not 0, 1, 2, or 3.
185    #[must_use = "unused return value"]
186    pub fn beta_rt(&self, i: u8, dart_id: DartIdType) -> DartIdType {
187        assert!(i < 4);
188        match i {
189            0 => self.beta::<0>(dart_id),
190            1 => self.beta::<1>(dart_id),
191            2 => self.beta::<2>(dart_id),
192            3 => self.beta::<3>(dart_id),
193            _ => unreachable!(),
194        }
195    }
196
197    /// Check if a given dart is `I`-free.
198    ///
199    /// # Return
200    ///
201    /// The method returns:
202    /// - `true` if β<sub>`I`</sub>(`dart_id`) = `NULL_DART_ID`,
203    /// - `false` otherwise.
204    ///
205    /// # Panics
206    ///
207    /// The function will panic if *I* is not 0, 1, 2, or 3.
208    #[must_use = "unused return value"]
209    pub fn is_i_free<const I: u8>(&self, dart_id: DartIdType) -> bool {
210        self.beta::<I>(dart_id) == NULL_DART_ID
211    }
212
213    /// Check if a given dart is free for all `i`.
214    ///
215    /// # Return
216    ///
217    /// Returns `true` if the dart is 0-free, 1-free, 2-free, **and** 3-free.
218    #[must_use = "unused return value"]
219    pub fn is_free(&self, dart_id: DartIdType) -> bool {
220        self.beta::<0>(dart_id) == NULL_DART_ID
221            && self.beta::<1>(dart_id) == NULL_DART_ID
222            && self.beta::<2>(dart_id) == NULL_DART_ID
223            && self.beta::<3>(dart_id) == NULL_DART_ID
224    }
225}
226
227/// **I-cell-related methods**
228impl<T: CoordsFloat> CMap3<T> {
229    /// Compute the ID of the vertex a given dart is part of.
230    ///
231    /// This corresponds to the minimum dart ID among darts composing the 0-cell orbit.
232    #[must_use = "unused return value"]
233    pub fn vertex_id(&self, dart_id: DartIdType) -> VertexIdType {
234        atomically(|t| self.vertex_id_transac(t, dart_id))
235    }
236
237    /// Compute the ID of the vertex a given dart is part of, transactionally.
238    ///
239    /// This corresponds to the minimum dart ID among darts composing the 0-cell orbit.
240    ///
241    /// # Errors
242    ///
243    /// This method is meant to be called in a context where the returned `Result` is used to
244    /// validate the transaction passed as argument. Errors should not be processed manually,
245    /// only processed via the `?` operator.
246    pub fn vertex_id_transac(
247        &self,
248        trans: &mut Transaction,
249        dart_id: DartIdType,
250    ) -> Result<VertexIdType, StmError> {
251        AUXILIARIES.with(|t| {
252            let (pending, marked) = &mut *t.borrow_mut();
253            // clear from previous computations
254            pending.clear();
255            marked.clear();
256            // initialize
257            pending.push_front(dart_id);
258            marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit
259
260            let mut min = dart_id;
261
262            while let Some(d) = pending.pop_front() {
263                if marked.insert(d) {
264                    min = min.min(d);
265                    let (b0, b2, b3) = (
266                        self.beta_transac::<0>(trans, d)?, // ?
267                        self.beta_transac::<2>(trans, d)?,
268                        self.beta_transac::<3>(trans, d)?,
269                    );
270                    pending.push_back(self.beta_transac::<1>(trans, b3)?);
271                    pending.push_back(self.beta_transac::<3>(trans, b2)?);
272                    pending.push_back(self.beta_transac::<1>(trans, b2)?);
273                    pending.push_back(self.beta_transac::<3>(trans, b0)?); // ?
274                    pending.push_back(self.beta_transac::<2>(trans, b0)?); // ?
275                }
276            }
277
278            Ok(min)
279        })
280    }
281
282    /// Compute the ID of the edge a given dart is part of.
283    ///
284    /// This corresponds to the minimum dart ID among darts composing the 1-cell orbit.
285    #[must_use = "unused return value"]
286    pub fn edge_id(&self, dart_id: DartIdType) -> EdgeIdType {
287        atomically(|t| self.edge_id_transac(t, dart_id))
288    }
289
290    /// Compute the ID of the edge a given dart is part of.
291    ///
292    /// This corresponds to the minimum dart ID among darts composing the 1-cell orbit.
293    ///
294    /// # Errors
295    ///
296    /// This method is meant to be called in a context where the returned `Result` is used to
297    /// validate the transaction passed as argument. Errors should not be processed manually,
298    /// only processed via the `?` operator.
299    pub fn edge_id_transac(
300        &self,
301        trans: &mut Transaction,
302        dart_id: DartIdType,
303    ) -> Result<EdgeIdType, StmError> {
304        AUXILIARIES.with(|t| {
305            let (pending, marked) = &mut *t.borrow_mut();
306            // clear from previous computations
307            pending.clear();
308            marked.clear();
309            // initialize
310            pending.push_back(dart_id);
311            marked.insert(NULL_DART_ID);
312
313            let mut min = dart_id;
314
315            while let Some(d) = pending.pop_front() {
316                if marked.insert(d) {
317                    min = min.min(d);
318                    for im in [
319                        self.beta_transac::<2>(trans, d)?,
320                        self.beta_transac::<3>(trans, d)?,
321                    ] {
322                        pending.push_back(im);
323                    }
324                }
325            }
326
327            Ok(min)
328        })
329    }
330
331    /// Compute the ID of the face a given dart is part of.
332    ///
333    /// This corresponds to the minimum dart ID among darts composing the 2-cell orbit.
334    #[must_use = "unused return value"]
335    pub fn face_id(&self, dart_id: DartIdType) -> FaceIdType {
336        atomically(|t| self.face_id_transac(t, dart_id))
337    }
338
339    /// Compute the ID of the face a given dart is part of.
340    ///
341    /// This corresponds to the minimum dart ID among darts composing the 2-cell orbit.
342    ///
343    /// # Errors
344    ///
345    /// This method is meant to be called in a context where the returned `Result` is used to
346    /// validate the transaction passed as argument. Errors should not be processed manually,
347    /// only processed via the `?` operator.
348    pub fn face_id_transac(
349        &self,
350        trans: &mut Transaction,
351        dart_id: DartIdType,
352    ) -> Result<FaceIdType, StmError> {
353        AUXILIARIES.with(|t| {
354            let (_pending, marked) = &mut *t.borrow_mut();
355            // clear from previous computations
356            marked.clear();
357            // initialize
358            marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit
359
360            let b3_dart_id = self.beta_transac::<3>(trans, dart_id)?;
361            let (mut lb, mut rb) = (dart_id, b3_dart_id);
362            let mut min = if rb == NULL_DART_ID { lb } else { lb.min(rb) };
363
364            while marked.insert(lb) || marked.insert(rb) {
365                (lb, rb) = (
366                    self.beta_transac::<1>(trans, lb)?,
367                    self.beta_transac::<0>(trans, rb)?,
368                );
369                if lb != NULL_DART_ID {
370                    min = min.min(lb);
371                }
372                if rb != NULL_DART_ID {
373                    min = min.min(rb);
374                }
375            }
376            // face is open, we need to iterate in the other direction
377            if lb == NULL_DART_ID || rb == NULL_DART_ID {
378                (lb, rb) = (
379                    self.beta_transac::<0>(trans, dart_id)?,
380                    self.beta_transac::<1>(trans, b3_dart_id)?,
381                );
382                while marked.insert(lb) || marked.insert(rb) {
383                    (lb, rb) = (
384                        self.beta_transac::<0>(trans, lb)?,
385                        self.beta_transac::<1>(trans, rb)?,
386                    );
387                    if lb != NULL_DART_ID {
388                        min = min.min(lb);
389                    }
390                    if rb != NULL_DART_ID {
391                        min = min.min(rb);
392                    }
393                }
394            }
395
396            Ok(min)
397        })
398    }
399
400    /// Compute the ID of the volume a given dart is part of.
401    ///
402    /// This corresponds to the minimum dart ID among darts composing the 3-cell orbit.
403    #[must_use = "unused return value"]
404    pub fn volume_id(&self, dart_id: DartIdType) -> VolumeIdType {
405        atomically(|t| self.volume_id_transac(t, dart_id))
406    }
407
408    /// Compute the ID of the volume a given dart is part of.
409    ///
410    /// This corresponds to the minimum dart ID among darts composing the 3-cell orbit.
411    ///
412    /// # Errors
413    ///
414    /// This method is meant to be called in a context where the returned `Result` is used to
415    /// validate the transaction passed as argument. Errors should not be processed manually,
416    /// only processed via the `?` operator.
417    pub fn volume_id_transac(
418        &self,
419        trans: &mut Transaction,
420        dart_id: DartIdType,
421    ) -> Result<VolumeIdType, StmError> {
422        AUXILIARIES.with(|t| {
423            let (pending, marked) = &mut *t.borrow_mut();
424            // clear from previous computations
425            pending.clear();
426            marked.clear();
427            // initialize
428            pending.push_front(dart_id);
429            marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit
430
431            let mut min = dart_id;
432
433            while let Some(d) = pending.pop_front() {
434                if marked.insert(d) {
435                    min = min.min(d);
436                    pending.push_back(self.beta_transac::<1>(trans, d)?);
437                    pending.push_back(self.beta_transac::<0>(trans, d)?); // ?
438                    pending.push_back(self.beta_transac::<2>(trans, d)?);
439                }
440            }
441
442            Ok(min)
443        })
444    }
445
446    /// Return an iterator over IDs of all the map's vertices.
447    pub fn iter_vertices(&self) -> impl Iterator<Item = VertexIdType> + '_ {
448        (1..self.n_darts() as DartIdType)
449            .zip(self.unused_darts.iter().skip(1))
450            .filter_map(
451                |(d, unused)| {
452                    if unused.read_atomic() { None } else { Some(d) }
453                },
454            )
455            .filter_map(|d| {
456                let vid = self.vertex_id(d);
457                if d == vid { Some(vid) } else { None }
458            })
459    }
460
461    /// Return an iterator over IDs of all the map's edges.
462    pub fn iter_edges(&self) -> impl Iterator<Item = EdgeIdType> + '_ {
463        (1..self.n_darts() as DartIdType)
464            .zip(self.unused_darts.iter().skip(1))
465            .filter_map(
466                |(d, unused)| {
467                    if unused.read_atomic() { None } else { Some(d) }
468                },
469            )
470            .filter_map(|d| {
471                let eid = self.edge_id(d);
472                if d == eid { Some(eid) } else { None }
473            })
474    }
475
476    /// Return an iterator over IDs of all the map's faces.
477    pub fn iter_faces(&self) -> impl Iterator<Item = FaceIdType> + '_ {
478        (1..self.n_darts() as DartIdType)
479            .zip(self.unused_darts.iter().skip(1))
480            .filter_map(
481                |(d, unused)| {
482                    if unused.read_atomic() { None } else { Some(d) }
483                },
484            )
485            .filter_map(|d| {
486                let fid = self.face_id(d);
487                if d == fid { Some(fid) } else { None }
488            })
489    }
490
491    /// Return an iterator over IDs of all the map's volumes.
492    pub fn iter_volumes(&self) -> impl Iterator<Item = VolumeIdType> + '_ {
493        (1..self.n_darts() as DartIdType)
494            .zip(self.unused_darts.iter().skip(1))
495            .filter_map(
496                |(d, unused)| {
497                    if unused.read_atomic() { None } else { Some(d) }
498                },
499            )
500            .filter_map(|d| {
501                let vid = self.volume_id(d);
502                if d == vid { Some(vid) } else { None }
503            })
504    }
505}