Skip to main content

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::cell::RefCell;
11use std::collections::VecDeque;
12
13use rayon::prelude::*;
14use rustc_hash::FxHashSet as HashSet;
15
16use crate::cmap::{CMap2, DartIdType, EdgeIdType, FaceIdType, NULL_DART_ID, VertexIdType};
17use crate::geometry::CoordsFloat;
18use crate::stm::{StmClosureResult, 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::default()));
25}
26
27/// **Beta-related methods**
28impl<T: CoordsFloat> CMap2<T> {
29    // --- read
30
31    /// Return  β<sub>`I`</sub>(`dart_id`).
32    ///
33    /// # Panics
34    ///
35    /// The method will panic if `I` is not 0, 1 or 2.
36    #[must_use = "unused return value"]
37    pub fn beta<const I: u8>(&self, dart_id: DartIdType) -> DartIdType {
38        assert!(I < 3);
39        self.betas[(I, dart_id)].read_atomic()
40    }
41
42    /// Return  β<sub>`i`</sub>(`dart_id`).
43    ///
44    /// # Panics
45    ///
46    /// The method will panic if `i` is not 0, 1 or 2.
47    #[must_use = "unused return value"]
48    pub fn beta_rt(&self, i: u8, dart_id: DartIdType) -> DartIdType {
49        assert!(i < 3);
50        match i {
51            0 => self.beta::<0>(dart_id),
52            1 => self.beta::<1>(dart_id),
53            2 => self.beta::<2>(dart_id),
54            _ => unreachable!(),
55        }
56    }
57
58    /// Return  β<sub>`I`</sub>(`dart_id`).
59    ///
60    /// # Errors
61    ///
62    /// This method is meant to be called in a context where the returned `Result` is used to
63    /// validate the transaction passed as argument. Errors should not be processed manually,
64    /// only processed via the `?` operator.
65    ///
66    /// # Panics
67    ///
68    /// The method will panic if `I` is not 0, 1 or 2.
69    pub fn beta_tx<const I: u8>(
70        &self,
71        t: &mut Transaction,
72        dart_id: DartIdType,
73    ) -> StmClosureResult<DartIdType> {
74        assert!(I < 3);
75        self.betas[(I, dart_id)].read(t)
76    }
77
78    /// Return  β<sub>`i`</sub>(`dart_id`).
79    ///
80    /// # Errors
81    ///
82    /// This method is meant to be called in a context where the returned `Result` is used to
83    /// validate the transaction passed as argument. Errors should not be processed manually,
84    /// only processed via the `?` operator.
85    ///
86    /// # Panics
87    ///
88    /// The method will panic if `i` is not 0, 1 or 2.
89    pub fn beta_rt_tx(
90        &self,
91        t: &mut Transaction,
92        i: u8,
93        dart_id: DartIdType,
94    ) -> StmClosureResult<DartIdType> {
95        assert!(i < 3);
96        match i {
97            0 => self.beta_tx::<0>(t, dart_id),
98            1 => self.beta_tx::<1>(t, dart_id),
99            2 => self.beta_tx::<2>(t, dart_id),
100            _ => unreachable!(),
101        }
102    }
103
104    /// Check if a given dart is `I`-free.
105    ///
106    /// # Return
107    ///
108    /// Return a boolean indicating if the dart is `I`-free, i.e.:
109    /// - `true` if β<sub>`I`</sub>(`dart_id`) = `NULL_DART_ID`,
110    /// - `false` else.
111    ///
112    /// # Panics
113    ///
114    /// The function will panic if *I* is not 0, 1 or 2.
115    ///
116    #[must_use = "unused return value"]
117    pub fn is_i_free<const I: u8>(&self, dart_id: DartIdType) -> bool {
118        self.beta::<I>(dart_id) == NULL_DART_ID
119    }
120
121    /// Check if a given dart is `i`-free, for all `i`.
122    ///
123    /// # Return
124    ///
125    /// Return a boolean indicating if the dart is 0-free, 1-free **and** 2-free.
126    #[must_use = "unused return value"]
127    pub fn is_free(&self, dart_id: DartIdType) -> bool {
128        atomically(|t| self.is_free_tx(t, dart_id))
129    }
130
131    #[allow(clippy::missing_errors_doc)]
132    /// Check if a given dart is `i`-free, for all `i`.
133    ///
134    /// # Return / Errors
135    ///
136    /// Return a boolean indicating if the dart is 0-free, 1-free **and** 2-free.
137    ///
138    /// This method is meant to be called in a context where the returned `Result` is used to
139    /// validate the transaction passed as argument. Errors should not be processed manually,
140    /// only processed via the `?` operator.
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> CMap2<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.
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    ) -> StmClosureResult<VertexIdType> {
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_back(dart_id);
180            marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit
181            marked.insert(dart_id); // we're starting here, so we mark it beforehand
182
183            let mut min = dart_id;
184
185            while let Some(d) = pending.pop_front() {
186                // THIS CODE IS ONLY VALID IN 2D
187                let (b2d, b0d) = (self.beta_tx::<2>(t, d)?, self.beta_tx::<0>(t, d)?);
188                let image1 = self.beta_tx::<1>(t, b2d)?;
189                if marked.insert(image1) {
190                    // if true, we did not see this dart yet
191                    // i.e. we need to visit it later
192                    min = min.min(image1);
193                    pending.push_back(image1);
194                }
195                let image2 = self.beta_tx::<2>(t, b0d)?;
196                if marked.insert(image2) {
197                    // if true, we did not see this dart yet
198                    // i.e. we need to visit it later
199                    min = min.min(image2);
200                    pending.push_back(image2);
201                }
202            }
203
204            Ok(min)
205        })
206    }
207
208    /// Compute the ID of the edge a given dart is part of.
209    ///
210    /// This corresponds to the minimum dart ID among darts composing the 1-cell orbit.
211    #[must_use = "unused return value"]
212    pub fn edge_id(&self, dart_id: DartIdType) -> EdgeIdType {
213        atomically(|t| self.edge_id_tx(t, dart_id))
214    }
215
216    /// Compute the ID of the edge a given dart is part of.
217    ///
218    /// This corresponds to the minimum dart ID among darts composing the 1-cell orbit.
219    ///
220    /// # Errors
221    ///
222    /// This method is meant to be called in a context where the returned `Result` is used to
223    /// validate the transaction passed as argument. Errors should not be processed manually,
224    /// only processed via the `?` operator.
225    pub fn edge_id_tx(
226        &self,
227        t: &mut Transaction,
228        dart_id: DartIdType,
229    ) -> StmClosureResult<EdgeIdType> {
230        // optimizing this one bc I'm tired
231        let b2 = self.beta_tx::<2>(t, dart_id)?;
232        if b2 == NULL_DART_ID {
233            Ok(dart_id as EdgeIdType)
234        } else {
235            Ok(b2.min(dart_id) as EdgeIdType)
236        }
237    }
238
239    /// Compute the ID of the face a given dart is part of.
240    ///
241    /// This corresponds to the minimum dart ID among darts composing the 2-cell orbit.
242    #[must_use = "unused return value"]
243    pub fn face_id(&self, dart_id: DartIdType) -> FaceIdType {
244        atomically(|t| self.face_id_tx(t, dart_id))
245    }
246
247    /// Compute the ID of the face a given dart is part of.
248    ///
249    /// This corresponds to the minimum dart ID among darts composing the 2-cell orbit.
250    ///
251    /// # Errors
252    ///
253    /// This method is meant to be called in a context where the returned `Result` is used to
254    /// validate the transaction passed as argument. Errors should not be processed manually,
255    /// only processed via the `?` operator.
256    pub fn face_id_tx(
257        &self,
258        t: &mut Transaction,
259        dart_id: DartIdType,
260    ) -> StmClosureResult<FaceIdType> {
261        AUXILIARIES.with(|cell| {
262            let (pending, marked) = &mut *cell.borrow_mut();
263            // clear from previous computations
264            pending.clear();
265            marked.clear();
266            // initialize
267            pending.push_back(dart_id);
268            marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit
269            marked.insert(dart_id); // we're starting here, so we mark it beforehand
270
271            let mut min = dart_id;
272
273            while let Some(d) = pending.pop_front() {
274                // THIS CODE IS ONLY VALID IN 2D
275                let image1 = self.beta_tx::<1>(t, d)?;
276                if marked.insert(image1) {
277                    // if true, we did not see this dart yet
278                    // i.e. we need to visit it later
279                    min = min.min(image1);
280                    pending.push_back(image1);
281                }
282                let image2 = self.beta_tx::<0>(t, d)?;
283                if marked.insert(image2) {
284                    // if true, we did not see this dart yet
285                    // i.e. we need to visit it later
286                    min = min.min(image2);
287                    pending.push_back(image2);
288                }
289            }
290
291            Ok(min)
292        })
293    }
294
295    /// Return an iterator over IDs of all the map's vertices.
296    #[must_use = "unused return value"]
297    pub fn iter_vertices(&self) -> impl Iterator<Item = VertexIdType> + '_ {
298        (1..self.n_darts() as DartIdType)
299            .zip(self.unused_darts.iter().skip(1))
300            .filter_map(
301                |(d, unused)| {
302                    if unused.read_atomic() { None } else { Some(d) }
303                },
304            )
305            .filter_map(|d| {
306                let vid = self.vertex_id(d);
307                if d == vid { Some(vid) } else { None }
308            })
309    }
310
311    /// Return an iterator over IDs of all the map's edges.
312    #[must_use = "unused return value"]
313    pub fn iter_edges(&self) -> impl Iterator<Item = EdgeIdType> + '_ {
314        (1..self.n_darts() as DartIdType)
315            .zip(self.unused_darts.iter().skip(1))
316            .filter_map(
317                |(d, unused)| {
318                    if unused.read_atomic() { None } else { Some(d) }
319                },
320            )
321            .filter_map(|d| {
322                let eid = self.edge_id(d);
323                if d == eid { Some(eid) } else { None }
324            })
325    }
326
327    /// Return an iterator over IDs of all the map's faces.
328    #[must_use = "unused return value"]
329    pub fn iter_faces(&self) -> impl Iterator<Item = FaceIdType> + '_ {
330        (1..self.n_darts() as DartIdType)
331            .zip(self.unused_darts.iter().skip(1))
332            .filter_map(
333                |(d, unused)| {
334                    if unused.read_atomic() { None } else { Some(d) }
335                },
336            )
337            .filter_map(|d| {
338                let fid = self.face_id(d);
339                if d == fid { Some(fid) } else { None }
340            })
341    }
342
343    /// Return an iterator over IDs of all the map's vertices.
344    #[must_use = "unused return value"]
345    pub fn par_iter_vertices(&self) -> impl ParallelIterator<Item = VertexIdType> + '_ {
346        (1..self.n_darts() as DartIdType)
347            .into_par_iter()
348            .filter_map(|d| if self.is_unused(d) { None } else { Some(d) })
349            .filter_map(|d| {
350                let vid = self.vertex_id(d);
351                if d == vid { Some(vid) } else { None }
352            })
353    }
354
355    /// Return an iterator over IDs of all the map's edges.
356    #[must_use = "unused return value"]
357    pub fn par_iter_edges(&self) -> impl ParallelIterator<Item = EdgeIdType> + '_ {
358        (1..self.n_darts() as DartIdType)
359            .into_par_iter()
360            .filter_map(|d| if self.is_unused(d) { None } else { Some(d) })
361            .filter_map(|d| {
362                let eid = self.edge_id(d);
363                if d == eid { Some(eid) } else { None }
364            })
365    }
366
367    /// Return an iterator over IDs of all the map's faces.
368    #[must_use = "unused return value"]
369    pub fn par_iter_faces(&self) -> impl ParallelIterator<Item = FaceIdType> + '_ {
370        (1..self.n_darts() as DartIdType)
371            .into_par_iter()
372            .filter_map(|d| if self.is_unused(d) { None } else { Some(d) })
373            .filter_map(|d| {
374                let fid = self.face_id(d);
375                if d == fid { Some(fid) } else { None }
376            })
377    }
378}