1use std::vec::Vec;
34
35#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
41pub struct NodeId {
42 index: u32,
44 generation: u32,
47}
48
49impl NodeId {
50 #[must_use]
57 pub const fn index(self) -> usize {
58 self.index as usize
59 }
60
61 #[must_use]
69 pub const fn to_bits(self) -> u64 {
70 ((self.generation as u64) << 32) | self.index as u64
71 }
72
73 #[must_use]
78 pub const fn from_bits(bits: u64) -> Self {
79 Self {
80 index: bits as u32,
81 generation: (bits >> 32) as u32,
82 }
83 }
84}
85
86#[derive(Clone, Copy, PartialEq, Eq, Debug)]
88pub enum EdgeError {
89 NoSuchNode(NodeId),
92 SelfLoop,
94 Cycle,
96}
97
98#[derive(Debug)]
100struct NodeSlot<N> {
101 weight: Option<N>,
103 generation: u32,
107 dependents: Vec<u32>,
109 dependencies: Vec<u32>,
111}
112
113#[derive(Debug)]
115struct EdgeSlot {
116 from: u32,
118 to: u32,
120}
121
122#[derive(Debug)]
129pub struct Dag<N> {
130 nodes: Vec<NodeSlot<N>>,
131 edges: Vec<EdgeSlot>,
132 free_nodes: Vec<u32>,
134 free_edges: Vec<u32>,
136 live: usize,
138 visited: Vec<u32>,
142 stamp: u32,
144 stack: Vec<u32>,
146 plan_stack: Vec<(u32, bool)>,
149 order: Vec<u32>,
151}
152
153impl<N> Default for Dag<N> {
154 fn default() -> Self {
155 Self::new()
156 }
157}
158
159impl<N> Dag<N> {
160 pub fn new() -> Self {
162 Self {
163 nodes: Vec::new(),
164 edges: Vec::new(),
165 free_nodes: Vec::new(),
166 free_edges: Vec::new(),
167 live: 0,
168 visited: Vec::new(),
169 stamp: 0,
170 stack: Vec::new(),
171 plan_stack: Vec::new(),
172 order: Vec::new(),
173 }
174 }
175
176 pub fn len(&self) -> usize {
178 self.live
179 }
180
181 pub fn is_empty(&self) -> bool {
183 self.live == 0
184 }
185
186 pub fn insert(&mut self, weight: N) -> NodeId {
191 let index = match self.free_nodes.pop() {
192 Some(index) => {
193 let slot = &mut self.nodes[index as usize];
194 debug_assert!(slot.weight.is_none(), "a free slot holds no node");
195 slot.weight = Some(weight);
196 slot.generation = slot.generation.wrapping_add(1);
197 slot.dependents.clear();
198 slot.dependencies.clear();
199 index
200 }
201 None => {
202 self.nodes.push(NodeSlot {
203 weight: Some(weight),
204 generation: 0,
205 dependents: Vec::new(),
206 dependencies: Vec::new(),
207 });
208 (self.nodes.len() - 1) as u32
209 }
210 };
211 self.live += 1;
212 self.node_id(index)
213 }
214
215 pub fn iter(&self) -> impl Iterator<Item = (NodeId, &N)> {
222 self.nodes.iter().enumerate().filter_map(|(index, slot)| {
223 slot.weight
224 .as_ref()
225 .map(|weight| (self.node_id(index as u32), weight))
226 })
227 }
228
229 pub fn get(&self, id: NodeId) -> Option<&N> {
231 let index = self.resolve(id)?;
232 self.nodes[index].weight.as_ref()
233 }
234
235 pub fn get_mut(&mut self, id: NodeId) -> Option<&mut N> {
237 let index = self.resolve(id)?;
238 self.nodes[index].weight.as_mut()
239 }
240
241 pub fn add_edge(&mut self, from: NodeId, to: NodeId) -> Result<bool, EdgeError> {
251 let Some(from_index) = self.resolve(from) else {
252 return Err(EdgeError::NoSuchNode(from));
253 };
254 let Some(to_index) = self.resolve(to) else {
255 return Err(EdgeError::NoSuchNode(to));
256 };
257 if from_index == to_index {
258 return Err(EdgeError::SelfLoop);
259 }
260 if self.reaches(to_index, from_index) {
262 return Err(EdgeError::Cycle);
263 }
264 if self.has_edge(from_index, to_index) {
265 return Ok(false);
266 }
267 let edge = match self.free_edges.pop() {
268 Some(edge) => {
269 self.edges[edge as usize] = EdgeSlot {
270 from: from_index as u32,
271 to: to_index as u32,
272 };
273 edge
274 }
275 None => {
276 self.edges.push(EdgeSlot {
277 from: from_index as u32,
278 to: to_index as u32,
279 });
280 (self.edges.len() - 1) as u32
281 }
282 };
283 self.nodes[from_index].dependents.push(edge);
284 self.nodes[to_index].dependencies.push(edge);
285 Ok(true)
286 }
287
288 pub fn dependencies(&self, id: NodeId) -> impl Iterator<Item = NodeId> + '_ {
292 self.incident(id, |slot| &slot.dependencies)
293 .map(|edge| self.node_id(self.edges[edge as usize].from))
294 }
295
296 pub fn for_each_dependent_mut(&mut self, id: NodeId, mut f: impl FnMut(&mut N)) {
302 self.plan_dependents(id);
303 let order = core::mem::take(&mut self.order);
304 for &index in &order {
305 if let Some(weight) = self.nodes[index as usize].weight.as_mut() {
306 f(weight);
307 }
308 }
309 self.order = order;
310 }
311
312 pub fn remove_where_drop(&mut self, is_dead: impl Fn(&N) -> bool) -> usize {
319 self.order.clear();
320 self.order.extend(
321 (0..self.nodes.len())
322 .filter(|&index| self.nodes[index].weight.as_ref().is_some_and(&is_dead))
323 .map(|index| index as u32),
324 );
325 let order = core::mem::take(&mut self.order);
326 let removed = order.len();
327 for &index in &order {
328 let id = self.node_id(index);
329 self.remove(id);
330 }
331 self.order = order;
332 removed
333 }
334
335 pub fn topological_order(&self) -> Vec<NodeId> {
342 let mut pending: Vec<u32> = self
345 .nodes
346 .iter()
347 .map(|slot| {
348 if slot.weight.is_some() {
349 slot.dependencies.len() as u32
350 } else {
351 0
352 }
353 })
354 .collect();
355 let mut ready: Vec<u32> = self
356 .nodes
357 .iter()
358 .enumerate()
359 .filter_map(|(index, slot)| {
360 (slot.weight.is_some() && slot.dependencies.is_empty()).then_some(index as u32)
361 })
362 .collect();
363 let mut order = Vec::with_capacity(self.live);
364 while let Some(index) = ready.pop() {
365 order.push(self.node_id(index));
366 for &edge in &self.nodes[index as usize].dependents {
367 let to = self.edges[edge as usize].to;
368 pending[to as usize] -= 1;
369 if pending[to as usize] == 0 {
370 ready.push(to);
371 }
372 }
373 }
374 debug_assert_eq!(order.len(), self.live, "the graph is acyclic");
375 order
376 }
377
378 fn remove(&mut self, id: NodeId) -> Option<N> {
384 let index = self.resolve(id)?;
385 let slot = &mut self.nodes[index];
386 let weight = slot.weight.take()?;
387 let dependents = core::mem::take(&mut slot.dependents);
390 let dependencies = core::mem::take(&mut slot.dependencies);
391 self.live -= 1;
392 self.free_nodes.push(index as u32);
393 for edge in dependents {
394 let to = self.edges[edge as usize].to;
395 self.nodes[to as usize].dependencies.retain(|&e| e != edge);
396 self.free_edges.push(edge);
397 }
398 for edge in dependencies {
399 let from = self.edges[edge as usize].from;
400 self.nodes[from as usize].dependents.retain(|&e| e != edge);
401 self.free_edges.push(edge);
402 }
403 Some(weight)
404 }
405
406 fn resolve(&self, id: NodeId) -> Option<usize> {
412 let index = id.index as usize;
413 self.nodes
414 .get(index)
415 .filter(|slot| slot.weight.is_some() && slot.generation == id.generation)
416 .map(|_| index)
417 }
418
419 fn node_id(&self, index: u32) -> NodeId {
421 NodeId {
422 index,
423 generation: self.nodes[index as usize].generation,
424 }
425 }
426
427 fn has_edge(&self, from: usize, to: usize) -> bool {
429 self.nodes[from]
430 .dependents
431 .iter()
432 .any(|&edge| self.edges[edge as usize].to as usize == to)
433 }
434
435 fn incident<'a>(
438 &'a self,
439 id: NodeId,
440 select: impl Fn(&'a NodeSlot<N>) -> &'a Vec<u32>,
441 ) -> impl Iterator<Item = u32> + 'a {
442 self.resolve(id)
443 .map_or(&[][..], |index| select(&self.nodes[index]).as_slice())
444 .iter()
445 .copied()
446 }
447
448 fn reaches(&mut self, start: usize, target: usize) -> bool {
450 if start == target {
451 return true;
452 }
453 let stamp = self.next_stamp();
454 let mut stack = core::mem::take(&mut self.stack);
455 stack.clear();
456 stack.push(start as u32);
457 self.visited[start] = stamp;
458 let mut found = false;
459 while let Some(index) = stack.pop() {
460 for &edge in &self.nodes[index as usize].dependents {
461 let to = self.edges[edge as usize].to as usize;
462 if to == target {
463 found = true;
464 break;
465 }
466 if self.visited[to] != stamp {
467 self.visited[to] = stamp;
468 stack.push(to as u32);
469 }
470 }
471 if found {
472 break;
473 }
474 }
475 self.stack = stack;
476 found
477 }
478
479 fn plan_dependents(&mut self, root: NodeId) {
485 self.order.clear();
486 let Some(root_index) = self.resolve(root) else {
487 return;
488 };
489 let stamp = self.next_stamp();
492 let mut stack = core::mem::take(&mut self.plan_stack);
493 let mut order = core::mem::take(&mut self.order);
494 stack.clear();
495 stack.push((root_index as u32, false));
496 self.visited[root_index] = stamp;
497 while let Some((index, expanded)) = stack.pop() {
498 if expanded {
499 order.push(index);
500 continue;
501 }
502 stack.push((index, true));
503 for &edge in &self.nodes[index as usize].dependents {
504 let to = self.edges[edge as usize].to;
505 if self.visited[to as usize] != stamp {
506 self.visited[to as usize] = stamp;
507 stack.push((to, false));
508 }
509 }
510 }
511 self.plan_stack = stack;
512 order.reverse();
515 self.order = order;
516 }
517
518 fn next_stamp(&mut self) -> u32 {
521 if self.visited.len() < self.nodes.len() {
522 self.visited.resize(self.nodes.len(), 0);
523 }
524 self.stamp = self.stamp.wrapping_add(1);
525 if self.stamp == 0 {
526 self.visited.fill(0);
529 self.stamp = 1;
530 }
531 self.stamp
532 }
533}
534
535#[cfg(test)]
536mod tests {
537 use super::*;
538
539 #[test]
540 fn a_removed_slot_is_reused_with_a_new_generation() {
541 let mut dag = Dag::new();
542 let first = dag.insert("first");
543 assert_eq!(first.index(), 0);
544 assert_eq!(first.to_bits(), 0, "generation 0 in the high bits");
545 assert_eq!(dag.get(first), Some(&"first"));
546
547 assert_eq!(dag.remove(first), Some("first"));
548 assert_eq!(dag.get(first), None, "the slot is vacant");
549
550 let second = dag.insert("second");
553 assert_eq!(second.index(), first.index(), "the slot is reused");
554 assert_ne!(second.to_bits(), first.to_bits(), "but not the handle");
555 assert_eq!(dag.get(second), Some(&"second"));
556 assert_eq!(dag.get(first), None, "the old handle is stale");
557 }
558
559 #[test]
560 fn a_handle_survives_a_round_trip_through_bits() {
561 let mut dag = Dag::new();
562 let id = dag.insert("value");
563 let bits = id.to_bits();
564 assert_eq!(NodeId::from_bits(bits), id);
565 assert_eq!(dag.get(NodeId::from_bits(bits)), Some(&"value"));
566
567 dag.remove(id);
570 let reused = dag.insert("reused");
571 assert_eq!(reused.index(), id.index(), "the slot is reused");
572 assert_eq!(
573 dag.get(NodeId::from_bits(bits)),
574 None,
575 "the bits of a removed node do not resolve"
576 );
577 }
578
579 #[test]
580 fn removing_a_node_removes_only_its_edges() {
581 let mut dag = Dag::new();
582 let base = dag.insert("base");
583 let middle = dag.insert("middle");
584 let leaf = dag.insert("leaf");
585 dag.add_edge(base, middle).unwrap();
586 dag.add_edge(middle, leaf).unwrap();
587
588 dag.remove(middle);
589 assert_eq!(dag.dependencies(base).count(), 0);
590 assert_eq!(
591 dag.dependencies(leaf).count(),
592 0,
593 "the far end of the edge was unlinked too"
594 );
595 assert_eq!(dag.len(), 2);
596 }
597
598 #[test]
599 fn an_edge_that_would_close_a_cycle_is_refused() {
600 let mut dag = Dag::new();
601 let a = dag.insert("a");
602 let b = dag.insert("b");
603 let c = dag.insert("c");
604 dag.add_edge(a, b).unwrap();
605 dag.add_edge(b, c).unwrap();
606
607 assert_eq!(dag.add_edge(c, a), Err(EdgeError::Cycle));
608 assert_eq!(dag.add_edge(a, a), Err(EdgeError::SelfLoop));
609 assert_eq!(dag.len(), 3, "nothing was removed");
610 assert_eq!(
611 dag.dependencies(b).collect::<Vec<_>>(),
612 vec![a],
613 "the refused edges were not recorded"
614 );
615 }
616
617 #[test]
618 fn declaring_the_same_edge_twice_records_it_once() {
619 let mut dag = Dag::new();
620 let base = dag.insert("base");
621 let dependent = dag.insert("dependent");
622 dag.add_edge(base, dependent).unwrap();
623 dag.add_edge(base, dependent).unwrap();
624
625 assert_eq!(dag.dependencies(dependent).collect::<Vec<_>>(), vec![base]);
626 }
627
628 #[test]
629 fn an_edge_with_a_stale_endpoint_names_the_node_it_refused() {
630 let mut dag = Dag::new();
631 let base = dag.insert("base");
632 let removed = dag.insert("removed");
633 dag.remove(removed);
634
635 assert_eq!(
636 dag.add_edge(base, removed),
637 Err(EdgeError::NoSuchNode(removed))
638 );
639 }
640
641 #[test]
642 fn the_topological_order_puts_every_dependency_first() {
643 let mut dag = Dag::new();
644 let base = dag.insert("base");
650 let left = dag.insert("left");
651 let right = dag.insert("right");
652 let join = dag.insert("join");
653 dag.add_edge(base, left).unwrap();
654 dag.add_edge(base, right).unwrap();
655 dag.add_edge(left, join).unwrap();
656 dag.add_edge(right, join).unwrap();
657
658 let order = dag.topological_order();
659 let position = |id: NodeId| order.iter().position(|&node| node == id).unwrap();
660 assert_eq!(order.len(), 4);
661 assert!(position(base) < position(left));
662 assert!(position(base) < position(right));
663 assert!(position(left) < position(join));
664 assert!(position(right) < position(join));
665 }
666
667 #[test]
668 fn the_topological_order_lists_isolated_nodes() {
669 let mut dag = Dag::new();
670 dag.insert("a");
671 dag.insert("b");
672 assert_eq!(dag.topological_order().len(), 2);
673 }
674
675 #[test]
676 fn applying_to_dependents_visits_the_subtree_and_nothing_else() {
677 let mut dag = Dag::new();
678 let base = dag.insert(0u32);
679 let middle = dag.insert(0);
680 let leaf = dag.insert(0);
681 let unrelated = dag.insert(0);
682 dag.add_edge(base, middle).unwrap();
683 dag.add_edge(middle, leaf).unwrap();
684
685 dag.for_each_dependent_mut(middle, |count| *count += 1);
686 assert_eq!(dag.get(base), Some(&0), "the walk only goes forwards");
687 assert_eq!(dag.get(middle), Some(&1));
688 assert_eq!(dag.get(leaf), Some(&1));
689 assert_eq!(dag.get(unrelated), Some(&0));
690 }
691
692 #[test]
693 fn removing_where_leaves_the_nodes_that_were_not_selected() {
694 let mut dag = Dag::new();
695 let base = dag.insert("base");
696 let middle = dag.insert("middle");
697 let root = dag.insert("root");
698 let outside = dag.insert("outside");
699 dag.add_edge(base, middle).unwrap();
700 dag.add_edge(middle, root).unwrap();
701
702 let removed = dag.remove_where_drop(|&name| name == "middle");
703 assert_eq!(removed, 1);
704 assert_eq!(dag.get(base), Some(&"base"), "the node it depends on stays");
705 assert_eq!(dag.get(outside), Some(&"outside"));
706 assert_eq!(dag.get(middle), None);
707 assert_eq!(
708 dag.get(root),
709 Some(&"root"),
710 "a dependent is not removed with its dependency"
711 );
712 assert_eq!(dag.len(), 3);
713 }
714
715 #[test]
718 fn removing_where_reports_the_count_so_a_caller_can_sweep_to_a_fixpoint() {
719 let mut dag = Dag::new();
720 dag.insert("first");
724 dag.insert("second");
725 dag.insert("keep");
726 let mut sweeps = 0;
727 loop {
728 sweeps += 1;
729 let removed = dag.remove_where_drop(|&name| name != "keep");
730 if removed == 0 {
731 break;
732 }
733 }
734
735 assert_eq!(sweeps, 2, "one sweep removed the two, the next found none");
736 assert_eq!(dag.len(), 1);
737 }
738}