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}