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 Some(neighbor) = graph.opposite(edge, node) else {
198 continue;
199 };
200 if neighbor == node {
201 let edge_slot = G::edge_slot(edge);
202 if !self.seen_self_loop[edge_slot] {
203 self.seen_self_loop[edge_slot] = true;
204 self.back_edges[G::node_slot(node)].push(BackEdge {
205 descendant: node,
206 edge,
207 });
208 }
209 continue;
210 }
211 let slot = G::node_slot(node);
212 let neighbor_slot = G::node_slot(neighbor);
213 if self.discovery[neighbor_slot].is_none() {
214 self.discover(neighbor, Some(node), Some(edge));
215 frames.push(Frame {
216 node: neighbor,
217 parent_edge: Some(edge),
218 next: 0,
219 });
220 } else if self.discovery[neighbor_slot] < self.discovery[slot] {
221 self.back_edges[neighbor_slot].push(BackEdge {
222 descendant: node,
223 edge,
224 });
225 }
226 }
227 }
228
229 fn discover(&mut self, node: G::Node, parent: Option<G::Node>, edge: Option<G::Edge>) {
230 let slot = G::node_slot(node);
231 self.discovery[slot] = Some(self.order.len());
232 self.parent_node[slot] = parent;
233 self.parent_edge[slot] = edge;
234 self.order.push(node);
235 }
236
237 fn build_chains(self) -> ChainDecomposition<G::Node, G::Edge> {
238 let mut visited = vec![false; self.discovery.len()];
239 let mut chains = Vec::new();
240 for ancestor in self.order {
241 visited[G::node_slot(ancestor)] = true;
242 'back_edges: for back in &self.back_edges[G::node_slot(ancestor)] {
243 let mut chain = vec![ChainStep {
244 edge: back.edge,
245 source: ancestor,
246 target: back.descendant,
247 }];
248 let mut cursor = back.descendant;
249 while !visited[G::node_slot(cursor)] {
250 visited[G::node_slot(cursor)] = true;
251 let slot = G::node_slot(cursor);
252 let Some((parent, edge)) = self.parent_node[slot].zip(self.parent_edge[slot])
253 else {
254 continue 'back_edges;
255 };
256 chain.push(ChainStep {
257 edge,
258 source: cursor,
259 target: parent,
260 });
261 cursor = parent;
262 }
263 chains.push(chain);
264 }
265 }
266 ChainDecomposition { chains }
267 }
268}