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