1use alloc::{boxed::Box, vec, vec::Vec};
2use core::{
3 any::{Any, TypeId},
4 fmt::Debug,
5};
6use smallvec::SmallVec;
7
8use bevy_platform::collections::{HashMap, HashSet};
9use bevy_utils::TypeIdMap;
10
11use fixedbitset::FixedBitSet;
12
13use crate::schedule::set::*;
14
15mod graph_map;
16mod node;
17mod tarjan_scc;
18
19pub use graph_map::{DiGraph, Direction, UnGraph};
20pub use node::NodeId;
21
22#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
24pub(crate) enum DependencyKind {
25 Before,
27 After,
29}
30
31pub(crate) struct Dependency {
33 pub(crate) kind: DependencyKind,
34 pub(crate) set: InternedSystemSet,
35 pub(crate) options: TypeIdMap<Box<dyn Any>>,
36}
37
38impl Dependency {
39 pub fn new(kind: DependencyKind, set: InternedSystemSet) -> Self {
40 Self {
41 kind,
42 set,
43 options: Default::default(),
44 }
45 }
46 pub fn add_config<T: 'static>(mut self, option: T) -> Self {
47 self.options.insert(TypeId::of::<T>(), Box::new(option));
48 self
49 }
50}
51
52#[derive(Clone, Debug, Default)]
54pub(crate) enum Ambiguity {
55 #[default]
56 Check,
57 IgnoreWithSet(Vec<InternedSystemSet>),
59 IgnoreAll,
61}
62
63#[derive(Default)]
65pub struct GraphInfo {
66 pub(crate) hierarchy: Vec<InternedSystemSet>,
68 pub(crate) dependencies: Vec<Dependency>,
70 pub(crate) ambiguous_with: Ambiguity,
71}
72
73pub(crate) fn index(row: usize, col: usize, num_cols: usize) -> usize {
75 debug_assert!(col < num_cols);
76 (row * num_cols) + col
77}
78
79pub(crate) fn row_col(index: usize, num_cols: usize) -> (usize, usize) {
81 (index / num_cols, index % num_cols)
82}
83
84pub(crate) struct CheckGraphResults {
86 pub(crate) reachable: FixedBitSet,
88 pub(crate) connected: HashSet<(NodeId, NodeId)>,
90 pub(crate) disconnected: Vec<(NodeId, NodeId)>,
92 pub(crate) transitive_edges: Vec<(NodeId, NodeId)>,
94 pub(crate) transitive_reduction: DiGraph,
96 #[expect(dead_code, reason = "See the TODO above this attribute.")]
99 pub(crate) transitive_closure: DiGraph,
100}
101
102impl Default for CheckGraphResults {
103 fn default() -> Self {
104 Self {
105 reachable: FixedBitSet::new(),
106 connected: HashSet::default(),
107 disconnected: Vec::new(),
108 transitive_edges: Vec::new(),
109 transitive_reduction: DiGraph::default(),
110 transitive_closure: DiGraph::default(),
111 }
112 }
113}
114
115pub(crate) fn check_graph(graph: &DiGraph, topological_order: &[NodeId]) -> CheckGraphResults {
127 if graph.node_count() == 0 {
128 return CheckGraphResults::default();
129 }
130
131 let n = graph.node_count();
132
133 let mut map = <HashMap<_, _>>::with_capacity_and_hasher(n, Default::default());
135 let mut topsorted = <DiGraph>::default();
136 for (i, &node) in topological_order.iter().enumerate() {
138 map.insert(node, i);
139 topsorted.add_node(node);
140 for pred in graph.neighbors_directed(node, Direction::Incoming) {
142 topsorted.add_edge(pred, node);
143 }
144 }
145
146 let mut reachable = FixedBitSet::with_capacity(n * n);
147 let mut connected = <HashSet<_>>::default();
148 let mut disconnected = Vec::new();
149
150 let mut transitive_edges = Vec::new();
151 let mut transitive_reduction = DiGraph::default();
152 let mut transitive_closure = DiGraph::default();
153
154 let mut visited = FixedBitSet::with_capacity(n);
155
156 for node in topsorted.nodes() {
158 transitive_reduction.add_node(node);
159 transitive_closure.add_node(node);
160 }
161
162 for a in topsorted.nodes().rev() {
164 let index_a = *map.get(&a).unwrap();
165 for b in topsorted.neighbors_directed(a, Direction::Outgoing) {
167 let index_b = *map.get(&b).unwrap();
168 debug_assert!(index_a < index_b);
169 if !visited[index_b] {
170 transitive_reduction.add_edge(a, b);
172 transitive_closure.add_edge(a, b);
173 reachable.insert(index(index_a, index_b, n));
174
175 let successors = transitive_closure
176 .neighbors_directed(b, Direction::Outgoing)
177 .collect::<Vec<_>>();
178 for c in successors {
179 let index_c = *map.get(&c).unwrap();
180 debug_assert!(index_b < index_c);
181 if !visited[index_c] {
182 visited.insert(index_c);
183 transitive_closure.add_edge(a, c);
184 reachable.insert(index(index_a, index_c, n));
185 }
186 }
187 } else {
188 transitive_edges.push((a, b));
190 }
191 }
192
193 visited.clear();
194 }
195
196 for i in 0..(n - 1) {
198 for index in index(i, i + 1, n)..=index(i, n - 1, n) {
200 let (a, b) = row_col(index, n);
201 let pair = (topological_order[a], topological_order[b]);
202 if reachable[index] {
203 connected.insert(pair);
204 } else {
205 disconnected.push(pair);
206 }
207 }
208 }
209
210 CheckGraphResults {
216 reachable,
217 connected,
218 disconnected,
219 transitive_edges,
220 transitive_reduction,
221 transitive_closure,
222 }
223}
224
225pub fn simple_cycles_in_component(graph: &DiGraph, scc: &[NodeId]) -> Vec<Vec<NodeId>> {
232 let mut cycles = vec![];
233 let mut sccs = vec![SmallVec::from_slice(scc)];
234
235 while let Some(mut scc) = sccs.pop() {
236 let mut subgraph = <DiGraph>::default();
238 for &node in &scc {
239 subgraph.add_node(node);
240 }
241
242 for &node in &scc {
243 for successor in graph.neighbors(node) {
244 if subgraph.contains_node(successor) {
245 subgraph.add_edge(node, successor);
246 }
247 }
248 }
249
250 let mut path = Vec::with_capacity(subgraph.node_count());
252 let mut blocked: HashSet<_> =
254 HashSet::with_capacity_and_hasher(subgraph.node_count(), Default::default());
255 let mut unblock_together: HashMap<NodeId, HashSet<NodeId>> =
258 HashMap::with_capacity_and_hasher(subgraph.node_count(), Default::default());
259 let mut unblock_stack = Vec::with_capacity(subgraph.node_count());
261 let mut maybe_in_more_cycles: HashSet<NodeId> =
263 HashSet::with_capacity_and_hasher(subgraph.node_count(), Default::default());
264 let mut stack = Vec::with_capacity(subgraph.node_count());
266
267 let root = scc.pop().unwrap();
269 path.clear();
271 path.push(root);
272 blocked.insert(root);
274
275 stack.clear();
277 stack.push((root, subgraph.neighbors(root)));
278 while !stack.is_empty() {
279 let &mut (ref node, ref mut successors) = stack.last_mut().unwrap();
280 if let Some(next) = successors.next() {
281 if next == root {
282 maybe_in_more_cycles.extend(path.iter());
284 cycles.push(path.clone());
285 } else if !blocked.contains(&next) {
286 maybe_in_more_cycles.remove(&next);
288 path.push(next);
289 blocked.insert(next);
290 stack.push((next, subgraph.neighbors(next)));
291 continue;
292 } else {
293 }
295 }
296
297 if successors.peekable().peek().is_none() {
298 if maybe_in_more_cycles.contains(node) {
299 unblock_stack.push(*node);
300 while let Some(n) = unblock_stack.pop() {
302 if blocked.remove(&n) {
303 let unblock_predecessors = unblock_together.entry(n).or_default();
304 unblock_stack.extend(unblock_predecessors.iter());
305 unblock_predecessors.clear();
306 }
307 }
308 } else {
309 for successor in subgraph.neighbors(*node) {
311 unblock_together.entry(successor).or_default().insert(*node);
312 }
313 }
314
315 path.pop();
317 stack.pop();
318 }
319 }
320
321 drop(stack);
322
323 subgraph.remove_node(root);
325
326 sccs.extend(subgraph.iter_sccs().filter(|scc| scc.len() > 1));
328 }
329
330 cycles
331}