Skip to content

Commit

Permalink
fix: Hierarchy descendants return root siblings (#178)
Browse files Browse the repository at this point in the history
Closes #177
  • Loading branch information
aborgna-q authored Jan 20, 2025
1 parent fbd65b7 commit da7a040
Showing 1 changed file with 46 additions and 8 deletions.
54 changes: 46 additions & 8 deletions src/hierarchy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ impl Hierarchy {
pub fn descendants(&self, node: NodeIndex) -> Descendants<'_> {
Descendants {
layout: self,
child_queue: VecDeque::from(vec![node]),
node_queue: NextStates::Root(node),
}
}

Expand Down Expand Up @@ -634,15 +634,35 @@ pub struct Descendants<'a> {
///
/// For each region, we point to a child node that has not been visited yet.
/// When a region is visited, we move to the next child node and queue its children region at the end of the queue.
child_queue: VecDeque<NodeIndex>,
node_queue: NextStates,
}

/// A queue of nodes to visit next in the [`Descendants`] iterator.
#[derive(Clone, Debug)]
enum NextStates {
/// The iterator hasn't been queried yet. It stores only the root node.
Root(NodeIndex),
/// We are in the process of exploring the descendants.
/// This queue does **not** include the root node.
Descendants(VecDeque<NodeIndex>),
}

impl NextStates {
/// Returns the number of nodes currently in the queue.
fn len(&self) -> usize {
match self {
Self::Root(_) => 1,
Self::Descendants(queue) => queue.len(),
}
}
}

impl Default for Descendants<'static> {
fn default() -> Self {
static HIERARCHY: Hierarchy = Hierarchy::new();
Self {
layout: &HIERARCHY,
child_queue: VecDeque::new(),
node_queue: NextStates::Descendants(VecDeque::new()),
}
}
}
Expand All @@ -651,25 +671,35 @@ impl Iterator for Descendants<'_> {
type Item = NodeIndex;

fn next(&mut self) -> Option<Self::Item> {
if let NextStates::Root(root) = &self.node_queue {
let root = *root;
self.node_queue = NextStates::Descendants(VecDeque::from_iter(self.layout.first(root)));
return Some(root);
};
let NextStates::Descendants(queue) = &mut self.node_queue else {
unreachable!()
};

// The next element is always the first node in the queue.
let next = self.child_queue.pop_front()?;
let next = queue.pop_front()?;

// Check if the node had a next sibling, and add it to the front of queue.
// Check if the node had a next sibling and add it to the front of
// queue.
if let Some(next_sibling) = self.layout.next(next) {
self.child_queue.push_front(next_sibling);
queue.push_front(next_sibling);
}

// Now add the children region of `next` to the end of the queue.
if let Some(child) = self.layout.first(next) {
self.child_queue.push_back(child);
queue.push_back(child);
}

Some(next)
}

#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.child_queue.len(), None)
(self.node_queue.len(), None)
}
}

Expand All @@ -692,6 +722,8 @@ pub enum AttachError {

#[cfg(test)]
mod test {
use itertools::Itertools;

use crate::{PortGraph, PortMut, PortView};

use super::*;
Expand All @@ -708,6 +740,10 @@ mod test {
assert_eq!(hierarchy.next(root), None);
assert_eq!(hierarchy.prev(root), None);

// root
// |-> child0
// |-> child1
// -> child2
let child0 = NodeIndex::new(0);
let child1 = NodeIndex::new(1);
let child2 = NodeIndex::new(2);
Expand Down Expand Up @@ -746,6 +782,8 @@ mod test {
for child in children {
assert_eq!(hierarchy.parent(child), Some(root));
assert_eq!(hierarchy.child_count(child), 0);
// https://github.com/CQCL/portgraph/issues/177
assert_eq!(hierarchy.descendants(child).collect_vec(), vec![child]);
}
assert_eq!(hierarchy.prev(child0), None);
assert_eq!(hierarchy.next(child0), Some(child1));
Expand Down

0 comments on commit da7a040

Please sign in to comment.