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