1use super::undirected_snapshot::UndirectedSnapshot;
2use crate::{IndexUndirectedGraphView, Vec};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct ChainStep<Node, Edge> {
7 edge: Edge,
8 source: Node,
9 target: Node,
10}
11
12impl<Node: Copy, Edge: Copy> ChainStep<Node, Edge> {
13 #[must_use]
15 pub const fn edge(&self) -> Edge {
16 self.edge
17 }
18
19 #[must_use]
21 pub const fn source(&self) -> Node {
22 self.source
23 }
24
25 #[must_use]
27 pub const fn target(&self) -> Node {
28 self.target
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct ChainDecomposition<Node, Edge> {
35 chains: Vec<Vec<ChainStep<Node, Edge>>>,
36}
37
38impl<Node, Edge> ChainDecomposition<Node, Edge> {
39 #[must_use]
41 pub fn chains(&self) -> &[Vec<ChainStep<Node, Edge>>] {
42 &self.chains
43 }
44
45 #[must_use]
47 pub fn chain_count(&self) -> usize {
48 self.chains.len()
49 }
50
51 #[must_use]
53 pub fn into_chains(self) -> Vec<Vec<ChainStep<Node, Edge>>> {
54 self.chains
55 }
56}
57
58#[must_use]
63pub fn chain_decomposition<G>(graph: &G) -> ChainDecomposition<G::Node, G::Edge>
64where
65 G: IndexUndirectedGraphView,
66{
67 chain_decomposition_filtered(graph, |_| true)
68}
69
70#[must_use]
74pub fn chain_decomposition_filtered<G, F>(
75 graph: &G,
76 allows_edge: F,
77) -> ChainDecomposition<G::Node, G::Edge>
78where
79 G: IndexUndirectedGraphView,
80 F: Fn(G::Edge) -> bool,
81{
82 let snapshot = UndirectedSnapshot::new(graph, allows_edge);
83 let roots = snapshot.nodes().to_vec();
84 decompose(graph, &snapshot, &roots)
85}
86
87#[must_use]
91pub fn chain_decomposition_from<G>(
92 graph: &G,
93 source: G::Node,
94) -> Option<ChainDecomposition<G::Node, G::Edge>>
95where
96 G: IndexUndirectedGraphView,
97{
98 chain_decomposition_from_filtered(graph, source, |_| true)
99}
100
101#[must_use]
106pub fn chain_decomposition_from_filtered<G, F>(
107 graph: &G,
108 source: G::Node,
109 allows_edge: F,
110) -> Option<ChainDecomposition<G::Node, G::Edge>>
111where
112 G: IndexUndirectedGraphView,
113 F: Fn(G::Edge) -> bool,
114{
115 if !graph.contains_node(source) {
116 return None;
117 }
118 let snapshot = UndirectedSnapshot::new(graph, allows_edge);
119 Some(decompose(graph, &snapshot, &[source]))
120}
121
122fn decompose<G>(
123 graph: &G,
124 snapshot: &UndirectedSnapshot<G>,
125 roots: &[G::Node],
126) -> ChainDecomposition<G::Node, G::Edge>
127where
128 G: IndexUndirectedGraphView,
129{
130 let mut forest = DfsForest::<G>::new(graph);
131 for &root in roots {
132 if forest.discovery[G::node_slot(root)].is_none() {
133 forest.search_from(graph, snapshot, root);
134 }
135 }
136 forest.build_chains()
137}
138
139#[derive(Clone, Copy)]
140struct BackEdge<Node, Edge> {
141 descendant: Node,
142 edge: Edge,
143}
144
145struct Frame<Node, Edge> {
146 node: Node,
147 parent_edge: Option<Edge>,
148 next: usize,
149}
150
151struct DfsForest<G>
152where
153 G: IndexUndirectedGraphView,
154{
155 discovery: Vec<Option<usize>>,
156 parent_node: Vec<Option<G::Node>>,
157 parent_edge: Vec<Option<G::Edge>>,
158 order: Vec<G::Node>,
159 back_edges: Vec<Vec<BackEdge<G::Node, G::Edge>>>,
160 seen_self_loop: Vec<bool>,
161}
162
163impl<G> DfsForest<G>
164where
165 G: IndexUndirectedGraphView,
166{
167 fn new(graph: &G) -> Self {
168 Self {
169 discovery: vec![None; graph.node_bound()],
170 parent_node: vec![None; graph.node_bound()],
171 parent_edge: vec![None; graph.node_bound()],
172 order: Vec::with_capacity(graph.node_count()),
173 back_edges: (0..graph.node_bound()).map(|_| Vec::new()).collect(),
174 seen_self_loop: vec![false; graph.edge_bound()],
175 }
176 }
177
178 fn search_from(&mut self, graph: &G, snapshot: &UndirectedSnapshot<G>, root: G::Node) {
179 self.discover(root, None, None);
180 let mut frames = vec![Frame {
181 node: root,
182 parent_edge: None,
183 next: 0,
184 }];
185 while let Some(frame) = frames.last_mut() {
186 let incident = snapshot.incident(frame.node);
187 if frame.next == incident.len() {
188 frames.pop();
189 continue;
190 }
191 let edge = incident[frame.next];
192 frame.next += 1;
193 if Some(edge) == frame.parent_edge {
194 continue;
195 }
196 let node = frame.node;
197 let neighbor = graph.opposite(edge, node).expect("incident edge has node");
198 if neighbor == node {
199 let edge_slot = G::edge_slot(edge);
200 if !self.seen_self_loop[edge_slot] {
201 self.seen_self_loop[edge_slot] = true;
202 self.back_edges[G::node_slot(node)].push(BackEdge {
203 descendant: node,
204 edge,
205 });
206 }
207 continue;
208 }
209 let slot = G::node_slot(node);
210 let neighbor_slot = G::node_slot(neighbor);
211 if self.discovery[neighbor_slot].is_none() {
212 self.discover(neighbor, Some(node), Some(edge));
213 frames.push(Frame {
214 node: neighbor,
215 parent_edge: Some(edge),
216 next: 0,
217 });
218 } else if self.discovery[neighbor_slot] < self.discovery[slot] {
219 self.back_edges[neighbor_slot].push(BackEdge {
220 descendant: node,
221 edge,
222 });
223 }
224 }
225 }
226
227 fn discover(&mut self, node: G::Node, parent: Option<G::Node>, edge: Option<G::Edge>) {
228 let slot = G::node_slot(node);
229 self.discovery[slot] = Some(self.order.len());
230 self.parent_node[slot] = parent;
231 self.parent_edge[slot] = edge;
232 self.order.push(node);
233 }
234
235 fn build_chains(self) -> ChainDecomposition<G::Node, G::Edge> {
236 let mut visited = vec![false; self.discovery.len()];
237 let mut chains = Vec::new();
238 for ancestor in self.order {
239 visited[G::node_slot(ancestor)] = true;
240 for back in &self.back_edges[G::node_slot(ancestor)] {
241 let mut chain = vec![ChainStep {
242 edge: back.edge,
243 source: ancestor,
244 target: back.descendant,
245 }];
246 let mut cursor = back.descendant;
247 while !visited[G::node_slot(cursor)] {
248 visited[G::node_slot(cursor)] = true;
249 let slot = G::node_slot(cursor);
250 let parent = self.parent_node[slot].expect("unvisited descendant has parent");
251 chain.push(ChainStep {
252 edge: self.parent_edge[slot].expect("non-root has parent edge"),
253 source: cursor,
254 target: parent,
255 });
256 cursor = parent;
257 }
258 chains.push(chain);
259 }
260 }
261 ChainDecomposition { chains }
262 }
263}