1use std::collections::{BTreeMap, BTreeSet};
13
14use crate::id::{GraphEdgeId, GraphId, GraphNodeId, index_of, position};
15
16use super::index::{AnalysisContext, SelectedAdjacency, SelectedIndexes};
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct ComponentIdKind;
21
22pub type GraphComponentId = GraphId<ComponentIdKind>;
27
28pub struct GraphComponent {
30 id: GraphComponentId,
31 members: Box<[GraphNodeId]>,
32 cyclic: bool,
33}
34
35impl GraphComponent {
36 pub fn id(&self) -> GraphComponentId {
38 self.id
39 }
40
41 pub fn members(&self) -> &[GraphNodeId] {
43 &self.members
44 }
45
46 pub fn is_cyclic(&self) -> bool {
49 self.cyclic
50 }
51}
52
53pub struct GraphComponents {
59 components: Box<[GraphComponent]>,
60 membership: Box<[Option<GraphComponentId>]>,
61}
62
63impl GraphComponents {
64 pub fn components(&self) -> &[GraphComponent] {
66 &self.components
67 }
68
69 pub fn component(&self, id: GraphComponentId) -> Option<&GraphComponent> {
71 self.components.get(index_of(id.index()))
72 }
73
74 pub fn component_of(&self, node: GraphNodeId) -> Option<GraphComponentId> {
76 self.membership
77 .get(index_of(node.index()))
78 .copied()
79 .flatten()
80 }
81}
82
83pub struct CondensationEdge {
89 source: GraphComponentId,
90 target: GraphComponentId,
91 edges: Box<[GraphEdgeId]>,
92}
93
94impl CondensationEdge {
95 pub fn source(&self) -> GraphComponentId {
97 self.source
98 }
99
100 pub fn target(&self) -> GraphComponentId {
102 self.target
103 }
104
105 pub fn edges(&self) -> &[GraphEdgeId] {
107 &self.edges
108 }
109}
110
111pub struct CondensationGraph {
113 components: GraphComponents,
114 edges: Box<[CondensationEdge]>,
115 order: Box<[GraphComponentId]>,
116}
117
118impl CondensationGraph {
119 pub fn components(&self) -> &GraphComponents {
121 &self.components
122 }
123
124 pub fn edges(&self) -> &[CondensationEdge] {
126 &self.edges
127 }
128
129 pub fn topological_order(&self) -> &[GraphComponentId] {
131 &self.order
132 }
133}
134
135pub(crate) fn discover(analysis: &AnalysisContext<'_>) -> GraphComponents {
141 let indexes = analysis.indexes();
142 let finished = finish_order(indexes);
143 assembled(grouped_backwards(indexes, &finished), indexes)
144}
145
146pub(crate) fn condense(analysis: &AnalysisContext<'_>) -> CondensationGraph {
148 let indexes = analysis.indexes();
149 let components = discover(analysis);
150 let edges = coalesced(indexes, &components);
151 let order = ordered(&edges, components.components.len());
152 CondensationGraph {
153 components,
154 edges,
155 order,
156 }
157}
158
159fn finish_order(indexes: &SelectedIndexes) -> Box<[GraphNodeId]> {
161 let width = indexes.node_count();
162 let mut seen: Vec<bool> = vec![false; width];
163 let mut finished: Vec<GraphNodeId> = Vec::with_capacity(width);
164 let mut stack: Vec<(GraphNodeId, usize)> = Vec::new();
165 for at in 0..width {
166 begin(&mut seen, &mut stack, GraphNodeId::new(position(at)));
167 drain(indexes, (&mut seen, &mut stack, &mut finished));
168 }
169 finished.into_boxed_slice()
170}
171
172fn begin(seen: &mut [bool], stack: &mut Vec<(GraphNodeId, usize)>, node: GraphNodeId) {
174 if unclaimed(seen, node) {
175 stack.push((node, 0));
176 }
177}
178
179fn unclaimed(flags: &mut [bool], node: GraphNodeId) -> bool {
184 match flags.get_mut(index_of(node.index())) {
185 Some(slot) if !*slot => {
186 *slot = true;
187 true
188 }
189 _ => false,
190 }
191}
192
193fn drain(
195 indexes: &SelectedIndexes,
196 state: (
197 &mut [bool],
198 &mut Vec<(GraphNodeId, usize)>,
199 &mut Vec<GraphNodeId>,
200 ),
201) {
202 let (seen, stack, finished) = state;
203 while let Some(step) = stack.pop() {
204 advance(indexes, (seen, stack, finished), step);
205 }
206}
207
208fn advance(
210 indexes: &SelectedIndexes,
211 state: (
212 &mut [bool],
213 &mut Vec<(GraphNodeId, usize)>,
214 &mut Vec<GraphNodeId>,
215 ),
216 step: (GraphNodeId, usize),
217) {
218 let (seen, stack, finished) = state;
219 let (node, at) = step;
220 match indexes.outgoing(node).get(at) {
221 Some(entry) => resume(seen, stack, (node, at + 1), entry.node()),
222 None => finished.push(node),
223 }
224}
225
226fn resume(
228 seen: &mut [bool],
229 stack: &mut Vec<(GraphNodeId, usize)>,
230 step: (GraphNodeId, usize),
231 reached: GraphNodeId,
232) {
233 stack.push(step);
234 begin(seen, stack, reached);
235}
236
237fn grouped_backwards(indexes: &SelectedIndexes, finished: &[GraphNodeId]) -> Vec<Vec<GraphNodeId>> {
244 let mut taken: Vec<bool> = vec![false; indexes.node_count()];
245 let mut found: Vec<Vec<GraphNodeId>> = Vec::new();
246 for node in finished.iter().rev() {
247 let members = gathered(indexes, &mut taken, *node);
248 match members.is_empty() {
249 true => (),
250 false => found.push(members),
251 }
252 }
253 found
254}
255
256fn gathered(indexes: &SelectedIndexes, taken: &mut [bool], root: GraphNodeId) -> Vec<GraphNodeId> {
258 let mut members: Vec<GraphNodeId> = Vec::new();
259 let mut stack: Vec<GraphNodeId> = Vec::new();
260 claim(taken, &mut stack, root);
261 while let Some(node) = stack.pop() {
262 members.push(node);
263 for entry in indexes.incoming(node) {
264 claim(taken, &mut stack, entry.node());
265 }
266 }
267 members
268}
269
270fn claim(taken: &mut [bool], stack: &mut Vec<GraphNodeId>, node: GraphNodeId) {
273 if unclaimed(taken, node) {
274 stack.push(node);
275 }
276}
277
278fn assembled(grouped: Vec<Vec<GraphNodeId>>, indexes: &SelectedIndexes) -> GraphComponents {
280 let mut ordered = grouped;
281 for members in &mut ordered {
282 members.sort_unstable();
283 }
284 ordered.sort_unstable_by_key(|members| members.first().copied());
285 let components: Box<[GraphComponent]> = ordered
286 .into_iter()
287 .enumerate()
288 .map(|(at, members)| built(at, members, indexes))
289 .collect();
290 let membership = placed(&components, indexes.node_count());
291 GraphComponents {
292 components,
293 membership,
294 }
295}
296
297fn built(at: usize, members: Vec<GraphNodeId>, indexes: &SelectedIndexes) -> GraphComponent {
302 GraphComponent {
303 id: GraphComponentId::new(position(at)),
304 cyclic: cyclic(&members, indexes),
305 members: members.into_boxed_slice(),
306 }
307}
308
309fn cyclic(members: &[GraphNodeId], indexes: &SelectedIndexes) -> bool {
314 match members {
315 [only] => indexes
316 .outgoing(*only)
317 .iter()
318 .any(|entry| entry.node() == *only),
319 _ => members.len() > 1,
320 }
321}
322
323fn placed(components: &[GraphComponent], width: usize) -> Box<[Option<GraphComponentId>]> {
329 let mut table: Vec<Option<GraphComponentId>> = vec![None; width];
330 for component in components {
331 for member in component.members.iter() {
332 record(&mut table, *member, component.id);
333 }
334 }
335 table.into_boxed_slice()
336}
337
338fn record(table: &mut [Option<GraphComponentId>], node: GraphNodeId, component: GraphComponentId) {
340 if let Some(slot) = table.get_mut(index_of(node.index())) {
341 *slot = Some(component);
342 }
343}
344
345fn coalesced(indexes: &SelectedIndexes, components: &GraphComponents) -> Box<[CondensationEdge]> {
348 let mut crossing: Crossings = BTreeMap::new();
349 for at in 0..indexes.node_count() {
350 cross(
351 indexes,
352 components,
353 &mut crossing,
354 GraphNodeId::new(position(at)),
355 );
356 }
357 crossing.into_iter().map(joined).collect()
358}
359
360type Crossings = BTreeMap<(GraphComponentId, GraphComponentId), Vec<GraphEdgeId>>;
362
363fn cross(
365 indexes: &SelectedIndexes,
366 components: &GraphComponents,
367 crossing: &mut Crossings,
368 node: GraphNodeId,
369) {
370 let source = match components.component_of(node) {
371 Some(found) => found,
372 None => return,
373 };
374 for entry in indexes.outgoing(node) {
375 leaving(components, crossing, (source, *entry));
376 }
377}
378
379fn leaving(
381 components: &GraphComponents,
382 crossing: &mut Crossings,
383 step: (GraphComponentId, SelectedAdjacency),
384) {
385 let (source, entry) = step;
386 let target = match components.component_of(entry.node()) {
387 Some(found) => found,
388 None => return,
389 };
390 match source == target {
391 true => (),
392 false => crossing
393 .entry((source, target))
394 .or_default()
395 .push(entry.edge()),
396 }
397}
398
399fn joined(crossing: ((GraphComponentId, GraphComponentId), Vec<GraphEdgeId>)) -> CondensationEdge {
401 let ((source, target), mut edges) = crossing;
402 edges.sort_unstable();
403 CondensationEdge {
404 source,
405 target,
406 edges: edges.into_boxed_slice(),
407 }
408}
409
410fn ordered(edges: &[CondensationEdge], count: usize) -> Box<[GraphComponentId]> {
416 let mut waiting: Vec<usize> = vec![0; count];
417 for edge in edges {
418 bump(&mut waiting, edge.target);
419 }
420 let mut ready: BTreeSet<GraphComponentId> = (0..count)
421 .map(|at| GraphComponentId::new(position(at)))
422 .filter(|component| waiting.get(index_of(component.index())) == Some(&0))
423 .collect();
424 let mut consumed: Vec<GraphComponentId> = Vec::with_capacity(count);
425 while let Some(least) = ready.pop_first() {
426 consumed.push(least);
427 release(edges, least, (&mut waiting, &mut ready));
428 }
429 consumed.into_boxed_slice()
430}
431
432fn bump(waiting: &mut [usize], component: GraphComponentId) {
434 if let Some(slot) = waiting.get_mut(index_of(component.index())) {
435 *slot += 1;
436 }
437}
438
439fn release(
442 edges: &[CondensationEdge],
443 consumed: GraphComponentId,
444 state: (&mut [usize], &mut BTreeSet<GraphComponentId>),
445) {
446 let (waiting, ready) = state;
447 ready.extend(
448 leaving_pairs(edges, consumed)
449 .iter()
450 .filter_map(|edge| arrived(waiting, edge.target)),
451 );
452}
453
454fn leaving_pairs(edges: &[CondensationEdge], source: GraphComponentId) -> &[CondensationEdge] {
462 let start = edges.partition_point(|edge| edge.source < source);
463 let end = edges.partition_point(|edge| edge.source <= source);
464 &edges[start..end]
465}
466
467fn arrived(waiting: &mut [usize], component: GraphComponentId) -> Option<GraphComponentId> {
474 let slot = waiting.get_mut(index_of(component.index()))?;
475 *slot = slot.checked_sub(1)?;
476 match *slot {
477 0 => Some(component),
478 _ => None,
479 }
480}