1use core::fmt::Debug;
2
3use bevy_utils::{HashMap, HashSet};
4use fixedbitset::FixedBitSet;
5use petgraph::{algo::TarjanScc, graphmap::NodeTrait, prelude::*};
6
7use crate::schedule::set::*;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub enum NodeId {
14 System(usize),
16 Set(usize),
18}
19
20impl NodeId {
21 pub(crate) fn index(&self) -> usize {
23 match self {
24 NodeId::System(index) | NodeId::Set(index) => *index,
25 }
26 }
27
28 pub const fn is_system(&self) -> bool {
30 matches!(self, NodeId::System(_))
31 }
32
33 pub const fn is_set(&self) -> bool {
35 matches!(self, NodeId::Set(_))
36 }
37}
38
39#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
41pub(crate) enum DependencyKind {
42 Before,
44 After,
46 BeforeNoSync,
48 AfterNoSync,
50}
51
52#[derive(Clone)]
54pub(crate) struct Dependency {
55 pub(crate) kind: DependencyKind,
56 pub(crate) set: InternedSystemSet,
57}
58
59impl Dependency {
60 pub fn new(kind: DependencyKind, set: InternedSystemSet) -> Self {
61 Self { kind, set }
62 }
63}
64
65#[derive(Clone, Debug, Default)]
67pub(crate) enum Ambiguity {
68 #[default]
69 Check,
70 IgnoreWithSet(Vec<InternedSystemSet>),
72 IgnoreAll,
74}
75
76#[derive(Clone, Default)]
78pub(crate) struct GraphInfo {
79 pub(crate) hierarchy: Vec<InternedSystemSet>,
81 pub(crate) dependencies: Vec<Dependency>,
83 pub(crate) ambiguous_with: Ambiguity,
84}
85
86pub(crate) fn index(row: usize, col: usize, num_cols: usize) -> usize {
88 debug_assert!(col < num_cols);
89 (row * num_cols) + col
90}
91
92pub(crate) fn row_col(index: usize, num_cols: usize) -> (usize, usize) {
94 (index / num_cols, index % num_cols)
95}
96
97pub(crate) struct CheckGraphResults<V> {
99 pub(crate) reachable: FixedBitSet,
101 pub(crate) connected: HashSet<(V, V)>,
103 pub(crate) disconnected: Vec<(V, V)>,
105 pub(crate) transitive_edges: Vec<(V, V)>,
107 pub(crate) transitive_reduction: DiGraphMap<V, ()>,
109 #[allow(dead_code)]
112 pub(crate) transitive_closure: DiGraphMap<V, ()>,
113}
114
115impl<V: NodeTrait + Debug> Default for CheckGraphResults<V> {
116 fn default() -> Self {
117 Self {
118 reachable: FixedBitSet::new(),
119 connected: HashSet::new(),
120 disconnected: Vec::new(),
121 transitive_edges: Vec::new(),
122 transitive_reduction: DiGraphMap::new(),
123 transitive_closure: DiGraphMap::new(),
124 }
125 }
126}
127
128pub(crate) fn check_graph<V>(
140 graph: &DiGraphMap<V, ()>,
141 topological_order: &[V],
142) -> CheckGraphResults<V>
143where
144 V: NodeTrait + Debug,
145{
146 if graph.node_count() == 0 {
147 return CheckGraphResults::default();
148 }
149
150 let n = graph.node_count();
151
152 let mut map = HashMap::with_capacity(n);
154 let mut topsorted = DiGraphMap::<V, ()>::new();
155 for (i, &node) in topological_order.iter().enumerate() {
157 map.insert(node, i);
158 topsorted.add_node(node);
159 for pred in graph.neighbors_directed(node, Incoming) {
161 topsorted.add_edge(pred, node, ());
162 }
163 }
164
165 let mut reachable = FixedBitSet::with_capacity(n * n);
166 let mut connected = HashSet::new();
167 let mut disconnected = Vec::new();
168
169 let mut transitive_edges = Vec::new();
170 let mut transitive_reduction = DiGraphMap::<V, ()>::new();
171 let mut transitive_closure = DiGraphMap::<V, ()>::new();
172
173 let mut visited = FixedBitSet::with_capacity(n);
174
175 for node in topsorted.nodes() {
177 transitive_reduction.add_node(node);
178 transitive_closure.add_node(node);
179 }
180
181 for a in topsorted.nodes().rev() {
183 let index_a = *map.get(&a).unwrap();
184 for b in topsorted.neighbors_directed(a, Outgoing) {
186 let index_b = *map.get(&b).unwrap();
187 debug_assert!(index_a < index_b);
188 if !visited[index_b] {
189 transitive_reduction.add_edge(a, b, ());
191 transitive_closure.add_edge(a, b, ());
192 reachable.insert(index(index_a, index_b, n));
193
194 let successors = transitive_closure
195 .neighbors_directed(b, Outgoing)
196 .collect::<Vec<_>>();
197 for c in successors {
198 let index_c = *map.get(&c).unwrap();
199 debug_assert!(index_b < index_c);
200 if !visited[index_c] {
201 visited.insert(index_c);
202 transitive_closure.add_edge(a, c, ());
203 reachable.insert(index(index_a, index_c, n));
204 }
205 }
206 } else {
207 transitive_edges.push((a, b));
209 }
210 }
211
212 visited.clear();
213 }
214
215 for i in 0..(n - 1) {
217 for index in index(i, i + 1, n)..=index(i, n - 1, n) {
219 let (a, b) = row_col(index, n);
220 let pair = (topological_order[a], topological_order[b]);
221 if reachable[index] {
222 connected.insert(pair);
223 } else {
224 disconnected.push(pair);
225 }
226 }
227 }
228
229 CheckGraphResults {
235 reachable,
236 connected,
237 disconnected,
238 transitive_edges,
239 transitive_reduction,
240 transitive_closure,
241 }
242}
243
244pub fn simple_cycles_in_component<N>(graph: &DiGraphMap<N, ()>, scc: &[N]) -> Vec<Vec<N>>
251where
252 N: NodeTrait + Debug,
253{
254 let mut cycles = vec![];
255 let mut sccs = vec![scc.to_vec()];
256
257 while let Some(mut scc) = sccs.pop() {
258 let mut subgraph = DiGraphMap::new();
260 for &node in &scc {
261 subgraph.add_node(node);
262 }
263
264 for &node in &scc {
265 for successor in graph.neighbors(node) {
266 if subgraph.contains_node(successor) {
267 subgraph.add_edge(node, successor, ());
268 }
269 }
270 }
271
272 let mut path = Vec::with_capacity(subgraph.node_count());
274 let mut blocked = HashSet::with_capacity(subgraph.node_count());
276 let mut unblock_together: HashMap<N, HashSet<N>> =
279 HashMap::with_capacity(subgraph.node_count());
280 let mut unblock_stack = Vec::with_capacity(subgraph.node_count());
282 let mut maybe_in_more_cycles: HashSet<N> = HashSet::with_capacity(subgraph.node_count());
284 let mut stack = Vec::with_capacity(subgraph.node_count());
286
287 let root = scc.pop().unwrap();
289 path.clear();
291 path.push(root);
292 blocked.insert(root);
294
295 stack.clear();
297 stack.push((root, subgraph.neighbors(root)));
298 while !stack.is_empty() {
299 let (ref node, successors) = stack.last_mut().unwrap();
300 if let Some(next) = successors.next() {
301 if next == root {
302 maybe_in_more_cycles.extend(path.iter());
304 cycles.push(path.clone());
305 } else if !blocked.contains(&next) {
306 maybe_in_more_cycles.remove(&next);
308 path.push(next);
309 blocked.insert(next);
310 stack.push((next, subgraph.neighbors(next)));
311 continue;
312 } else {
313 }
315 }
316
317 if successors.peekable().peek().is_none() {
318 if maybe_in_more_cycles.contains(node) {
319 unblock_stack.push(*node);
320 while let Some(n) = unblock_stack.pop() {
322 if blocked.remove(&n) {
323 let unblock_predecessors = unblock_together.entry(n).or_default();
324 unblock_stack.extend(unblock_predecessors.iter());
325 unblock_predecessors.clear();
326 }
327 }
328 } else {
329 for successor in subgraph.neighbors(*node) {
331 unblock_together.entry(successor).or_default().insert(*node);
332 }
333 }
334
335 path.pop();
337 stack.pop();
338 }
339 }
340
341 subgraph.remove_node(root);
343
344 let mut tarjan_scc = TarjanScc::new();
346 tarjan_scc.run(&subgraph, |scc| {
347 if scc.len() > 1 {
348 sccs.push(scc.to_vec());
349 }
350 });
351 }
352
353 cycles
354}