Skip to main content

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