digraph_rs/analyzer/
fs.rs

1use graphviz_rust::{
2    attributes::{color_name, NodeAttributes},
3    dot_structures::Stmt,
4};
5
6use crate::{
7    analyzer::SearchRes,
8    visualizer::dot::{DotProcessor, ToStringProcessor},
9};
10use std::collections::VecDeque;
11use std::fmt::Debug;
12use std::hash::Hash;
13
14use super::visit::{Visited, VisitedSet};
15use crate::DiGraph;
16
17struct DFS<'a, NId, NL, EL>
18where
19    NId: Eq + Hash,
20{
21    graph: &'a DiGraph<NId, NL, EL>,
22}
23
24impl<'a, NId, NL, EL> DFS<'a, NId, NL, EL>
25where
26    NId: Eq + Hash + Clone,
27{
28    fn new(graph: &'a DiGraph<NId, NL, EL>) -> Self {
29        Self { graph }
30    }
31    pub fn search_by_eq(&self, start: &'a NId, target: &'a NId) -> Option<&'a NId> {
32        self.search(start, |n| {
33            if target == n {
34                SearchRes::Find
35            } else {
36                SearchRes::Next
37            }
38        })
39    }
40    fn search<S>(&self, start: &'a NId, target: S) -> Option<&'a NId>
41    where
42        S: Fn(&'a NId) -> SearchRes,
43    {
44        let mut visited = VisitedSet::default();
45        let mut q = vec![];
46
47        q.push(start);
48
49        while let Some(node) = q.pop() {
50            visited.visit(node);
51            match target(node) {
52                SearchRes::Next => {
53                    for nexts in self.graph.successors(node.clone()) {
54                        for s in nexts.keys() {
55                            if !visited.already_visited(s) {
56                                q.push(s)
57                            }
58                        }
59                    }
60                }
61                SearchRes::Skip => (),
62                SearchRes::Find => return Some(node),
63                SearchRes::Stop => return None,
64            }
65        }
66
67        None
68    }
69}
70
71struct BFS<'a, NId, NL, EL>
72where
73    NId: Eq + Hash,
74{
75    graph: &'a DiGraph<NId, NL, EL>,
76}
77
78impl<'a, NId, NL, EL> BFS<'a, NId, NL, EL>
79where
80    NId: Eq + Hash + Clone,
81{
82    fn new(graph: &'a DiGraph<NId, NL, EL>) -> Self {
83        BFS { graph }
84    }
85
86    pub fn search_by_eq(&self, start: &'a NId, target: &'a NId) -> Option<&'a NId> {
87        self.search(start, |n| {
88            if target == n {
89                SearchRes::Find
90            } else {
91                SearchRes::Next
92            }
93        })
94    }
95    fn search<S>(&self, start: &'a NId, target: S) -> Option<&'a NId>
96    where
97        S: Fn(&'a NId) -> SearchRes,
98    {
99        let mut visited = VisitedSet::default();
100        let mut q = VecDeque::new();
101
102        q.push_back(start);
103
104        while let Some(node) = q.pop_front() {
105            visited.visit(node);
106            match target(node) {
107                SearchRes::Next => {
108                    for nexts in self.graph.successors(node.clone()) {
109                        for s in nexts.keys() {
110                            if !visited.already_visited(s) {
111                                q.push_back(s)
112                            }
113                        }
114                    }
115                }
116                SearchRes::Skip => (),
117                SearchRes::Find => return Some(node),
118                SearchRes::Stop => return None,
119            }
120        }
121
122        None
123    }
124}
125
126struct SrcTrgHighlighter<'a, NId>
127where
128    NId: Eq + Hash + Clone + ToString,
129{
130    src: &'a NId,
131    trg: &'a NId,
132    delegate: ToStringProcessor,
133}
134
135impl<'a, NId> SrcTrgHighlighter<'a, NId>
136where
137    NId: Eq + Hash + Clone + ToString,
138{
139    fn new(src: &'a NId, trg: &'a NId) -> Self {
140        Self {
141            src,
142            trg,
143            delegate: ToStringProcessor {},
144        }
145    }
146}
147
148impl<'a, NId, NL, EL> DotProcessor<'a, NId, NL, EL> for SrcTrgHighlighter<'a, NId>
149where
150    NId: Eq + Hash + Clone + ToString,
151    EL: ToString,
152    NL: ToString,
153{
154    fn node(&self, id: &'a NId, nl: &'a NL) -> Stmt {
155        if id != self.src && id != self.trg {
156            (&self.delegate as &dyn DotProcessor<NId, NL, EL>).node(id, nl)
157        } else {
158            let green = NodeAttributes::color(color_name::green);
159            let bold = NodeAttributes::style("bold".to_string());
160            self.delegate.node_with_attrs(id, nl, vec![green, bold])
161        }
162    }
163
164    fn edge(&self, from: &'a NId, to: &'a NId, el: &'a EL) -> Stmt {
165        (&self.delegate as &dyn DotProcessor<NId, NL, EL>).edge(from, to, el)
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use crate::analyzer::fs::{SearchRes, SrcTrgHighlighter, BFS, DFS};
172    use crate::DiGraph;
173    use crate::EmptyPayload;
174    use crate::{digraph, extend_edges, extend_nodes};
175
176    #[test]
177    fn bfs_simple_test() {
178        let graph = digraph!((usize,_,_) => [1,2,3,4,5,6,7,8,9,10] => {
179            1 => 2;
180            2 => [3,4,5];
181            3 => 6;
182            6 => [1,7,8];
183            8 => 9;
184            9 => [1,10];
185        });
186
187        let res = graph.visualize().str_to_dot_file("dots/bfs.svg");
188        assert!(res.is_ok());
189
190        let bfs = BFS::new(&graph);
191
192        let res = bfs.search_by_eq(&1, &10);
193        assert_eq!(res, Some(&10))
194    }
195
196    #[test]
197    fn simple_test2() {
198        let graph = digraph!((usize,_,_) => [1,2,3,4,5,6,7,8,9,10] => {
199            1 => 2;
200            2 => [3,4,5];
201            3 => 6;
202            6 => [1,7,8];
203            8 => 9;
204            9 => [1,10];
205        });
206
207        let res = graph.visualize().str_to_dot_file("dots/bfs.svg");
208        assert!(res.is_ok());
209
210        let dfs = DFS::new(&graph);
211
212        let res = dfs.search_by_eq(&1, &10);
213        assert_eq!(res, Some(&10))
214    }
215    #[test]
216    fn cap_test() {
217        let graph = digraph!((usize,_,_) => [1,2,3,4,5,6,7,8,9,10] => {
218            1 => 2;
219            2 => [3,4,5];
220            3 => 6;
221            6 => [1,7,8];
222            8 => 9;
223            9 => [1,10];
224        });
225
226        let res = graph.visualize().str_to_dot_file("dots/bfs.svg");
227        assert!(res.is_ok());
228
229        let dfs = DFS::new(&graph);
230        let bfs = BFS::new(&graph);
231
232        let res = dfs.search(&1, move |n| {
233            if &10 == n {
234                SearchRes::Find
235            } else {
236                println!("n = {}", n);
237                SearchRes::Next
238            }
239        });
240        assert_eq!(res, Some(&10));
241
242        println!("bfs");
243
244        let res = bfs.search(&1, move |n| {
245            if &10 == n {
246                SearchRes::Find
247            } else {
248                println!("n = {}", n);
249                SearchRes::Next
250            }
251        });
252        assert_eq!(res, Some(&10));
253        graph
254            .visualize()
255            .to_dot_file("dots/test.svg", SrcTrgHighlighter::new(&1, &10));
256    }
257}