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