1use std::{fmt::Display, cmp::min};
2
3use itertools::Itertools;
4
5use crate::{Edge, Link};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct Path {
9 edges:Vec<Edge>,
10 closed:bool
11}
12
13impl Path {
14 pub fn new<I>(edges: I, closed: bool) -> Self
15 where I: IntoIterator<Item = Edge> {
16 let edges = edges.into_iter().collect_vec();
17 assert!(!edges.is_empty());
18 Self { edges, closed }
19 }
20
21 pub fn arc<I>(edges: I) -> Self
22 where I: IntoIterator<Item = Edge> {
23 Self::new(edges, false)
24 }
25
26 pub fn circ<I>(edges: I) -> Self
27 where I: IntoIterator<Item = Edge> {
28 Self::new(edges, true)
29 }
30
31 pub fn contains(&self, e: Edge) -> bool {
32 self.edges.contains(&e)
33 }
34
35 pub fn len(&self) -> usize {
36 self.edges.len()
37 }
38
39 pub fn edges(&self) -> &Vec<Edge> {
40 &self.edges
41 }
42
43 pub fn min_edge(&self) -> Edge {
44 *self.edges.iter().min().unwrap()
45 }
46
47 pub fn ends(&self) -> Option<(Edge, Edge)> {
48 if self.is_arc() {
49 let &e0 = self.edges.first().unwrap();
50 let &e1 = self.edges.last().unwrap();
51 Some((e0, e1))
52 } else {
53 None
54 }
55 }
56
57 pub fn is_arc(&self) -> bool {
58 !self.closed
59 }
60
61 pub fn is_circle(&self) -> bool {
62 self.closed
63 }
64
65 pub fn reduce(&mut self) {
66 if self.is_arc() && self.len() > 2 {
67 let e0 = self.edges.remove(0);
68 let e1 = self.edges.pop().unwrap();
69 let min = self.edges().iter().filter(|&e| e < min(&e0, &e1)).min();
70
71 self.edges = if let Some(&e2) = min {
72 vec![e0, e2, e1]
73 } else {
74 vec![e0, e1]
75 };
76 } else if self.is_circle() && self.len() > 1 {
77 let e0 = *self.edges.iter().min().unwrap();
78 self.edges = vec![e0];
79 }
80 }
81
82 pub fn is_connectable(&self, other: &Self) -> bool {
83 let Some((e0, e1)) = self.ends() else { return false };
84 let Some((f0, f1)) = other.ends() else { return false };
85
86 e0 == f0 || e0 == f1 || e1 == f0 || e1 == f1
87 }
88
89 pub fn is_connectable_bothends(&self, other: &Self) -> bool {
90 let Some((e0, e1)) = self.ends() else { return false };
91 let Some((f0, f1)) = other.ends() else { return false };
92
93 (e0, e1) == (f0, f1) || (e0, e1) == (f1, f0)
94 }
95
96 pub fn connect(&mut self, other: Self) {
97 assert!(self.is_connectable(&other), "{self} and {other} is not connectable.");
98
99 let (e0, e1) = self.ends().unwrap();
100 let (f0, f1) = other.ends().unwrap();
101
102 let Path {mut edges, ..} = other.clone();
103
104 if e1 == f0 { edges.remove(0);
106 self.edges.append(&mut edges);
107 } else if e1 == f1 { edges.pop();
109 edges.reverse();
110 self.edges.append(&mut edges);
111 } else if e0 == f0 { edges.remove(0);
113 edges.reverse();
114 edges.append(&mut self.edges);
115 self.edges = edges;
116 } else if e0 == f1 { edges.pop();
118 edges.append(&mut self.edges);
119 self.edges = edges;
120 } else {
121 panic!() }
123
124 let (e0, e1) = self.ends().unwrap();
125
126 if e0 == e1 {
127 self.edges.pop();
128 self.closed = true;
129 }
130 }
131
132 pub fn is_adj(&self, other: &Path, link: &Link) -> bool {
133 for x in link.nodes() {
137 if !x.edges().iter().any(|e|
138 self.edges.contains(e)
139 ) {
140 continue
141 }
142
143 let Some(e) = x.edges().iter().find(|e|
144 !self.edges.contains(e)
145 ) else {
146 continue
147 };
148
149 if other.edges.contains(e) {
150 return true
151 }
152 }
153
154 false
155 }
156
157 fn edge_sum(&self) -> usize {
158 self.edges.iter().sum()
159 }
160
161 pub fn unori_eq(&self, other: &Self) -> bool {
162 if self.closed != other.closed ||
164 self.edges.len() != other.edges.len() ||
165 self.edge_sum() != other.edge_sum()
166 {
167 return false;
168 }
169
170 if self.edges == other.edges {
171 return true
172 }
173
174 if self.closed {
175 let n = self.edges.len();
176 let Some(p) = other.edges.iter().position(|e| e == &self.edges[0]) else {
177 return false
178 };
179 (0 .. n).all(|i| self.edges[i] == other.edges[(p + i) % n]) ||
180 (0 .. n).all(|i| self.edges[i] == other.edges[(p + n - i) % n])
181 } else {
182 Iterator::zip(
183 self.edges.iter(),
184 other.edges.iter().rev()
185 ).all(|(e, f)|
186 e == f
187 )
188 }
189 }
190}
191
192impl Display for Path {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 let c = self.edges.iter().map(|e| e.to_string()).join("-");
195 if self.is_circle() {
196 write!(f, "⚪︎({c})")
197 } else {
198 write!(f, "[{c}]")
199 }
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn reduce() {
209 let mut c = Path::new(vec![0], false);
210 c.reduce();
211 assert_eq!(c, Path::new(vec![0], false));
212
213 let mut c = Path::new(vec![0,1,2,3], false);
214 c.reduce();
215 assert_eq!(c, Path::new(vec![0, 3], false));
216
217 let mut c = Path::new(vec![0], true);
218 c.reduce();
219 assert_eq!(c, Path::new(vec![0], true));
220
221 let mut c = Path::new(vec![0,1,2,3], true);
222 c.reduce();
223 assert_eq!(c, Path::new(vec![0], true));
224 }
225
226 #[test]
227 fn is_connectable() {
228 let c = Path::new(vec![1,2,3,4], false);
229
230 assert!( c.is_connectable(&Path::new(vec![4,5], false)));
231 assert!( c.is_connectable(&Path::new(vec![5,4], false)));
232 assert!( c.is_connectable(&Path::new(vec![1,5], false)));
233 assert!( c.is_connectable(&Path::new(vec![5,1], false)));
234 assert!(!c.is_connectable(&Path::new(vec![5,6], false)));
235 assert!(!c.is_connectable(&Path::new(vec![2,3], false)));
236 assert!(!c.is_connectable(&Path::new(vec![0], true)));
237 }
238
239 #[test]
240 fn is_connectable_bothends() {
241 let c = Path::new(vec![1,2,3,4], false);
242
243 assert!(!c.is_connectable_bothends(&Path::new(vec![4,5], false)));
244 assert!(!c.is_connectable_bothends(&Path::new(vec![5,4], false)));
245 assert!(!c.is_connectable_bothends(&Path::new(vec![1,5], false)));
246 assert!(!c.is_connectable_bothends(&Path::new(vec![5,1], false)));
247 assert!( c.is_connectable_bothends(&Path::new(vec![1,4], false)));
248 assert!( c.is_connectable_bothends(&Path::new(vec![4,1], false)));
249 assert!(!c.is_connectable_bothends(&Path::new(vec![0], true)));
250 }
251
252 #[test]
253 fn connect() {
254 let mut c = Path::new(vec![1,2,3,4], false);
255 c.connect(Path::new(vec![4,5], false));
256 assert_eq!(c, Path::new(vec![1,2,3,4,5], false));
257
258 let mut c = Path::new(vec![1,2,3,4], false);
259 c.connect(Path::new(vec![5,4], false));
260 assert_eq!(c, Path::new(vec![1,2,3,4,5], false));
261
262 let mut c = Path::new(vec![1,2,3,4], false);
263 c.connect(Path::new(vec![6,1], false));
264 assert_eq!(c, Path::new(vec![6,1,2,3,4], false));
265
266 let mut c = Path::new(vec![1,2,3,4], false);
267 c.connect(Path::new(vec![1,6], false));
268 assert_eq!(c, Path::new(vec![6,1,2,3,4], false));
269
270 let mut c = Path::new(vec![1,2,3,4], false);
271 c.connect(Path::new(vec![1], false));
272 assert_eq!(c, Path::new(vec![1,2,3,4], false));
273
274 let mut c = Path::new(vec![1,2,3,4], false);
275 c.connect(Path::new(vec![4], false));
276 assert_eq!(c, Path::new(vec![1,2,3,4], false));
277 }
278
279 #[test]
280 fn unori_eq_arc() {
281 let c = Path::arc(vec![1,2,3]);
282
283 assert!(c.unori_eq(&Path::arc(vec![1,2,3])));
284 assert!(c.unori_eq(&Path::arc(vec![3,2,1])));
285
286 assert!(!c.unori_eq(&Path::arc(vec![1,2])));
287 assert!(!c.unori_eq(&Path::arc(vec![1,2,3,4])));
288 assert!(!c.unori_eq(&Path::circ(vec![1,2,3])));
289 }
290
291 #[test]
292 fn unori_eq_circ() {
293 let c = Path::circ(vec![1,2,3,4]);
294
295 assert!(c.unori_eq(&Path::circ(vec![1,2,3,4])));
296 assert!(c.unori_eq(&Path::circ(vec![2,3,4,1])));
297 assert!(c.unori_eq(&Path::circ(vec![3,4,1,2])));
298 assert!(c.unori_eq(&Path::circ(vec![2,3,4,1])));
299 assert!(c.unori_eq(&Path::circ(vec![4,3,2,1])));
300 assert!(c.unori_eq(&Path::circ(vec![3,2,1,4])));
301
302 assert!(!c.unori_eq(&Path::circ(vec![1,2,3])));
303 assert!(!c.unori_eq(&Path::circ(vec![1,2,3,4,5])));
304 assert!(!c.unori_eq(&Path::circ(vec![1,2,4,3])));
305 }
306}