Skip to main content

gen_graph/
lib.rs

1use std::{
2    collections::{HashMap, HashSet, VecDeque},
3    fmt::{self, Debug},
4    hash::Hash,
5    iter::from_fn,
6    rc::Rc,
7};
8
9use gen_core::{
10    HashId, NodeIntervalBlock, PATH_END_NODE_ID, PATH_START_NODE_ID, PathBlock, Strand,
11    is_end_node, is_start_node,
12};
13use interavl::IntervalTree as IT2;
14use intervaltree::IntervalTree;
15use petgraph::{
16    Direction,
17    graphmap::DiGraphMap,
18    prelude::EdgeRef,
19    visit::{
20        Dfs, GraphRef, IntoEdgeReferences, IntoEdges, IntoNeighbors, IntoNeighborsDirected,
21        NodeCount, Reversed,
22    },
23};
24use serde::{Deserialize, Serialize};
25
26pub mod traits;
27pub use traits::MergeGraph;
28
29#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
30pub struct GraphNode {
31    pub node_id: HashId,
32    pub sequence_start: i64,
33    pub sequence_end: i64,
34}
35
36impl fmt::Display for GraphNode {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        write!(
39            f,
40            "{}[{}-{}]",
41            self.node_id, self.sequence_start, self.sequence_end
42        )
43    }
44}
45
46impl GraphNode {
47    pub fn length(&self) -> i64 {
48        self.sequence_end - self.sequence_start
49    }
50}
51
52pub type GenGraph = DiGraphMap<GraphNode, Vec<GraphEdge>>;
53pub type OperationGraph = DiGraphMap<HashId, ()>;
54
55#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
56pub struct GraphEdge {
57    pub edge_id: HashId,
58    pub source_strand: Strand,
59    pub target_strand: Strand,
60    pub chromosome_index: i64,
61    pub phased: i64,
62    pub created_on: i64,
63}
64
65// #[derive(Debug)]
66// pub struct OperationGraph {
67//     pub graph: DiGraphMap<usize, ()>,
68//     max_node_id: usize,
69//     pub node_ids: HashMap<String, usize>,
70//     reverse_map: HashMap<usize, String>,
71// }
72
73// impl Default for OperationGraph {
74//     fn default() -> Self {
75//         Self::new()
76//     }
77// }
78
79// impl OperationGraph {
80//     pub fn new() -> Self {
81//         OperationGraph {
82//             graph: DiGraphMap::new(),
83//             max_node_id: HashId::convert_str("0"),
84//             node_ids: HashMap::new(),
85//             reverse_map: HashMap::new(),
86//         }
87//     }
88
89//     pub fn add_node(&mut self, hash_id: &str) -> usize {
90//         let node_id = *self.node_ids.entry(hash_id.to_string()).or_insert_with(|| {
91//             let node_id = self.max_node_id;
92//             self.reverse_map.insert(node_id, hash_id.to_string());
93//             self.graph.add_node(node_id);
94//             self.max_node_id += 1;
95//             node_id
96//         });
97//         node_id
98//     }
99
100//     pub fn remove_node(&mut self, node_id: usize) {
101//         self.graph.remove_node(node_id);
102//         if let Some(key) = self.reverse_map.remove(&node_id) {
103//             self.node_ids.remove(&key).unwrap();
104//         }
105//     }
106
107//     pub fn remove_key(&mut self, hash_id: &str) {
108//         if let Some(node_index) = self.node_ids.remove(hash_id) {
109//             self.graph.remove_node(node_index);
110//             self.reverse_map.remove(&node_index).unwrap();
111//         }
112//     }
113
114//     pub fn get_node(&self, node_id: &str) -> usize {
115//         self.node_ids[node_id]
116//     }
117
118//     pub fn get_key(&self, index: usize) -> String {
119//         self.reverse_map[&index].clone()
120//     }
121
122//     pub fn add_edge(&mut self, src: &str, target: &str) {
123//         let src_node_id = self.add_node(src);
124//         let target_node_id = self.add_node(target);
125//         self.graph.add_edge(src_node_id, target_node_id, ());
126//     }
127// }
128
129// hacked from https://docs.rs/petgraph/latest/src/petgraph/algo/simple_paths.rs.html#36-102 to support digraphmap
130
131pub fn all_simple_paths<G>(
132    graph: G,
133    from: G::NodeId,
134    to: G::NodeId,
135) -> impl Iterator<Item = Vec<G::NodeId>>
136where
137    G: NodeCount,
138    G: IntoNeighborsDirected,
139    G::NodeId: Eq + Hash,
140{
141    // list of visited nodes
142    let mut visited = vec![from];
143    // list of childs of currently exploring path nodes,
144    // last elem is list of childs of last visited node
145    let mut stack = vec![graph.neighbors_directed(from, Direction::Outgoing)];
146
147    from_fn(move || {
148        while let Some(children) = stack.last_mut() {
149            if let Some(child) = children.next() {
150                if child == to {
151                    let path = visited.iter().cloned().chain(Some(to)).collect::<_>();
152                    return Some(path);
153                } else if !visited.contains(&child) {
154                    visited.push(child);
155                    stack.push(graph.neighbors_directed(child, Direction::Outgoing));
156                }
157            } else {
158                stack.pop();
159                visited.pop();
160            }
161        }
162        None
163    })
164}
165
166pub fn all_intermediate_edges<G>(
167    graph: G,
168    from: G::NodeId,
169    to: G::NodeId,
170) -> Vec<<G as IntoEdgeReferences>::EdgeRef>
171where
172    G: GraphRef + IntoEdges + petgraph::visit::IntoNeighborsDirected + petgraph::visit::Visitable,
173    G::NodeId: Eq + Hash + std::fmt::Display,
174{
175    let mut outgoing_nodes = HashSet::new();
176    outgoing_nodes.insert(from);
177    let mut dfs_outbound = Dfs::new(graph, from);
178
179    while let Some(outgoing_node) = dfs_outbound.next(graph) {
180        outgoing_nodes.insert(outgoing_node);
181    }
182
183    // This is a standard graph algorithm trick.  To get all nodes on paths from a source s to a
184    // target t, but not any other nodes, first find all nodes reachable from s.  Then reverse all
185    // the edges in the graph and find all nodes reachable from t.  The nodes in common between
186    // those two sets are the exactly the ones on some path from s to t.
187    let reversed_graph = Reversed(&graph);
188    let mut incoming_nodes = HashSet::new();
189    incoming_nodes.insert(to);
190    let mut dfs_inbound = Dfs::new(reversed_graph, to);
191    while let Some(incoming_node) = dfs_inbound.next(reversed_graph) {
192        incoming_nodes.insert(incoming_node);
193    }
194
195    let common_nodes: HashSet<&<G as petgraph::visit::GraphBase>::NodeId> =
196        outgoing_nodes.intersection(&incoming_nodes).collect();
197    let mut common_edgerefs = vec![];
198    for edge in graph.edge_references() {
199        let (source, target) = (edge.source(), edge.target());
200
201        if common_nodes.contains(&source) && common_nodes.contains(&target) {
202            common_edgerefs.push(edge);
203        }
204    }
205
206    common_edgerefs
207}
208
209pub fn all_simple_paths_by_edge<G>(
210    graph: G,
211    from: G::NodeId,
212    to: G::NodeId,
213) -> impl Iterator<Item = Vec<G::EdgeRef>>
214where
215    G: NodeCount + IntoEdges,
216    G: IntoNeighborsDirected,
217    G::NodeId: Eq + Hash,
218{
219    // list of visited nodes
220    let mut visited = vec![from];
221    // list of childs of currently exploring path nodes,
222    // last elem is list of childs of last visited node
223    let mut path: Vec<G::EdgeRef> = vec![];
224    let mut stack = vec![graph.edges(from)];
225
226    from_fn(move || {
227        while let Some(edges) = stack.last_mut() {
228            if let Some(edge) = edges.next() {
229                let target = edge.target();
230                if target == to {
231                    let a_path = path.iter().cloned().chain(Some(edge)).collect::<_>();
232                    return Some(a_path);
233                } else if !visited.contains(&target) {
234                    path.push(edge);
235                    visited.push(target);
236                    stack.push(graph.edges(target));
237                }
238            } else {
239                stack.pop();
240                path.pop();
241                visited.pop();
242            }
243        }
244        None
245    })
246}
247
248pub fn all_reachable_nodes<G>(graph: G, nodes: &[G::NodeId]) -> HashSet<G::NodeId>
249where
250    G: GraphRef + IntoNeighbors,
251    G::NodeId: Eq + Hash + Debug,
252{
253    let mut stack = VecDeque::new();
254    let mut reachable = HashSet::new();
255    for node in nodes.iter() {
256        stack.push_front(*node);
257        reachable.insert(*node);
258        while let Some(nx) = stack.pop_front() {
259            for succ in graph.neighbors(nx) {
260                if !reachable.contains(&succ) {
261                    reachable.insert(succ);
262                    stack.push_back(succ);
263                }
264            }
265        }
266    }
267    reachable
268}
269
270pub fn flatten_to_interval_tree(
271    graph: &GenGraph,
272    remove_ambiguous_positions: bool,
273) -> IntervalTree<i64, NodeIntervalBlock> {
274    #[derive(Clone, Debug, Ord, PartialOrd, Eq, Hash, PartialEq)]
275    struct NodeP {
276        x: i64,
277        y: i64,
278    }
279    let mut excluded_nodes = HashSet::new();
280    let mut node_tree: HashMap<HashId, IT2<NodeP, HashId>> = HashMap::new();
281
282    let mut start_nodes = vec![];
283    let mut end_nodes = vec![];
284    for node in graph.nodes() {
285        if is_start_node(node.node_id) {
286            start_nodes.push(node);
287        }
288        if is_end_node(node.node_id) {
289            end_nodes.push(node);
290        }
291    }
292
293    let mut spans: HashSet<NodeIntervalBlock> = HashSet::new();
294
295    for start in start_nodes.iter() {
296        for end_node in end_nodes.iter() {
297            for path in all_simple_paths_by_edge(&graph, *start, *end_node) {
298                let mut offset = 0;
299                for (source_node, target_node, edges) in path.iter() {
300                    let block_len = source_node.length();
301                    let node_start = offset;
302                    let node_end = offset + block_len;
303                    // Use the first edge in the vector for strand information
304                    let edge = &edges[0];
305                    spans.insert(NodeIntervalBlock {
306                        node_id: source_node.node_id,
307                        start: node_start,
308                        end: node_end,
309                        sequence_start: source_node.sequence_start,
310                        sequence_end: source_node.sequence_end,
311                        strand: edge.source_strand,
312                    });
313                    spans.insert(NodeIntervalBlock {
314                        node_id: target_node.node_id,
315                        start: node_end,
316                        end: node_end + target_node.length(),
317                        sequence_start: target_node.sequence_start,
318                        sequence_end: target_node.sequence_end,
319                        strand: edge.target_strand,
320                    });
321                    if remove_ambiguous_positions {
322                        for (node_id, node_range) in [
323                            (
324                                source_node.node_id,
325                                NodeP {
326                                    x: node_start,
327                                    y: source_node.sequence_start,
328                                }..NodeP {
329                                    x: node_end,
330                                    y: source_node.sequence_end,
331                                },
332                            ),
333                            (
334                                target_node.node_id,
335                                NodeP {
336                                    x: node_end,
337                                    y: target_node.sequence_start,
338                                }..NodeP {
339                                    x: node_end + target_node.length(),
340                                    y: target_node.sequence_end,
341                                },
342                            ),
343                        ] {
344                            // TODO; This could be a bit better by trying to conserve subregions
345                            // within a node that are not ambiguous instead of kicking the entire
346                            // node out.
347                            node_tree
348                                .entry(node_id)
349                                .and_modify(|tree| {
350                                    for (stored_range, _stored_node_id) in
351                                        tree.iter_overlaps(&node_range)
352                                    {
353                                        if *stored_range != node_range {
354                                            excluded_nodes.insert(node_id);
355                                            break;
356                                        }
357                                    }
358                                    tree.insert(node_range.clone(), node_id);
359                                })
360                                .or_insert_with(|| {
361                                    let mut t = IT2::default();
362                                    t.insert(node_range.clone(), node_id);
363                                    t
364                                });
365                        }
366                    }
367                    offset += block_len;
368                }
369            }
370        }
371    }
372
373    let tree: IntervalTree<i64, NodeIntervalBlock> = spans
374        .iter()
375        .filter(|block| !remove_ambiguous_positions || !excluded_nodes.contains(&block.node_id))
376        .map(|block| {
377            if block.node_id == PATH_START_NODE_ID {
378                (i64::MIN + 1..0, *block)
379            } else if block.node_id == PATH_END_NODE_ID {
380                (block.end..i64::MAX, *block)
381            } else {
382                (block.start..block.end, *block)
383            }
384        })
385        .collect();
386    tree
387}
388
389pub fn project_path(graph: &GenGraph, path_blocks: &[PathBlock]) -> Vec<(GraphNode, Strand)> {
390    // When a path is created, it will refer to node positions in the graph as it exists then.
391    // If the graph is then updated, the path nodes may be split and the graph no longer contains
392    // the corresponding nodes in the initial path. This takes the initial path and identifies the
393    // set of nodes in the current graph corresponding to the path using a depth first search
394    // approach.
395
396    #[derive(Debug)]
397    struct PathNode {
398        node: (GraphNode, Strand),
399        // path_index serves as a pointer at which path block the added node was at. This is because
400        // in a DFS, we may reset to a previous position for a search and need to reset the position
401        // in the path as well.
402        path_index: usize,
403        prev: Option<Rc<PathNode>>,
404    }
405
406    let start_nodes = graph
407        .nodes()
408        .filter(|node| node.node_id == PATH_START_NODE_ID)
409        .collect::<Vec<GraphNode>>();
410    let start_node = start_nodes.first().unwrap();
411
412    let mut final_path = vec![];
413    let mut stack: VecDeque<Rc<PathNode>> = VecDeque::new();
414    let mut visited: HashSet<(GraphNode, Strand)> = HashSet::new();
415    let mut path_index = 0;
416    let mut current_pos;
417
418    // Start with the initial path containing only the start node
419    stack.push_back(Rc::new(PathNode {
420        node: (*start_node, Strand::Forward),
421        path_index,
422        prev: None,
423    }));
424
425    while let Some(current_node) = stack.pop_back() {
426        let current = current_node.node;
427        let mut current_block = &path_blocks[current_node.path_index];
428
429        if !visited.insert(current) {
430            continue;
431        }
432
433        // if current completes the current block, move to the next path block
434        if current.0.sequence_end == current_block.sequence_end {
435            path_index += 1;
436            if path_index < path_blocks.len() {
437                current_block = &path_blocks[path_index];
438                current_pos = current_block.sequence_start;
439            } else {
440                // we're done, path_block is fully consumed
441                let mut path = vec![];
442                let mut path_ref = Some(Rc::clone(&current_node));
443                while let Some(pn) = path_ref {
444                    path.push(pn.node);
445                    path_ref = pn.prev.clone();
446                }
447                final_path = path.into_iter().rev().collect();
448                break;
449            }
450        } else {
451            current_pos = current.0.sequence_end;
452        }
453
454        for (_src, neighbor, edges) in graph.edges(current.0) {
455            // Use the first edge in the vector for strand information
456            if neighbor.node_id == current_block.node_id
457                && neighbor.sequence_start == current_pos
458                && edges
459                    .iter()
460                    .any(|edge| current_block.strand == edge.target_strand)
461            {
462                stack.push_back(Rc::new(PathNode {
463                    node: (neighbor, current_block.strand),
464                    path_index,
465                    prev: Some(Rc::clone(&current_node)),
466                }));
467            }
468        }
469    }
470    final_path
471}
472
473/// Find the articulation points of a directed graph using a non-recursive approach
474/// This is a modified version of the algorithm found here:
475/// https://en.wikipedia.org/wiki/Biconnected_component#Articulation_points
476pub fn find_articulation_points(graph: &GenGraph) -> Vec<GraphNode> {
477    let mut articulation_points: Vec<GraphNode> = Vec::new();
478    let mut discovery_time: HashMap<GraphNode, usize> = HashMap::new();
479    let mut low: HashMap<GraphNode, usize> = HashMap::new();
480    let mut parent: HashMap<GraphNode, Option<GraphNode>> = HashMap::new();
481    let mut time = 0;
482
483    for node in graph.nodes() {
484        if !discovery_time.contains_key(&node) {
485            let mut stack = vec![(node, None, true)];
486            while let Some((u, p, is_first_time)) = stack.pop() {
487                if is_first_time {
488                    // Initialize discovery time and low value
489                    discovery_time.insert(u, time);
490                    low.insert(u, time);
491                    time += 1;
492                    parent.insert(u, p);
493
494                    // Push the node back with is_first_time = false to process after its neighbors
495                    stack.push((u, p, false));
496
497                    // Consider both incoming and outgoing edges as undirected
498                    let neighbors: Vec<_> = graph
499                        .neighbors_directed(u, Direction::Outgoing)
500                        .chain(graph.neighbors_directed(u, Direction::Incoming))
501                        .collect();
502
503                    for v in neighbors {
504                        if !discovery_time.contains_key(&v) {
505                            stack.push((v, Some(u), true));
506                        } else if Some(v) != p {
507                            // Update low[u] if v is not parent
508                            let current_low = low.get(&u).cloned().unwrap_or(usize::MAX);
509                            let v_disc = discovery_time.get(&v).cloned().unwrap_or(usize::MAX);
510                            low.insert(u, current_low.min(v_disc));
511                        }
512                    }
513                } else {
514                    // Post-processing after visiting all neighbors
515                    let mut is_articulation = false;
516                    let mut child_count = 0;
517
518                    let neighbors: Vec<_> = graph
519                        .neighbors_directed(u, Direction::Outgoing)
520                        .chain(graph.neighbors_directed(u, Direction::Incoming))
521                        .collect();
522
523                    for v in neighbors {
524                        if parent.get(&v).cloned() == Some(Some(u)) {
525                            child_count += 1;
526                            let v_low = low.get(&v).cloned().unwrap_or(usize::MAX);
527                            let u_disc = discovery_time.get(&u).cloned().unwrap_or(usize::MAX);
528                            if v_low >= u_disc {
529                                is_articulation = true;
530                            }
531                            let current_low = low.get(&u).cloned().unwrap_or(usize::MAX);
532                            let v_low = low.get(&v).cloned().unwrap_or(usize::MAX);
533                            low.insert(u, current_low.min(v_low));
534                        } else if Some(v) != parent.get(&u).cloned().unwrap_or(None) {
535                            let v_disc = discovery_time.get(&v).cloned().unwrap_or(usize::MAX);
536                            let current_low = low.get(&u).cloned().unwrap_or(usize::MAX);
537                            low.insert(u, current_low.min(v_disc));
538                        }
539                    }
540
541                    let u_parent = parent.get(&u).cloned().unwrap_or(None);
542                    if (u_parent.is_some() && is_articulation)
543                        || (u_parent.is_none() && child_count > 1)
544                    {
545                        articulation_points.push(u);
546                    }
547                }
548            }
549        }
550    }
551
552    articulation_points.sort();
553    articulation_points.dedup();
554    articulation_points
555}
556
557#[cfg(test)]
558mod tests {
559    use std::collections::HashSet;
560
561    use petgraph::graphmap::DiGraphMap;
562
563    use super::*;
564
565    #[test]
566    fn test_path_graph() {
567        let mut graph: DiGraphMap<i64, ()> = DiGraphMap::new();
568        graph.add_node(1);
569        graph.add_node(2);
570        graph.add_node(3);
571
572        graph.add_edge(1, 2, ());
573        graph.add_edge(2, 3, ());
574
575        let paths = all_simple_paths(&graph, 1, 3).collect::<Vec<Vec<i64>>>();
576        assert_eq!(paths.len(), 1);
577        let path = paths.first().unwrap().clone();
578        assert_eq!(path, vec![1, 2, 3]);
579    }
580
581    #[test]
582    fn test_get_simple_paths_by_edge() {
583        let mut graph: DiGraphMap<i64, ()> = DiGraphMap::new();
584        graph.add_node(1);
585        graph.add_node(2);
586        graph.add_node(3);
587        graph.add_node(4);
588        graph.add_node(5);
589        graph.add_node(6);
590        graph.add_node(7);
591        graph.add_node(8);
592        graph.add_node(9);
593
594        graph.add_edge(1, 2, ());
595        graph.add_edge(2, 3, ());
596        graph.add_edge(3, 4, ());
597        graph.add_edge(4, 5, ());
598        graph.add_edge(2, 6, ());
599        graph.add_edge(6, 7, ());
600        graph.add_edge(7, 4, ());
601        graph.add_edge(6, 8, ());
602        graph.add_edge(8, 7, ());
603
604        let edge_path =
605            all_simple_paths_by_edge(&graph, 1, 5).collect::<Vec<Vec<(i64, i64, &())>>>();
606        assert_eq!(
607            edge_path,
608            vec![
609                vec![(1, 2, &()), (2, 3, &()), (3, 4, &()), (4, 5, &())],
610                vec![
611                    (1, 2, &()),
612                    (2, 6, &()),
613                    (6, 7, &()),
614                    (7, 4, &()),
615                    (4, 5, &())
616                ],
617                vec![
618                    (1, 2, &()),
619                    (2, 6, &()),
620                    (6, 8, &()),
621                    (8, 7, &()),
622                    (7, 4, &()),
623                    (4, 5, &())
624                ]
625            ]
626        );
627    }
628
629    #[test]
630    fn test_two_path_graph() {
631        let mut graph: DiGraphMap<i64, ()> = DiGraphMap::new();
632        graph.add_node(1);
633        graph.add_node(2);
634        graph.add_node(3);
635        graph.add_node(4);
636
637        graph.add_edge(1, 2, ());
638        graph.add_edge(1, 3, ());
639        graph.add_edge(2, 4, ());
640        graph.add_edge(3, 4, ());
641
642        let paths = all_simple_paths(&graph, 1, 4).collect::<Vec<Vec<i64>>>();
643        assert_eq!(paths.len(), 2);
644        assert_eq!(
645            HashSet::<Vec<i64>>::from_iter::<Vec<Vec<i64>>>(paths),
646            HashSet::from_iter(vec![vec![1, 2, 4], vec![1, 3, 4]])
647        );
648    }
649
650    #[test]
651    fn test_two_by_two_combinatorial_graph() {
652        let mut graph: DiGraphMap<i64, ()> = DiGraphMap::new();
653        graph.add_node(1);
654        graph.add_node(2);
655        graph.add_node(3);
656        graph.add_node(4);
657        graph.add_node(5);
658        graph.add_node(6);
659        graph.add_node(7);
660
661        graph.add_edge(1, 2, ());
662        graph.add_edge(1, 3, ());
663        graph.add_edge(2, 4, ());
664        graph.add_edge(3, 4, ());
665        graph.add_edge(4, 5, ());
666        graph.add_edge(4, 6, ());
667        graph.add_edge(5, 7, ());
668        graph.add_edge(6, 7, ());
669
670        let paths = all_simple_paths(&graph, 1, 7).collect::<Vec<Vec<i64>>>();
671        assert_eq!(paths.len(), 4);
672        assert_eq!(
673            HashSet::<Vec<i64>>::from_iter::<Vec<Vec<i64>>>(paths),
674            HashSet::from_iter(vec![
675                vec![1, 2, 4, 5, 7],
676                vec![1, 3, 4, 5, 7],
677                vec![1, 2, 4, 6, 7],
678                vec![1, 3, 4, 6, 7]
679            ])
680        );
681    }
682
683    #[test]
684    fn test_three_by_three_combinatorial_graph() {
685        let mut graph: DiGraphMap<i64, ()> = DiGraphMap::new();
686        graph.add_node(1);
687        graph.add_node(2);
688        graph.add_node(3);
689        graph.add_node(4);
690        graph.add_node(5);
691        graph.add_node(6);
692        graph.add_node(7);
693        graph.add_node(8);
694        graph.add_node(9);
695
696        graph.add_edge(1, 2, ());
697        graph.add_edge(1, 3, ());
698        graph.add_edge(1, 4, ());
699        graph.add_edge(2, 5, ());
700        graph.add_edge(3, 5, ());
701        graph.add_edge(4, 5, ());
702        graph.add_edge(5, 6, ());
703        graph.add_edge(5, 7, ());
704        graph.add_edge(5, 8, ());
705        graph.add_edge(6, 9, ());
706        graph.add_edge(7, 9, ());
707        graph.add_edge(8, 9, ());
708
709        let paths = all_simple_paths(&graph, 1, 9).collect::<Vec<Vec<i64>>>();
710        assert_eq!(paths.len(), 9);
711        let expected_paths = vec![
712            vec![1, 2, 5, 6, 9],
713            vec![1, 3, 5, 6, 9],
714            vec![1, 4, 5, 6, 9],
715            vec![1, 2, 5, 7, 9],
716            vec![1, 3, 5, 7, 9],
717            vec![1, 4, 5, 7, 9],
718            vec![1, 2, 5, 8, 9],
719            vec![1, 3, 5, 8, 9],
720            vec![1, 4, 5, 8, 9],
721        ];
722        assert_eq!(
723            HashSet::<Vec<i64>>::from_iter::<Vec<Vec<i64>>>(paths),
724            HashSet::from_iter(expected_paths)
725        );
726    }
727
728    #[test]
729    fn test_super_bubble_path() {
730        // This graph looks like this:
731        //              8
732        //            /  \
733        //          6  -> 7
734        //         /        \
735        //    1 -> 2 -> 3 -> 4 -> 5
736        //
737        //  We ensure that we capture all 3 paths from 1 -> 5
738        let mut graph: DiGraphMap<i64, ()> = DiGraphMap::new();
739        graph.add_node(1);
740        graph.add_node(2);
741        graph.add_node(3);
742        graph.add_node(4);
743        graph.add_node(5);
744        graph.add_node(6);
745        graph.add_node(7);
746        graph.add_node(8);
747        graph.add_node(9);
748
749        graph.add_edge(1, 2, ());
750        graph.add_edge(2, 3, ());
751        graph.add_edge(3, 4, ());
752        graph.add_edge(4, 5, ());
753        graph.add_edge(2, 6, ());
754        graph.add_edge(6, 7, ());
755        graph.add_edge(7, 4, ());
756        graph.add_edge(6, 8, ());
757        graph.add_edge(8, 7, ());
758
759        let paths = all_simple_paths(&graph, 1, 5).collect::<Vec<Vec<i64>>>();
760        assert_eq!(
761            HashSet::<Vec<i64>>::from_iter::<Vec<Vec<i64>>>(paths),
762            HashSet::from_iter(vec![
763                vec![1, 2, 3, 4, 5],
764                vec![1, 2, 6, 7, 4, 5],
765                vec![1, 2, 6, 8, 7, 4, 5]
766            ])
767        );
768    }
769
770    #[test]
771    fn test_finds_all_reachable_nodes() {
772        //
773        //   1 -> 2 -> 3 -> 4 -> 5
774        //           /
775        //   6 -> 7
776        //
777        let mut graph: DiGraphMap<i64, ()> = DiGraphMap::new();
778        graph.add_node(1);
779        graph.add_node(2);
780        graph.add_node(3);
781        graph.add_node(4);
782        graph.add_node(5);
783        graph.add_node(6);
784        graph.add_node(7);
785
786        graph.add_edge(1, 2, ());
787        graph.add_edge(2, 3, ());
788        graph.add_edge(3, 4, ());
789        graph.add_edge(4, 5, ());
790        graph.add_edge(6, 7, ());
791        graph.add_edge(7, 3, ());
792
793        assert_eq!(
794            all_reachable_nodes(&graph, &[1]),
795            HashSet::from_iter(vec![1, 2, 3, 4, 5])
796        );
797
798        assert_eq!(
799            all_reachable_nodes(&graph, &[1, 6]),
800            HashSet::from_iter(vec![1, 2, 3, 4, 5, 6, 7])
801        );
802
803        assert_eq!(
804            all_reachable_nodes(&graph, &[3]),
805            HashSet::from_iter(vec![3, 4, 5])
806        );
807
808        assert_eq!(
809            all_reachable_nodes(&graph, &[5]),
810            HashSet::from_iter(vec![5])
811        );
812    }
813
814    mod test_all_intermediate_edges {
815        use super::*;
816        #[test]
817        fn test_one_part_group() {
818            //
819            //   1 -> 2 -> 3 -> 4 -> 5
820            //         \-> 6 /
821            //
822            let mut graph: DiGraphMap<i64, ()> = DiGraphMap::new();
823            graph.add_node(1);
824            graph.add_node(2);
825            graph.add_node(3);
826            graph.add_node(4);
827            graph.add_node(5);
828            graph.add_node(6);
829
830            graph.add_edge(1, 2, ());
831            graph.add_edge(2, 3, ());
832            graph.add_edge(3, 4, ());
833            graph.add_edge(4, 5, ());
834            graph.add_edge(2, 6, ());
835            graph.add_edge(6, 4, ());
836
837            let result_edges = all_intermediate_edges(&graph, 2, 4);
838            let intermediate_edges = result_edges
839                .iter()
840                .map(|(source, target, _weight)| (*source, *target))
841                .collect::<Vec<(i64, i64)>>();
842            assert_eq!(intermediate_edges, vec![(2, 3), (3, 4), (2, 6), (6, 4)]);
843        }
844
845        #[test]
846        fn test_two_part_groups() {
847            //
848            //   1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7
849            //         \-> 8 /    \-> 9 /
850            //
851            let mut graph: DiGraphMap<i64, ()> = DiGraphMap::new();
852            graph.add_node(1);
853            graph.add_node(2);
854            graph.add_node(3);
855            graph.add_node(4);
856            graph.add_node(5);
857            graph.add_node(6);
858            graph.add_node(7);
859            graph.add_node(8);
860            graph.add_node(9);
861
862            graph.add_edge(1, 2, ());
863            graph.add_edge(2, 3, ());
864            graph.add_edge(3, 4, ());
865            graph.add_edge(4, 5, ());
866            graph.add_edge(5, 6, ());
867            graph.add_edge(6, 7, ());
868            graph.add_edge(2, 8, ());
869            graph.add_edge(8, 4, ());
870            graph.add_edge(4, 9, ());
871            graph.add_edge(9, 6, ());
872
873            let result_edges = all_intermediate_edges(&graph, 2, 6);
874            let intermediate_edges = result_edges
875                .iter()
876                .map(|(source, target, _weight)| (*source, *target))
877                .collect::<Vec<(i64, i64)>>();
878            assert_eq!(
879                intermediate_edges,
880                vec![
881                    (2, 3),
882                    (3, 4),
883                    (4, 5),
884                    (5, 6),
885                    (2, 8),
886                    (8, 4),
887                    (4, 9),
888                    (9, 6)
889                ]
890            );
891        }
892
893        #[test]
894        fn test_one_part_group_with_unrelated_edges() {
895            //
896            //        / ------- 7 \
897            //   1 -> 2 -> 3 -> 4 -> 5
898            //         \-> 6 /
899            //
900            // Because 7 has 5 as a target, it is excluded from the subgraph from 2 to 4
901            let mut graph: DiGraphMap<i64, ()> = DiGraphMap::new();
902            graph.add_node(1);
903            graph.add_node(2);
904            graph.add_node(3);
905            graph.add_node(4);
906            graph.add_node(5);
907            graph.add_node(6);
908            graph.add_node(7);
909
910            graph.add_edge(1, 2, ());
911            graph.add_edge(2, 3, ());
912            graph.add_edge(3, 4, ());
913            graph.add_edge(4, 5, ());
914            graph.add_edge(2, 6, ());
915            graph.add_edge(6, 4, ());
916            graph.add_edge(2, 7, ());
917            graph.add_edge(7, 5, ());
918
919            let result_edges = all_intermediate_edges(&graph, 2, 4);
920            let intermediate_edges = result_edges
921                .iter()
922                .map(|(source, target, _weight)| (*source, *target))
923                .collect::<Vec<(i64, i64)>>();
924            assert_eq!(intermediate_edges, vec![(2, 3), (3, 4), (2, 6), (6, 4)]);
925        }
926    }
927
928    #[cfg(test)]
929    mod project_path {
930        use gen_core::PATH_END_NODE_ID;
931
932        use super::*;
933
934        #[test]
935        fn test_simple_path_projection() {
936            // graph looks like
937            //
938            //           /-> 4.3.5 -> 5.0.3 ---\
939            //  s -> 3.0.5 -------------------> 3.5.15 -> e
940            //  initial path is defined as 3.0.0 to 3.0.15
941            let mut graph = GenGraph::new();
942            graph.add_edge(
943                GraphNode {
944                    node_id: PATH_START_NODE_ID,
945                    sequence_start: 0,
946                    sequence_end: 0,
947                },
948                GraphNode {
949                    node_id: HashId::convert_str("3"),
950                    sequence_start: 0,
951                    sequence_end: 5,
952                },
953                vec![GraphEdge {
954                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
955                        .try_into()
956                        .unwrap(),
957                    source_strand: Strand::Forward,
958                    target_strand: Strand::Forward,
959                    chromosome_index: 0,
960                    phased: 0,
961                    created_on: 0,
962                }],
963            );
964            graph.add_edge(
965                GraphNode {
966                    node_id: HashId::convert_str("3"),
967                    sequence_start: 0,
968                    sequence_end: 5,
969                },
970                GraphNode {
971                    node_id: HashId::convert_str("4"),
972                    sequence_start: 3,
973                    sequence_end: 5,
974                },
975                vec![GraphEdge {
976                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
977                        .try_into()
978                        .unwrap(),
979                    source_strand: Strand::Forward,
980                    target_strand: Strand::Forward,
981                    chromosome_index: 0,
982                    phased: 0,
983                    created_on: 0,
984                }],
985            );
986            graph.add_edge(
987                GraphNode {
988                    node_id: HashId::convert_str("4"),
989                    sequence_start: 3,
990                    sequence_end: 5,
991                },
992                GraphNode {
993                    node_id: HashId::convert_str("5"),
994                    sequence_start: 0,
995                    sequence_end: 3,
996                },
997                vec![GraphEdge {
998                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
999                        .try_into()
1000                        .unwrap(),
1001                    source_strand: Strand::Forward,
1002                    target_strand: Strand::Forward,
1003                    chromosome_index: 0,
1004                    phased: 0,
1005                    created_on: 0,
1006                }],
1007            );
1008            graph.add_edge(
1009                GraphNode {
1010                    node_id: HashId::convert_str("5"),
1011                    sequence_start: 0,
1012                    sequence_end: 3,
1013                },
1014                GraphNode {
1015                    node_id: HashId::convert_str("6"),
1016                    sequence_start: 5,
1017                    sequence_end: 15,
1018                },
1019                vec![GraphEdge {
1020                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1021                        .try_into()
1022                        .unwrap(),
1023                    source_strand: Strand::Forward,
1024                    target_strand: Strand::Forward,
1025                    chromosome_index: 0,
1026                    phased: 0,
1027                    created_on: 0,
1028                }],
1029            );
1030            graph.add_edge(
1031                GraphNode {
1032                    node_id: HashId::convert_str("3"),
1033                    sequence_start: 0,
1034                    sequence_end: 5,
1035                },
1036                GraphNode {
1037                    node_id: HashId::convert_str("3"),
1038                    sequence_start: 5,
1039                    sequence_end: 15,
1040                },
1041                vec![GraphEdge {
1042                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1043                        .try_into()
1044                        .unwrap(),
1045                    source_strand: Strand::Forward,
1046                    target_strand: Strand::Forward,
1047                    chromosome_index: 0,
1048                    phased: 0,
1049                    created_on: 0,
1050                }],
1051            );
1052            graph.add_edge(
1053                GraphNode {
1054                    node_id: HashId::convert_str("3"),
1055                    sequence_start: 5,
1056                    sequence_end: 15,
1057                },
1058                GraphNode {
1059                    node_id: PATH_END_NODE_ID,
1060                    sequence_start: 0,
1061                    sequence_end: 0,
1062                },
1063                vec![GraphEdge {
1064                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1065                        .try_into()
1066                        .unwrap(),
1067                    source_strand: Strand::Forward,
1068                    target_strand: Strand::Forward,
1069                    chromosome_index: 0,
1070                    phased: 0,
1071                    created_on: 0,
1072                }],
1073            );
1074            let path_blocks = vec![
1075                PathBlock {
1076                    node_id: PATH_START_NODE_ID,
1077                    block_sequence: "".to_string(),
1078                    sequence_start: 0,
1079                    sequence_end: 0,
1080                    path_start: 0,
1081                    path_end: 0,
1082                    strand: Strand::Forward,
1083                },
1084                PathBlock {
1085                    node_id: HashId::convert_str("3"),
1086                    block_sequence: "".to_string(),
1087                    sequence_start: 0,
1088                    sequence_end: 15,
1089                    path_start: 0,
1090                    path_end: 0,
1091                    strand: Strand::Forward,
1092                },
1093                PathBlock {
1094                    node_id: PATH_END_NODE_ID,
1095                    block_sequence: "".to_string(),
1096                    sequence_start: 0,
1097                    sequence_end: 0,
1098                    path_start: 0,
1099                    path_end: 0,
1100                    strand: Strand::Forward,
1101                },
1102            ];
1103            let projection = project_path(&graph, &path_blocks);
1104            assert_eq!(
1105                projection,
1106                [
1107                    (
1108                        GraphNode {
1109                            node_id: PATH_START_NODE_ID,
1110                            sequence_start: 0,
1111                            sequence_end: 0
1112                        },
1113                        Strand::Forward
1114                    ),
1115                    (
1116                        GraphNode {
1117                            node_id: HashId::convert_str("3"),
1118                            sequence_start: 0,
1119                            sequence_end: 5
1120                        },
1121                        Strand::Forward
1122                    ),
1123                    (
1124                        GraphNode {
1125                            node_id: HashId::convert_str("3"),
1126                            sequence_start: 5,
1127                            sequence_end: 15
1128                        },
1129                        Strand::Forward
1130                    ),
1131                    (
1132                        GraphNode {
1133                            node_id: PATH_END_NODE_ID,
1134                            sequence_start: 0,
1135                            sequence_end: 0
1136                        },
1137                        Strand::Forward
1138                    )
1139                ]
1140            )
1141        }
1142
1143        #[test]
1144        fn test_nested_path_projection() {
1145            // graph looks like
1146            //                                                /-> 7.0.2 -\
1147            //           /-> 4.3.5 -> 5.0.3 ---\     /-> 6.0.3----------->6.3.7 -\
1148            //  s -> 3.0.5 -------------------> 3.5.10 ------------------------> 3.10.15 -> e
1149            //  initial path is defined as 3.0.0 -> 3.5.10 -> 6.0.7 -> 3.10.15
1150            let mut graph = GenGraph::new();
1151            graph.add_edge(
1152                GraphNode {
1153                    node_id: PATH_START_NODE_ID,
1154                    sequence_start: 0,
1155                    sequence_end: 0,
1156                },
1157                GraphNode {
1158                    node_id: HashId::convert_str("3"),
1159                    sequence_start: 0,
1160                    sequence_end: 5,
1161                },
1162                vec![GraphEdge {
1163                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1164                        .try_into()
1165                        .unwrap(),
1166                    source_strand: Strand::Forward,
1167                    target_strand: Strand::Forward,
1168                    chromosome_index: 0,
1169                    phased: 0,
1170                    created_on: 0,
1171                }],
1172            );
1173            graph.add_edge(
1174                GraphNode {
1175                    node_id: HashId::convert_str("3"),
1176                    sequence_start: 0,
1177                    sequence_end: 5,
1178                },
1179                GraphNode {
1180                    node_id: HashId::convert_str("4"),
1181                    sequence_start: 3,
1182                    sequence_end: 5,
1183                },
1184                vec![GraphEdge {
1185                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1186                        .try_into()
1187                        .unwrap(),
1188                    source_strand: Strand::Forward,
1189                    target_strand: Strand::Forward,
1190                    chromosome_index: 0,
1191                    phased: 0,
1192                    created_on: 0,
1193                }],
1194            );
1195            graph.add_edge(
1196                GraphNode {
1197                    node_id: HashId::convert_str("4"),
1198                    sequence_start: 3,
1199                    sequence_end: 5,
1200                },
1201                GraphNode {
1202                    node_id: HashId::convert_str("5"),
1203                    sequence_start: 0,
1204                    sequence_end: 3,
1205                },
1206                vec![GraphEdge {
1207                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1208                        .try_into()
1209                        .unwrap(),
1210                    source_strand: Strand::Forward,
1211                    target_strand: Strand::Forward,
1212                    chromosome_index: 0,
1213                    phased: 0,
1214                    created_on: 0,
1215                }],
1216            );
1217            graph.add_edge(
1218                GraphNode {
1219                    node_id: HashId::convert_str("5"),
1220                    sequence_start: 0,
1221                    sequence_end: 3,
1222                },
1223                GraphNode {
1224                    node_id: HashId::convert_str("3"),
1225                    sequence_start: 5,
1226                    sequence_end: 10,
1227                },
1228                vec![GraphEdge {
1229                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1230                        .try_into()
1231                        .unwrap(),
1232                    source_strand: Strand::Forward,
1233                    target_strand: Strand::Forward,
1234                    chromosome_index: 0,
1235                    phased: 0,
1236                    created_on: 0,
1237                }],
1238            );
1239            graph.add_edge(
1240                GraphNode {
1241                    node_id: HashId::convert_str("3"),
1242                    sequence_start: 0,
1243                    sequence_end: 5,
1244                },
1245                GraphNode {
1246                    node_id: HashId::convert_str("3"),
1247                    sequence_start: 5,
1248                    sequence_end: 10,
1249                },
1250                vec![GraphEdge {
1251                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1252                        .try_into()
1253                        .unwrap(),
1254                    source_strand: Strand::Forward,
1255                    target_strand: Strand::Forward,
1256                    chromosome_index: 0,
1257                    phased: 0,
1258                    created_on: 0,
1259                }],
1260            );
1261
1262            graph.add_edge(
1263                GraphNode {
1264                    node_id: HashId::convert_str("3"),
1265                    sequence_start: 5,
1266                    sequence_end: 10,
1267                },
1268                GraphNode {
1269                    node_id: HashId::convert_str("3"),
1270                    sequence_start: 10,
1271                    sequence_end: 15,
1272                },
1273                vec![GraphEdge {
1274                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1275                        .try_into()
1276                        .unwrap(),
1277                    source_strand: Strand::Forward,
1278                    target_strand: Strand::Forward,
1279                    chromosome_index: 0,
1280                    phased: 0,
1281                    created_on: 0,
1282                }],
1283            );
1284
1285            graph.add_edge(
1286                GraphNode {
1287                    node_id: HashId::convert_str("3"),
1288                    sequence_start: 5,
1289                    sequence_end: 10,
1290                },
1291                GraphNode {
1292                    node_id: HashId::convert_str("6"),
1293                    sequence_start: 0,
1294                    sequence_end: 3,
1295                },
1296                vec![GraphEdge {
1297                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1298                        .try_into()
1299                        .unwrap(),
1300                    source_strand: Strand::Forward,
1301                    target_strand: Strand::Forward,
1302                    chromosome_index: 0,
1303                    phased: 0,
1304                    created_on: 0,
1305                }],
1306            );
1307
1308            graph.add_edge(
1309                GraphNode {
1310                    node_id: HashId::convert_str("6"),
1311                    sequence_start: 0,
1312                    sequence_end: 3,
1313                },
1314                GraphNode {
1315                    node_id: HashId::convert_str("6"),
1316                    sequence_start: 3,
1317                    sequence_end: 7,
1318                },
1319                vec![GraphEdge {
1320                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1321                        .try_into()
1322                        .unwrap(),
1323                    source_strand: Strand::Forward,
1324                    target_strand: Strand::Forward,
1325                    chromosome_index: 0,
1326                    phased: 0,
1327                    created_on: 0,
1328                }],
1329            );
1330
1331            graph.add_edge(
1332                GraphNode {
1333                    node_id: HashId::convert_str("6"),
1334                    sequence_start: 0,
1335                    sequence_end: 3,
1336                },
1337                GraphNode {
1338                    node_id: HashId::convert_str("7"),
1339                    sequence_start: 0,
1340                    sequence_end: 2,
1341                },
1342                vec![GraphEdge {
1343                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1344                        .try_into()
1345                        .unwrap(),
1346                    source_strand: Strand::Forward,
1347                    target_strand: Strand::Forward,
1348                    chromosome_index: 0,
1349                    phased: 0,
1350                    created_on: 0,
1351                }],
1352            );
1353            graph.add_edge(
1354                GraphNode {
1355                    node_id: HashId::convert_str("7"),
1356                    sequence_start: 0,
1357                    sequence_end: 2,
1358                },
1359                GraphNode {
1360                    node_id: HashId::convert_str("6"),
1361                    sequence_start: 3,
1362                    sequence_end: 7,
1363                },
1364                vec![GraphEdge {
1365                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1366                        .try_into()
1367                        .unwrap(),
1368                    source_strand: Strand::Forward,
1369                    target_strand: Strand::Forward,
1370                    chromosome_index: 0,
1371                    phased: 0,
1372                    created_on: 0,
1373                }],
1374            );
1375
1376            graph.add_edge(
1377                GraphNode {
1378                    node_id: HashId::convert_str("6"),
1379                    sequence_start: 3,
1380                    sequence_end: 7,
1381                },
1382                GraphNode {
1383                    node_id: HashId::convert_str("3"),
1384                    sequence_start: 10,
1385                    sequence_end: 15,
1386                },
1387                vec![GraphEdge {
1388                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1389                        .try_into()
1390                        .unwrap(),
1391                    source_strand: Strand::Forward,
1392                    target_strand: Strand::Forward,
1393                    chromosome_index: 0,
1394                    phased: 0,
1395                    created_on: 0,
1396                }],
1397            );
1398
1399            graph.add_edge(
1400                GraphNode {
1401                    node_id: HashId::convert_str("3"),
1402                    sequence_start: 10,
1403                    sequence_end: 15,
1404                },
1405                GraphNode {
1406                    node_id: PATH_END_NODE_ID,
1407                    sequence_start: 0,
1408                    sequence_end: 0,
1409                },
1410                vec![GraphEdge {
1411                    edge_id: "0000000000000000000000000000000000000000000000000000000000000000"
1412                        .try_into()
1413                        .unwrap(),
1414                    source_strand: Strand::Forward,
1415                    target_strand: Strand::Forward,
1416                    chromosome_index: 0,
1417                    phased: 0,
1418                    created_on: 0,
1419                }],
1420            );
1421            let path_blocks = vec![
1422                PathBlock {
1423                    node_id: PATH_START_NODE_ID,
1424                    block_sequence: "".to_string(),
1425                    sequence_start: 0,
1426                    sequence_end: 0,
1427                    path_start: 0,
1428                    path_end: 0,
1429                    strand: Strand::Forward,
1430                },
1431                PathBlock {
1432                    node_id: HashId::convert_str("3"),
1433                    block_sequence: "".to_string(),
1434                    sequence_start: 0,
1435                    sequence_end: 5,
1436                    path_start: 0,
1437                    path_end: 5,
1438                    strand: Strand::Forward,
1439                },
1440                PathBlock {
1441                    node_id: HashId::convert_str("3"),
1442                    block_sequence: "".to_string(),
1443                    sequence_start: 5,
1444                    sequence_end: 10,
1445                    path_start: 5,
1446                    path_end: 10,
1447                    strand: Strand::Forward,
1448                },
1449                PathBlock {
1450                    node_id: HashId::convert_str("6"),
1451                    block_sequence: "".to_string(),
1452                    sequence_start: 0,
1453                    sequence_end: 7,
1454                    path_start: 10,
1455                    path_end: 17,
1456                    strand: Strand::Forward,
1457                },
1458                PathBlock {
1459                    node_id: HashId::convert_str("3"),
1460                    block_sequence: "".to_string(),
1461                    sequence_start: 10,
1462                    sequence_end: 15,
1463                    path_start: 17,
1464                    path_end: 23,
1465                    strand: Strand::Forward,
1466                },
1467                PathBlock {
1468                    node_id: PATH_END_NODE_ID,
1469                    block_sequence: "".to_string(),
1470                    sequence_start: 0,
1471                    sequence_end: 0,
1472                    path_start: 0,
1473                    path_end: 0,
1474                    strand: Strand::Forward,
1475                },
1476            ];
1477            let projection = project_path(&graph, &path_blocks);
1478            assert_eq!(
1479                projection,
1480                [
1481                    (
1482                        GraphNode {
1483                            node_id: PATH_START_NODE_ID,
1484                            sequence_start: 0,
1485                            sequence_end: 0
1486                        },
1487                        Strand::Forward
1488                    ),
1489                    (
1490                        GraphNode {
1491                            node_id: HashId::convert_str("3"),
1492                            sequence_start: 0,
1493                            sequence_end: 5
1494                        },
1495                        Strand::Forward
1496                    ),
1497                    (
1498                        GraphNode {
1499                            node_id: HashId::convert_str("3"),
1500                            sequence_start: 5,
1501                            sequence_end: 10
1502                        },
1503                        Strand::Forward
1504                    ),
1505                    (
1506                        GraphNode {
1507                            node_id: HashId::convert_str("6"),
1508                            sequence_start: 0,
1509                            sequence_end: 3
1510                        },
1511                        Strand::Forward
1512                    ),
1513                    (
1514                        GraphNode {
1515                            node_id: HashId::convert_str("6"),
1516                            sequence_start: 3,
1517                            sequence_end: 7
1518                        },
1519                        Strand::Forward
1520                    ),
1521                    (
1522                        GraphNode {
1523                            node_id: HashId::convert_str("3"),
1524                            sequence_start: 10,
1525                            sequence_end: 15
1526                        },
1527                        Strand::Forward
1528                    ),
1529                    (
1530                        GraphNode {
1531                            node_id: PATH_END_NODE_ID,
1532                            sequence_start: 0,
1533                            sequence_end: 0
1534                        },
1535                        Strand::Forward
1536                    )
1537                ]
1538            )
1539        }
1540    }
1541}