1use itertools::Itertools;
7
8pub type Pos = usize;
9pub type RingIdx = usize;
10
11crepe::crepe! {
12 @input
13 struct Position(Pos);
14
15 struct Neighbors(Pos, Pos);
16
17 @output
18 struct Grouped(Pos, Pos);
19
20 Neighbors(p1, p2) <- Position(p1), Position(p2), (are_neighbors(p1, p2));
21 Grouped(p1, p2) <- Neighbors(p1, p2);
22 Grouped(p1, p3) <- Neighbors(p1, p2), Grouped(p2, p3), (p1 != p3);
23}
24
25pub fn ring_offset(ring: RingIdx) -> Pos {
27 if ring == 0 {
28 0
29 } else {
30 3 * (ring - 1) * ring + 1
31 }
32}
33
34pub fn ring(pos: Pos) -> RingIdx {
36 (0..)
37 .map(ring_offset)
38 .position(|offset| pos < offset)
39 .unwrap()
40 - 1
41}
42
43pub fn is_at_ring_tip(pos: Pos) -> bool {
45 let ring = ring(pos);
46 let ring_offset = ring_offset(ring);
47
48 (0..6)
49 .map(|n| ring_offset + n * ring)
50 .any(|ring_tip| pos == ring_tip)
51}
52
53pub fn ring_edge_index(pos: Pos) -> usize {
55 let ring = ring(pos);
56 (pos - ring_offset(ring)) / ring
57}
58
59pub struct DirectionalNeighborIter {
62 curr_pos: Pos,
63 dir: usize,
64}
65
66impl DirectionalNeighborIter {
67 pub fn new(pos: Pos, dir: usize) -> Self {
70 assert!(dir <= 5);
71 Self { curr_pos: pos, dir }
72 }
73
74 pub fn curr_pos(&self) -> Pos {
76 self.curr_pos
77 }
78}
79
80impl Iterator for DirectionalNeighborIter {
81 type Item = Pos;
82
83 fn next(&mut self) -> Option<Self::Item> {
84 let next = neighboring_positions(self.curr_pos)[self.dir];
85 self.curr_pos = next;
86 Some(next)
87 }
88}
89
90fn ring_neighboring_positions(pos: Pos) -> [Pos; 2] {
91 assert!(pos != 0);
92
93 let ring = ring(pos);
94
95 if pos == ring_offset(ring) {
96 [ring_offset(ring + 1) - 1, pos + 1]
97 } else if pos == ring_offset(ring + 1) - 1 {
98 [pos - 1, ring_offset(ring)]
99 } else {
100 [pos - 1, pos + 1]
101 }
102}
103
104pub fn neighboring_positions(pos: Pos) -> [Pos; 6] {
106 let ring = ring(pos);
107
108 match ring {
109 0 => [1, 2, 3, 4, 5, 6],
110 _ => {
111 let edge_index = ring_edge_index(pos);
112
113 let mut poss = if is_at_ring_tip(pos) {
114 let lower_neighbor = ring_offset(ring - 1) + (ring - 1) * edge_index;
116 let ring_neighbors = ring_neighboring_positions(pos);
117 let upper_tip_neighbor = if pos == ring_offset(ring + 1) - 1 {
118 ring_offset(ring + 2) - 2
119 } else {
120 ring_offset(ring + 1) + (ring + 1) * edge_index
121 };
122 let upper_tip_neighbors = ring_neighboring_positions(upper_tip_neighbor);
123
124 [
125 upper_tip_neighbor,
126 upper_tip_neighbors[1],
127 ring_neighbors[1],
128 lower_neighbor,
129 ring_neighbors[0],
130 upper_tip_neighbors[0],
131 ]
132 } else {
133 let ring_pos = pos - ring_offset(ring);
135 let tip_offset = ring_pos - (edge_index * ring);
136 let (lower_neighbor1, lower_neighbor2) = if pos == ring_offset(ring + 1) - 1 {
137 (ring_offset(ring) - 1, ring_offset(ring - 1))
138 } else {
139 let lower_neighbor1 =
140 ring_offset(ring - 1) + (edge_index * (ring - 1)) + tip_offset - 1;
141 (lower_neighbor1, lower_neighbor1 + 1)
142 };
143 let ring_neighbors = ring_neighboring_positions(pos);
144 let upper_neighbor1 =
145 ring_offset(ring + 1) + (edge_index * (ring + 1)) + tip_offset;
146 let upper_neighbor2 = upper_neighbor1 + 1;
147
148 [
149 upper_neighbor1,
150 upper_neighbor2,
151 ring_neighbors[1],
152 lower_neighbor2,
153 lower_neighbor1,
154 ring_neighbors[0],
155 ]
156 };
157
158 poss.rotate_right(edge_index);
159
160 poss
161 }
162 }
163}
164
165pub fn are_neighbors(pos1: Pos, pos2: Pos) -> bool {
167 neighboring_positions(pos1).contains(&pos2)
168}
169
170pub fn is_path_consistent(poss: &[Pos]) -> bool {
172 assert!(poss.len() >= 2);
173
174 poss.windows(2).all(|pair| {
175 if let &[p1, p2] = pair {
176 are_neighbors(p1, p2)
177 } else {
178 unreachable!();
179 }
180 })
181}
182
183pub fn are_grouped(poss: &[Pos]) -> bool {
184 let mut rt = Crepe::new();
185 rt.extend(poss.iter().copied().map(Position));
186 let (groups,) = rt.run();
187
188 poss.iter()
189 .permutations(2)
190 .all(|pair| groups.contains(&Grouped(*pair[0], *pair[1])))
191}
192
193#[cfg(test)]
194mod tests {
195 use itertools::Itertools;
196
197 use super::*;
198
199 #[test]
200 fn ring_offsets() {
201 let offsets = [0, 1, 7, 19, 37, 61, 91];
202
203 for (i, offset) in offsets.into_iter().enumerate() {
204 assert_eq!(ring_offset(i), offset);
205 }
206 }
207
208 #[test]
209 fn position_rings() {
210 let offsets = [0, 1, 7, 19, 37, 61, 91];
211
212 for (i, window) in offsets.windows(2).enumerate() {
213 if let [beg, end] = window {
214 for pos in *beg..*end {
215 assert_eq!(ring(pos), i);
216 }
217 }
218 }
219 }
220
221 #[test]
222 fn ring_tips() {
223 for pos in 1..=6 {
224 assert!(is_at_ring_tip(pos), "{}", pos);
225 }
226
227 for pos in [7, 9, 11, 13, 15, 17] {
228 assert!(is_at_ring_tip(pos), "{}", pos);
229 }
230
231 for pos in [61, 66, 71, 76, 81, 86] {
232 assert!(is_at_ring_tip(pos), "{}", pos);
233 }
234 }
235
236 #[test]
237 fn ring_edges() {
238 for pos in [8, 10, 12, 14, 16, 18] {
239 assert!(!is_at_ring_tip(pos), "{}", pos);
240 }
241
242 for pos in (62..66)
243 .chain(67..71)
244 .chain(72..76)
245 .chain(77..81)
246 .chain(82..86)
247 {
248 assert!(!is_at_ring_tip(pos), "{}", pos);
249 }
250 }
251
252 #[test]
253 fn edge_indices_non_tips() {
254 for pos in [8, 21].into_iter().chain(38..=40).chain(62..=65) {
255 assert_eq!(ring_edge_index(pos), 0);
256 }
257 for pos in [10, 23, 24].into_iter().chain(42..=44).chain(67..=70) {
258 assert_eq!(ring_edge_index(pos), 1);
259 }
260 for pos in [12, 26, 27].into_iter().chain(46..=48).chain(72..=75) {
261 assert_eq!(ring_edge_index(pos), 2);
262 }
263 for pos in [14, 29, 30].into_iter().chain(50..=52).chain(77..=80) {
264 assert_eq!(ring_edge_index(pos), 3);
265 }
266 for pos in [16, 32, 33].into_iter().chain(54..=56).chain(82..=85) {
267 assert_eq!(ring_edge_index(pos), 4);
268 }
269 for pos in [18, 35, 36].into_iter().chain(58..=60).chain(87..=90) {
270 assert_eq!(ring_edge_index(pos), 5);
271 }
272 }
273
274 #[test]
275 fn ring_neighbors() {
276 assert_eq!(ring_neighboring_positions(1), [6, 2]);
277 assert_eq!(ring_neighboring_positions(2), [1, 3]);
278 assert_eq!(ring_neighboring_positions(3), [2, 4]);
279 assert_eq!(ring_neighboring_positions(4), [3, 5]);
280 assert_eq!(ring_neighboring_positions(5), [4, 6]);
281 assert_eq!(ring_neighboring_positions(6), [5, 1]);
282 assert_eq!(ring_neighboring_positions(18), [17, 7]);
283 assert_eq!(ring_neighboring_positions(58), [57, 59]);
284 }
285
286 #[test]
287 fn ring_tip_neighbors() {
288 assert_eq!(neighboring_positions(1), [7, 8, 2, 0, 6, 18]);
289 assert_eq!(neighboring_positions(2), [8, 9, 10, 3, 0, 1]);
290 assert_eq!(neighboring_positions(3), [2, 10, 11, 12, 4, 0]);
291 assert_eq!(neighboring_positions(4), [0, 3, 12, 13, 14, 5]);
292 assert_eq!(neighboring_positions(5), [6, 0, 4, 14, 15, 16]);
293 assert_eq!(neighboring_positions(6), [18, 1, 0, 5, 16, 17]);
294 assert_eq!(neighboring_positions(7), [19, 20, 8, 1, 18, 36]);
295 assert_eq!(neighboring_positions(9), [21, 22, 23, 10, 2, 8]);
296 assert_eq!(neighboring_positions(11), [10, 24, 25, 26, 12, 3]);
297 assert_eq!(neighboring_positions(13), [4, 12, 27, 28, 29, 14]);
298 assert_eq!(neighboring_positions(15), [16, 5, 14, 30, 31, 32]);
299 assert_eq!(neighboring_positions(17), [35, 18, 6, 16, 33, 34]);
300 assert_eq!(neighboring_positions(28), [13, 27, 48, 49, 50, 29]);
301 assert_eq!(neighboring_positions(53), [54, 31, 52, 80, 81, 82]);
302 assert_eq!(neighboring_positions(57), [87, 58, 34, 56, 85, 86]);
303 }
304
305 #[test]
306 fn ring_edge_neighbors() {
307 assert_eq!(neighboring_positions(8), [20, 21, 9, 2, 1, 7]);
308 assert_eq!(neighboring_positions(10), [9, 23, 24, 11, 3, 2]);
309 assert_eq!(neighboring_positions(12), [3, 11, 26, 27, 13, 4]);
310 assert_eq!(neighboring_positions(14), [5, 4, 13, 29, 30, 15]);
311 assert_eq!(neighboring_positions(16), [17, 6, 5, 15, 32, 33]);
312 assert_eq!(neighboring_positions(18), [36, 7, 1, 6, 17, 35]);
313 assert_eq!(neighboring_positions(38), [62, 63, 39, 20, 19, 37]);
314 assert_eq!(neighboring_positions(40), [64, 65, 41, 22, 21, 39]);
315 assert_eq!(neighboring_positions(42), [41, 67, 68, 43, 23, 22]);
316 assert_eq!(neighboring_positions(44), [43, 69, 70, 45, 25, 24]);
317 assert_eq!(neighboring_positions(46), [25, 45, 72, 73, 47, 26]);
318 assert_eq!(neighboring_positions(48), [27, 47, 74, 75, 49, 28]);
319 assert_eq!(neighboring_positions(50), [29, 28, 49, 77, 78, 51]);
320 assert_eq!(neighboring_positions(52), [31, 30, 51, 79, 80, 53]);
321 assert_eq!(neighboring_positions(54), [55, 32, 31, 53, 82, 83]);
322 assert_eq!(neighboring_positions(56), [57, 34, 33, 55, 84, 85]);
323 assert_eq!(neighboring_positions(58), [88, 59, 35, 34, 57, 87]);
324 assert_eq!(neighboring_positions(60), [90, 37, 19, 36, 59, 89]);
325 }
326
327 #[test]
328 fn groups() {
329 assert!([2, 8, 9]
330 .into_iter()
331 .permutations(3)
332 .all(|perm| are_grouped(&perm)));
333 assert!([1, 0, 4]
334 .into_iter()
335 .permutations(3)
336 .all(|perm| are_grouped(&perm)));
337 assert!([71, 45, 25, 24, 23, 22, 41, 66]
338 .into_iter()
339 .permutations(8)
340 .all(|perm| are_grouped(&perm)));
341 assert!([0, 1, 2, 3, 4, 5, 6]
342 .into_iter()
343 .permutations(7)
344 .all(|perm| are_grouped(&perm)));
345 assert!([5, 17, 18]
346 .into_iter()
347 .permutations(3)
348 .all(|perm| !are_grouped(&perm)));
349 assert!([2, 3, 5, 6]
350 .into_iter()
351 .permutations(4)
352 .all(|perm| !are_grouped(&perm)));
353 assert!([1, 4]
354 .into_iter()
355 .permutations(2)
356 .all(|perm| !are_grouped(&perm)));
357
358 assert!(are_grouped(&[11, 10, 2, 1, 6, 5, 15, 30, 29, 28, 27, 26]));
359 assert!(!are_grouped(&[
360 1, 2, 3, 4, 5, 16, 17, 35, 36, 20, 21, 22, 23, 24, 25, 26
361 ]));
362 assert!(are_grouped(&[
363 1, 2, 3, 4, 5, 16, 17, 35, 36, 19, 20, 21, 22, 23, 24, 25, 26
364 ]));
365 }
366
367 #[test]
368 fn directional_neighbor_iter() {
369 use DirectionalNeighborIter as DNI;
370
371 assert_eq!(
372 DNI::new(75, 0).take(9).collect::<Vec<_>>(),
373 vec![48, 27, 12, 3, 2, 8, 20, 38, 62]
374 );
375 assert_eq!(
376 DNI::new(76, 0).take(10).collect::<Vec<_>>(),
377 vec![49, 28, 13, 4, 0, 1, 7, 19, 37, 61]
378 );
379 assert_eq!(
380 DNI::new(77, 0).take(9).collect::<Vec<_>>(),
381 vec![50, 29, 14, 5, 6, 18, 36, 60, 90]
382 );
383
384 assert_eq!(
385 DNI::new(80, 1).take(9).collect::<Vec<_>>(),
386 vec![52, 30, 14, 4, 3, 10, 23, 42, 67]
387 );
388 assert_eq!(
389 DNI::new(81, 1).take(10).collect::<Vec<_>>(),
390 vec![53, 31, 15, 5, 0, 2, 9, 22, 41, 66]
391 );
392 assert_eq!(
393 DNI::new(82, 1).take(9).collect::<Vec<_>>(),
394 vec![54, 32, 16, 6, 1, 8, 21, 40, 65]
395 );
396
397 assert_eq!(
398 DNI::new(85, 2).take(9).collect::<Vec<_>>(),
399 vec![56, 33, 16, 5, 4, 12, 26, 46, 72]
400 );
401 assert_eq!(
402 DNI::new(86, 2).take(10).collect::<Vec<_>>(),
403 vec![57, 34, 17, 6, 0, 3, 11, 25, 45, 71]
404 );
405 assert_eq!(
406 DNI::new(87, 2).take(9).collect::<Vec<_>>(),
407 vec![58, 35, 18, 1, 2, 10, 24, 44, 70]
408 );
409 }
410}