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        assert!(self.is_free(dart_id)); // all beta images are 0
134        assert!(!atomically(|t| self.remove_free_dart_transac(t, dart_id)));
135    }
136
137    #[allow(clippy::missing_errors_doc)]
138    /// Transactionally remove a free dart from the map.
139    ///
140    /// The removed dart identifier is added to the list of free dart. This way of proceeding is
141    /// necessary as the structure relies on darts indexing for encoding data, making reordering of
142    /// any sort extremely costly.
143    ///
144    /// # Arguments
145    ///
146    /// - `dart_id: DartIdentifier` -- Identifier of the dart to remove.
147    ///
148    /// # Return / Errors
149    ///
150    /// This method return a boolean indicating whether the art was already unused or not.
151    ///
152    /// This method is meant to be called in a context where the returned `Result` is used to
153    /// validate the transaction passed as argument. Errors should not be processed manually,
154    /// only processed via the `?` operator.
155    pub fn remove_free_dart_transac(
156        &self,
157        t: &mut Transaction,
158        dart_id: DartIdType,
159    ) -> StmClosureResult<bool> {
160        self.unused_darts[dart_id].replace(t, true)
161    }
162}
163
164/// **Beta-related methods**
165impl<T: CoordsFloat> CMap2<T> {
166    // --- read
167
168    /// Return  β<sub>`I`</sub>(`dart_id`).
169    ///
170    /// # Panics
171    ///
172    /// The method will panic if `I` is not 0, 1 or 2.
173    #[must_use = "unused return value"]
174    pub fn beta<const I: u8>(&self, dart_id: DartIdType) -> DartIdType {
175        assert!(I < 3);
176        self.betas[(I, dart_id)].read_atomic()
177    }
178
179    /// Return  β<sub>`i`</sub>(`dart_id`).
180    ///
181    /// # Panics
182    ///
183    /// The method will panic if `i` is not 0, 1 or 2.
184    #[must_use = "unused return value"]
185    pub fn beta_rt(&self, i: u8, dart_id: DartIdType) -> DartIdType {
186        assert!(i < 3);
187        match i {
188            0 => self.beta::<0>(dart_id),
189            1 => self.beta::<1>(dart_id),
190            2 => self.beta::<2>(dart_id),
191            _ => unreachable!(),
192        }
193    }
194
195    /// Return  β<sub>`I`</sub>(`dart_id`).
196    ///
197    /// # Errors
198    ///
199    /// This method is meant to be called in a context where the returned `Result` is used to
200    /// validate the transaction passed as argument. Errors should not be processed manually,
201    /// only processed via the `?` operator.
202    ///
203    /// # Panics
204    ///
205    /// The method will panic if `I` is not 0, 1 or 2.
206    pub fn beta_transac<const I: u8>(
207        &self,
208        trans: &mut Transaction,
209        dart_id: DartIdType,
210    ) -> StmClosureResult<DartIdType> {
211        assert!(I < 3);
212        self.betas[(I, dart_id)].read(trans)
213    }
214
215    /// Return  β<sub>`i`</sub>(`dart_id`).
216    ///
217    /// # Errors
218    ///
219    /// This method is meant to be called in a context where the returned `Result` is used to
220    /// validate the transaction passed as argument. Errors should not be processed manually,
221    /// only processed via the `?` operator.
222    ///
223    /// # Panics
224    ///
225    /// The method will panic if `i` is not 0, 1 or 2.
226    pub fn beta_rt_transac(
227        &self,
228        trans: &mut Transaction,
229        i: u8,
230        dart_id: DartIdType,
231    ) -> StmClosureResult<DartIdType> {
232        assert!(i < 3);
233        match i {
234            0 => self.beta_transac::<0>(trans, dart_id),
235            1 => self.beta_transac::<1>(trans, dart_id),
236            2 => self.beta_transac::<2>(trans, dart_id),
237            _ => unreachable!(),
238        }
239    }
240
241    /// Check if a given dart is `I`-free.
242    ///
243    /// # Return
244    ///
245    /// Return a boolean indicating if the dart is `I`-free, i.e.:
246    /// - `true` if β<sub>`I`</sub>(`dart_id`) = `NULL_DART_ID`,
247    /// - `false` else.
248    ///
249    /// # Panics
250    ///
251    /// The function will panic if *I* is not 0, 1 or 2.
252    ///
253    #[must_use = "unused return value"]
254    pub fn is_i_free<const I: u8>(&self, dart_id: DartIdType) -> bool {
255        self.beta::<I>(dart_id) == NULL_DART_ID
256    }
257
258    /// Check if a given dart is `i`-free, for all `i`.
259    ///
260    /// # Return
261    ///
262    /// Return a boolean indicating if the dart is 0-free, 1-free **and** 2-free.
263    #[must_use = "unused return value"]
264    pub fn is_free(&self, dart_id: DartIdType) -> bool {
265        self.beta::<0>(dart_id) == NULL_DART_ID
266            && self.beta::<1>(dart_id) == NULL_DART_ID
267            && self.beta::<2>(dart_id) == NULL_DART_ID
268    }
269}
270
271/// **I-cell-related methods**
272impl<T: CoordsFloat> CMap2<T> {
273    /// Compute the ID of the vertex a given dart is part of.
274    ///
275    /// This corresponds to the minimum dart ID among darts composing the 0-cell orbit.
276    #[must_use = "unused return value"]
277    pub fn vertex_id(&self, dart_id: DartIdType) -> VertexIdType {
278        atomically(|trans| self.vertex_id_transac(trans, dart_id))
279    }
280
281    /// Compute the ID of the vertex a given dart is part of.
282    ///
283    /// This corresponds to the minimum dart ID among darts composing the 0-cell orbit.
284    ///
285    /// # Errors
286    ///
287    /// This method is meant to be called in a context where the returned `Result` is used to
288    /// validate the transaction passed as argument. Errors should not be processed manually,
289    /// only processed via the `?` operator.
290    pub fn vertex_id_transac(
291        &self,
292        trans: &mut Transaction,
293        dart_id: DartIdType,
294    ) -> StmClosureResult<VertexIdType> {
295        AUXILIARIES.with(|t| {
296            let (pending, marked) = &mut *t.borrow_mut();
297            // clear from previous computations
298            pending.clear();
299            marked.clear();
300            // initialize
301            pending.push_back(dart_id);
302            marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit
303            marked.insert(dart_id); // we're starting here, so we mark it beforehand
304
305            let mut min = dart_id;
306
307            while let Some(d) = pending.pop_front() {
308                // THIS CODE IS ONLY VALID IN 2D
309                let (b2d, b0d) = (
310                    self.beta_transac::<2>(trans, d)?,
311                    self.beta_transac::<0>(trans, d)?,
312                );
313                let image1 = self.beta_transac::<1>(trans, b2d)?;
314                if marked.insert(image1) {
315                    // if true, we did not see this dart yet
316                    // i.e. we need to visit it later
317                    min = min.min(image1);
318                    pending.push_back(image1);
319                }
320                let image2 = self.beta_transac::<2>(trans, b0d)?;
321                if marked.insert(image2) {
322                    // if true, we did not see this dart yet
323                    // i.e. we need to visit it later
324                    min = min.min(image2);
325                    pending.push_back(image2);
326                }
327            }
328
329            Ok(min)
330        })
331    }
332
333    /// Compute the ID of the edge a given dart is part of.
334    ///
335    /// This corresponds to the minimum dart ID among darts composing the 1-cell orbit.
336    #[must_use = "unused return value"]
337    pub fn edge_id(&self, dart_id: DartIdType) -> EdgeIdType {
338        atomically(|trans| self.edge_id_transac(trans, dart_id))
339    }
340
341    /// Compute the ID of the edge a given dart is part of.
342    ///
343    /// This corresponds to the minimum dart ID among darts composing the 1-cell orbit.
344    ///
345    /// # Errors
346    ///
347    /// This method is meant to be called in a context where the returned `Result` is used to
348    /// validate the transaction passed as argument. Errors should not be processed manually,
349    /// only processed via the `?` operator.
350    pub fn edge_id_transac(
351        &self,
352        trans: &mut Transaction,
353        dart_id: DartIdType,
354    ) -> StmClosureResult<EdgeIdType> {
355        // optimizing this one bc I'm tired
356        let b2 = self.beta_transac::<2>(trans, dart_id)?;
357        if b2 == NULL_DART_ID {
358            Ok(dart_id as EdgeIdType)
359        } else {
360            Ok(b2.min(dart_id) as EdgeIdType)
361        }
362    }
363
364    /// Compute the ID of the face a given dart is part of.
365    ///
366    /// This corresponds to the minimum dart ID among darts composing the 2-cell orbit.
367    #[must_use = "unused return value"]
368    pub fn face_id(&self, dart_id: DartIdType) -> FaceIdType {
369        atomically(|trans| self.face_id_transac(trans, dart_id))
370    }
371
372    /// Compute the ID of the face a given dart is part of.
373    ///
374    /// This corresponds to the minimum dart ID among darts composing the 2-cell orbit.
375    ///
376    /// # Errors
377    ///
378    /// This method is meant to be called in a context where the returned `Result` is used to
379    /// validate the transaction passed as argument. Errors should not be processed manually,
380    /// only processed via the `?` operator.
381    pub fn face_id_transac(
382        &self,
383        trans: &mut Transaction,
384        dart_id: DartIdType,
385    ) -> StmClosureResult<FaceIdType> {
386        AUXILIARIES.with(|t| {
387            let (pending, marked) = &mut *t.borrow_mut();
388            // clear from previous computations
389            pending.clear();
390            marked.clear();
391            // initialize
392            pending.push_back(dart_id);
393            marked.insert(NULL_DART_ID); // we don't want to include the null dart in the orbit
394            marked.insert(dart_id); // we're starting here, so we mark it beforehand
395
396            let mut min = dart_id;
397
398            while let Some(d) = pending.pop_front() {
399                // THIS CODE IS ONLY VALID IN 2D
400                let image1 = self.beta_transac::<1>(trans, d)?;
401                if marked.insert(image1) {
402                    // if true, we did not see this dart yet
403                    // i.e. we need to visit it later
404                    min = min.min(image1);
405                    pending.push_back(image1);
406                }
407                let image2 = self.beta_transac::<0>(trans, d)?;
408                if marked.insert(image2) {
409                    // if true, we did not see this dart yet
410                    // i.e. we need to visit it later
411                    min = min.min(image2);
412                    pending.push_back(image2);
413                }
414            }
415
416            Ok(min)
417        })
418    }
419
420    /// Return an iterator over IDs of all the map's vertices.
421    #[must_use = "unused return value"]
422    pub fn iter_vertices(&self) -> impl Iterator<Item = VertexIdType> + '_ {
423        (1..self.n_darts() as DartIdType)
424            .zip(self.unused_darts.iter().skip(1))
425            .filter_map(
426                |(d, unused)| {
427                    if unused.read_atomic() { None } else { 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    #[must_use = "unused return value"]
438    pub fn iter_edges(&self) -> impl Iterator<Item = EdgeIdType> + '_ {
439        (1..self.n_darts() as DartIdType)
440            .zip(self.unused_darts.iter().skip(1))
441            .filter_map(
442                |(d, unused)| {
443                    if unused.read_atomic() { None } else { Some(d) }
444                },
445            )
446            .filter_map(|d| {
447                let eid = self.edge_id(d);
448                if d == eid { Some(eid) } else { None }
449            })
450    }
451
452    /// Return an iterator over IDs of all the map's faces.
453    #[must_use = "unused return value"]
454    pub fn iter_faces(&self) -> impl Iterator<Item = FaceIdType> + '_ {
455        (1..self.n_darts() as DartIdType)
456            .zip(self.unused_darts.iter().skip(1))
457            .filter_map(
458                |(d, unused)| {
459                    if unused.read_atomic() { None } else { Some(d) }
460                },
461            )
462            .filter_map(|d| {
463                let fid = self.face_id(d);
464                if d == fid { Some(fid) } else { None }
465            })
466    }
467}