1use std::ops::Range;
8use std::{collections::HashSet, hash::Hash};
9
10use derive_more::{Constructor, From};
11use itertools::{Either, Itertools};
12use nonempty::{NonEmpty, nonempty};
13
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Serialize};
16#[cfg(feature = "serde-wasm")]
17use tsify::Tsify;
18
19use super::graph::{Graph, ReflexiveGraph};
20use crate::validate;
21use crate::zero::QualifiedName;
22
23#[derive(Clone, Debug, PartialEq, Eq, Hash)]
42pub enum Path<V, E> {
43 Id(V),
45
46 Seq(NonEmpty<E>),
48}
49
50pub type QualifiedPath = Path<QualifiedName, QualifiedName>;
52
53pub type SkelPath = Path<usize, usize>;
55
56impl<V, E> From<E> for Path<V, E> {
58 fn from(e: E) -> Self {
59 Path::single(e)
60 }
61}
62
63impl<V, E> IntoIterator for Path<V, E> {
65 type Item = E;
66 type IntoIter = Either<std::iter::Empty<E>, <NonEmpty<E> as IntoIterator>::IntoIter>;
67
68 fn into_iter(self) -> Self::IntoIter {
69 match self {
70 Path::Id(_) => Either::Left(std::iter::empty()),
71 Path::Seq(edges) => Either::Right(edges.into_iter()),
72 }
73 }
74}
75
76impl<V, E> Path<V, E> {
77 pub fn empty(v: V) -> Self {
79 Path::Id(v)
80 }
81
82 pub fn single(e: E) -> Self {
84 Path::Seq(NonEmpty::singleton(e))
85 }
86
87 pub fn pair(e: E, f: E) -> Self {
89 Path::Seq(nonempty![e, f])
90 }
91
92 pub fn collect<I>(iter: I) -> Option<Self>
96 where
97 I: IntoIterator<Item = E>,
98 {
99 NonEmpty::collect(iter).map(Path::Seq)
100 }
101
102 pub fn from_vec(vec: Vec<E>) -> Option<Self> {
106 NonEmpty::from_vec(vec).map(Path::Seq)
107 }
108
109 pub fn repeat_n(v: V, e: E, n: usize) -> Self
113 where
114 E: Clone,
115 {
116 Path::collect(std::iter::repeat_n(e, n)).unwrap_or_else(|| Path::empty(v))
117 }
118
119 pub fn len(&self) -> usize {
121 match self {
122 Path::Id(_) => 0,
123 Path::Seq(edges) => edges.len(),
124 }
125 }
126
127 pub fn is_empty(&self) -> bool {
129 match self {
130 Path::Id(_) => true,
131 Path::Seq(_) => false,
132 }
133 }
134
135 pub fn iter(&self) -> impl Iterator<Item = &E> {
139 match self {
140 Path::Id(_) => Either::Left(std::iter::empty()),
141 Path::Seq(edges) => Either::Right(edges.iter()),
142 }
143 }
144
145 pub fn only(self) -> Option<E> {
149 match self {
150 Path::Id(_) => None,
151 Path::Seq(edges) => {
152 if edges.tail.is_empty() {
153 Some(edges.head)
154 } else {
155 None
156 }
157 }
158 }
159 }
160
161 pub fn insert(&mut self, index: usize, edge: E) {
163 if let Path::Seq(edges) = self {
164 edges.insert(index, edge);
165 } else {
166 *self = Path::single(edge);
167 }
168 }
169
170 pub fn splice(self, range: Range<usize>, replace_with: Self) -> Self {
172 let new_path = if range.start == 0 && range.end == self.len() {
173 Some(replace_with)
174 } else if let Path::Seq(edges) = self {
175 let mut edges: Vec<_> = edges.into();
176 edges.splice(range, replace_with);
177 Path::from_vec(edges)
178 } else {
179 None
180 };
181 new_path.expect("Range of indices into path should be valid")
182 }
183
184 pub fn src(&self, graph: &impl Graph<V = V, E = E>) -> V
188 where
189 V: Clone,
190 {
191 match self {
192 Path::Id(v) => v.clone(),
193 Path::Seq(edges) => graph.src(edges.first()),
194 }
195 }
196
197 pub fn tgt(&self, graph: &impl Graph<V = V, E = E>) -> V
201 where
202 V: Clone,
203 {
204 match self {
205 Path::Id(v) => v.clone(),
206 Path::Seq(edges) => graph.tgt(edges.last()),
207 }
208 }
209
210 pub fn subpath(&self, graph: &impl Graph<V = V, E = E>, range: Range<usize>) -> Self
214 where
215 V: Eq + Clone,
216 E: Clone,
217 {
218 if let Path::Seq(edges) = self {
219 if range.is_empty() {
220 let index = range.start;
221 let v = if index == 0 {
222 graph.src(edges.first())
223 } else if index == edges.len() {
224 graph.tgt(edges.last())
225 } else if index < edges.len() {
226 let (t, s) = (graph.tgt(&(*edges)[index - 1]), graph.src(&(*edges)[index]));
227 assert!(t == s, "Inconsistent intermediate vertex in path");
228 t
229 } else {
230 panic!("Invalid index for empty subpath of path");
231 };
232 Path::Id(v)
233 } else {
234 let (start, end) = (range.start, range.end);
235 let iter = if start == 0 {
236 let head = std::iter::once(edges.head.clone());
237 let tail = edges.tail[0..(end - 1)].iter().cloned();
238 Either::Left(head.chain(tail))
239 } else {
240 Either::Right(edges.tail[(start - 1)..(end - 1)].iter().cloned())
241 };
242 Path::collect(iter).unwrap()
243 }
244 } else {
245 assert!(range.start == 0 && range.is_empty(), "Invalid subpath of empty path");
246 self.clone()
247 }
248 }
249
250 pub fn replace_subpath(
254 self,
255 graph: &impl Graph<V = V, E = E>,
256 range: Range<usize>,
257 f: impl FnOnce(Self) -> Self,
258 ) -> Self
259 where
260 V: Eq + Clone,
261 E: Clone,
262 {
263 let subpath = self.subpath(graph, range.clone());
264 self.splice(range, f(subpath))
265 }
266
267 pub fn concat_in(self, graph: &impl Graph<V = V, E = E>, other: Self) -> Option<Self>
275 where
276 V: Eq + Clone,
277 {
278 if self.tgt(graph) != other.src(graph) {
279 return None;
280 }
281 let concatenated = match (self, other) {
282 (path, Path::Id(_)) => path,
283 (Path::Id(_), path) => path,
284 (Path::Seq(mut edges), Path::Seq(mut other_edges)) => {
285 edges.push(other_edges.head);
286 edges.append(&mut other_edges.tail);
287 Path::Seq(edges)
288 }
289 };
290 Some(concatenated)
291 }
292
293 pub fn contained_in(&self, graph: &impl Graph<V = V, E = E>) -> bool
295 where
296 V: Eq,
297 {
298 match self {
299 Path::Id(v) => graph.has_vertex(v),
300 Path::Seq(edges) => {
301 edges.iter().all(|e| graph.has_edge(e)) &&
303 edges.iter().tuple_windows().all(|(e, f)| graph.tgt(e) == graph.src(f))
305 }
306 }
307 }
308
309 pub fn is_simple(&self) -> bool
313 where
314 E: Eq + Hash,
315 {
316 match self {
317 Path::Id(_) => true,
318 Path::Seq(edges) => {
319 let edges: HashSet<_> = edges.into_iter().collect();
320 edges.len() == self.len()
321 }
322 }
323 }
324
325 pub fn reduce(self, fv: impl FnOnce(V) -> E, fe: impl FnMut(E, E) -> E) -> E {
327 match self {
328 Path::Id(v) => fv(v),
329 Path::Seq(edges) => edges.into_iter().reduce(fe).unwrap(),
331 }
332 }
333
334 pub fn map<CodV, CodE>(
336 self,
337 fv: impl FnOnce(V) -> CodV,
338 fe: impl FnMut(E) -> CodE,
339 ) -> Path<CodV, CodE> {
340 match self {
341 Path::Id(v) => Path::Id(fv(v)),
342 Path::Seq(edges) => Path::Seq(edges.map(fe)),
343 }
344 }
345
346 pub fn map_reduce<T>(
351 self,
352 fv: impl FnOnce(V) -> T,
353 fe: impl FnMut(E) -> T,
354 f: impl FnMut(T, T) -> T,
355 ) -> T {
356 match self {
357 Path::Id(v) => fv(v),
358 Path::Seq(edges) => edges.into_iter().map(fe).reduce(f).unwrap(),
359 }
360 }
361
362 pub fn partial_map<CodV, CodE>(
364 self,
365 fv: impl FnOnce(V) -> Option<CodV>,
366 fe: impl FnMut(E) -> Option<CodE>,
367 ) -> Option<Path<CodV, CodE>> {
368 match self {
369 Path::Id(v) => Some(Path::Id(fv(v)?)),
370 Path::Seq(edges) => {
371 let edges: Option<Vec<_>> = edges.into_iter().map(fe).collect();
372 Path::from_vec(edges?)
373 }
374 }
375 }
376
377 pub fn try_map<CodV, CodE, Err>(
379 self,
380 fv: impl FnOnce(V) -> Result<CodV, Err>,
381 fe: impl FnMut(E) -> Result<CodE, Err>,
382 ) -> Result<Path<CodV, CodE>, Err> {
383 match self {
384 Path::Id(v) => Ok(Path::Id(fv(v)?)),
385 Path::Seq(edges) => {
386 let edges: Result<Vec<_>, _> = edges.into_iter().map(fe).collect();
387 Ok(Path::from_vec(edges?).unwrap())
388 }
389 }
390 }
391}
392
393impl<V, E> Path<V, Path<V, E>> {
394 pub fn flatten(self) -> Path<V, E> {
399 match self {
400 Path::Id(x) => Path::Id(x),
401 Path::Seq(paths) => {
402 if paths.iter().any(|p| matches!(p, Path::Seq(_))) {
403 let edges = paths
405 .into_iter()
406 .filter_map(|p| match p {
407 Path::Id(_) => None,
408 Path::Seq(edges) => Some(edges),
409 })
410 .flatten();
411 Path::Seq(NonEmpty::collect(edges).unwrap())
412 } else {
413 paths.head
415 }
416 }
417 }
418 }
419
420 pub fn flatten_in(self, graph: &impl Graph<V = V, E = E>) -> Option<Path<V, E>>
425 where
426 V: Eq + Clone,
427 {
428 if let Path::Seq(paths) = &self
429 && !paths.iter().tuple_windows().all(|(p1, p2)| p1.tgt(graph) == p2.src(graph))
430 {
431 None
432 } else {
433 Some(self.flatten())
434 }
435 }
436}
437
438#[derive(Clone, Debug, PartialEq, Eq, From)]
455pub enum ShortPath<V, E> {
456 Zero(V),
458
459 #[from]
461 One(E),
462}
463
464pub type SkelShortPath = ShortPath<usize, usize>;
466
467impl<V, E> From<ShortPath<V, E>> for Path<V, E> {
468 fn from(path: ShortPath<V, E>) -> Self {
469 match path {
470 ShortPath::Zero(v) => Path::Id(v),
471 ShortPath::One(e) => Path::single(e),
472 }
473 }
474}
475
476impl<V, E> TryFrom<Path<V, E>> for ShortPath<V, E> {
477 type Error = ();
478
479 fn try_from(path: Path<V, E>) -> Result<Self, Self::Error> {
480 match path {
481 Path::Id(v) => Ok(ShortPath::Zero(v)),
482 _ => path.only().map(ShortPath::One).ok_or(()),
483 }
484 }
485}
486
487impl<V, E> ShortPath<V, E> {
488 pub fn contained_in(&self, graph: &impl Graph<V = V, E = E>) -> bool {
490 match self {
491 ShortPath::Zero(v) => graph.has_vertex(v),
492 ShortPath::One(e) => graph.has_edge(e),
493 }
494 }
495
496 pub fn src(&self, graph: &impl Graph<V = V, E = E>) -> V
498 where
499 V: Clone,
500 {
501 match self {
502 ShortPath::Zero(v) => v.clone(),
503 ShortPath::One(e) => graph.src(e),
504 }
505 }
506
507 pub fn tgt(&self, graph: &impl Graph<V = V, E = E>) -> V
509 where
510 V: Clone,
511 {
512 match self {
513 ShortPath::Zero(v) => v.clone(),
514 ShortPath::One(e) => graph.tgt(e),
515 }
516 }
517
518 pub fn as_edge(self, graph: &impl ReflexiveGraph<V = V, E = E>) -> E {
520 match self {
521 ShortPath::Zero(v) => graph.refl(v),
522 ShortPath::One(e) => e,
523 }
524 }
525
526 pub fn map<CodV, CodE>(
528 self,
529 fv: impl FnOnce(V) -> CodV,
530 fe: impl FnOnce(E) -> CodE,
531 ) -> ShortPath<CodV, CodE> {
532 match self {
533 ShortPath::Zero(v) => ShortPath::Zero(fv(v)),
534 ShortPath::One(e) => ShortPath::One(fe(e)),
535 }
536 }
537}
538
539#[derive(Clone, Debug, PartialEq, Eq, Constructor)]
541pub struct PathEq<V, E> {
542 pub lhs: Path<V, E>,
544
545 pub rhs: Path<V, E>,
547}
548
549impl<V, E> PathEq<V, E> {
550 pub fn src(&self, graph: &impl Graph<V = V, E = E>) -> V
554 where
555 V: Eq + Clone,
556 {
557 let (x, y) = (self.lhs.src(graph), self.rhs.src(graph));
558 assert!(x == y, "Both sides of path equation should have same source");
559 x
560 }
561
562 pub fn tgt(&self, graph: &impl Graph<V = V, E = E>) -> V
566 where
567 V: Eq + Clone,
568 {
569 let (x, y) = (self.lhs.tgt(graph), self.rhs.tgt(graph));
570 assert!(x == y, "Both sides of path equation should have same target");
571 x
572 }
573
574 pub fn validate_in<G>(&self, graph: &G) -> Result<(), NonEmpty<InvalidPathEq>>
576 where
577 V: Eq + Clone,
578 G: Graph<V = V, E = E>,
579 {
580 validate::wrap_errors(self.iter_invalid_in(graph))
581 }
582
583 pub fn iter_invalid_in<G>(
585 &self,
586 graph: &G,
587 ) -> impl Iterator<Item = InvalidPathEq> + use<G, V, E>
588 where
589 V: Eq + Clone,
590 G: Graph<V = V, E = E>,
591 {
592 let mut errs = Vec::new();
593 if !self.lhs.contained_in(graph) {
594 errs.push(InvalidPathEq::Lhs);
595 }
596 if !self.rhs.contained_in(graph) {
597 errs.push(InvalidPathEq::Rhs);
598 }
599 if errs.is_empty() {
600 if self.lhs.src(graph) != self.rhs.src(graph) {
601 errs.push(InvalidPathEq::Src);
602 }
603 if self.lhs.tgt(graph) != self.rhs.tgt(graph) {
604 errs.push(InvalidPathEq::Tgt);
605 }
606 }
607 errs.into_iter()
608 }
609}
610
611#[derive(Clone, Debug, PartialEq, Eq)]
613#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
614#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
615#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
616pub enum InvalidPathEq {
617 Lhs,
619
620 Rhs,
622
623 Src,
625
626 Tgt,
628}
629
630#[cfg(test)]
631mod tests {
632 use super::super::graph::SkelGraph;
633 use super::*;
634 use std::convert::identity;
635
636 #[test]
637 fn path_in_graph() {
638 let g = SkelGraph::triangle();
639 let path = Path::pair(0, 1);
640 assert_eq!(path.src(&g), 0);
641 assert_eq!(path.tgt(&g), 2);
642 assert_eq!(Path::single(0).concat_in(&g, Path::single(1)), Some(path));
643
644 assert!(Path::Id(2).contained_in(&g));
645 assert!(!Path::Id(3).contained_in(&g));
646 assert!(Path::pair(0, 1).contained_in(&g));
647 assert!(!Path::pair(1, 0).contained_in(&g));
648 }
649
650 #[test]
651 fn short_paths() {
652 let (v, e) = (1, 1);
653 let path: SkelPath = ShortPath::Zero(v).into();
654 assert_eq!(path.try_into(), Ok(SkelShortPath::Zero(v)));
655 let path: SkelPath = ShortPath::One(e).into();
656 assert_eq!(path.clone().only(), Some(e));
657 assert_eq!(path.try_into(), Ok(ShortPath::One(e)));
658 }
659
660 #[test]
661 fn insert_into_path() {
662 let mut path = SkelPath::Id(0);
663 path.insert(0, 2);
664 assert_eq!(path, Path::single(2));
665 path.insert(0, 1);
666 assert_eq!(path, Path::pair(1, 2));
667
668 assert_eq!(SkelPath::empty(0).splice(0..0, Path::pair(0, 1)), Path::pair(0, 1));
669 assert_eq!(SkelPath::empty(0).splice(0..0, Path::empty(0)), Path::empty(0));
670 let target = SkelPath::Seq(nonempty![0, 1, 2]);
671 assert_eq!(Path::pair(0, 2).splice(1..1, Path::single(1)), target);
672 assert_eq!(Path::pair(0, 2).splice(1..2, Path::pair(1, 2)), target);
673 assert_eq!(target.clone().splice(1..3, Path::pair(1, 2)), target);
674 assert_eq!(target.clone().splice(1..1, Path::empty(0)), target);
675 }
676
677 #[test]
678 fn subpath() {
679 let g = SkelGraph::path(4);
680 assert_eq!(Path::Id(1).subpath(&g, 0..0), Path::Id(1));
681 let path = Path::Seq(nonempty![0, 1, 2]);
682 assert_eq!(path.subpath(&g, 0..0), Path::Id(0));
683 assert_eq!(path.subpath(&g, 1..1), Path::Id(1));
684 assert_eq!(path.subpath(&g, 3..3), Path::Id(3));
685 assert_eq!(path.subpath(&g, 0..2), Path::pair(0, 1));
686 assert_eq!(path.subpath(&g, 1..3), Path::pair(1, 2));
687 }
688
689 #[test]
690 fn map_path() {
691 let id = SkelPath::Id(1);
692 assert_eq!(id.iter().count(), 0);
693 assert_eq!(id.clone().into_iter().count(), 0);
694 assert_eq!(id.clone().map(|v| v + 1, identity), Path::Id(2));
695 assert_eq!(id.partial_map(|v| Some(v + 1), Some), Some(Path::Id(2)));
696
697 let pair = SkelPath::pair(0, 1);
698 assert_eq!(pair.iter().count(), 2);
699 assert_eq!(pair.clone().into_iter().count(), 2);
700 assert_eq!(pair.clone().map(identity, |e| e + 1), Path::pair(1, 2));
701 assert_eq!(pair.partial_map(Some, |e| Some(e + 1)), Some(Path::pair(1, 2)));
702 }
703
704 #[test]
705 fn path_eq() {
706 let g = SkelGraph::triangle();
707 let eq = PathEq::new(Path::pair(0, 1), Path::single(2));
708 assert_eq!(eq.src(&g), 0);
709 assert_eq!(eq.tgt(&g), 2);
710 assert!(eq.validate_in(&g).is_ok());
711 }
712
713 #[test]
714 fn path_is_simple() {
715 assert!(SkelPath::pair(0, 1).is_simple());
716 assert!(!SkelPath::pair(0, 0).is_simple());
717 assert!(SkelPath::Id(0).is_simple());
718 }
719}