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