Update to Rust 2018 and use pub(crate) for hidden APIs.

This commit is contained in:
Stu Black 2019-07-13 22:38:17 -04:00
parent c1a24ff1a6
commit d6de8fb161
12 changed files with 1124 additions and 1135 deletions

View File

@ -1,10 +1,11 @@
[package]
name = "search-graph"
version = "0.1.0"
version = "0.2.0"
authors = ["Stu Black"]
edition = "2018"
[dependencies]
symbol-map = "1.0"
[dev-dependencies]
crossbeam = "0.2.*"
crossbeam-utils = "0.6.*"

View File

@ -1,4 +1,4 @@
use ::symbol_map;
use symbol_map;
/// Internal edge identifier.
///
@ -6,7 +6,7 @@ use ::symbol_map;
/// graph that it belongs to, which makes it only slightly less dangerous than a
/// pointer with no lifetime.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct EdgeId(pub usize);
pub(crate) struct EdgeId(pub usize);
impl EdgeId {
/// Converts an `EdgeId` to a usize that is guaranteed to be unique within a
@ -24,7 +24,7 @@ impl EdgeId {
/// the graph that it belongs to, which makes it only slightly less dangerous
/// than a pointer with no lifetime.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct VertexId(pub usize);
pub(crate) struct VertexId(pub usize);
impl Default for VertexId {
fn default() -> Self { VertexId(0) }
@ -40,7 +40,7 @@ impl symbol_map::SymbolId for VertexId {
/// The Hash, Ord, and Eq implementations will conflate parallel edges with
/// identical statistics.
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct RawEdge<A> {
pub(crate) struct RawEdge<A> {
/// Edge data.
pub data: A,
/// Source vertex.
@ -51,7 +51,7 @@ pub struct RawEdge<A> {
/// Internal type for graph vertices.
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct RawVertex<S> {
pub(crate) struct RawVertex<S> {
/// Vertex data.
pub data: S,
/// Parent edges pointing into this vertex.

View File

@ -1,4 +0,0 @@
pub mod base;
pub mod nav;
pub mod mutators;

View File

@ -1,703 +0,0 @@
//! Data structures for tracking graph position during local search.
//!
//! The main data structure in this module is `Stack`, which provides
//! memory-safe construction of the path that was traversed when performing
//! local search on a graph.
use std::clone::Clone;
use std::cmp::Eq;
use std::error::Error;
use std::fmt;
use std::hash::Hash;
use std::iter::Iterator;
use ::Graph;
use ::hidden::base::*;
use ::hidden::mutators::MutNode;
use ::hidden::nav::{Edge, Node, make_edge, make_node};
/// Errors that may arise during search.
#[derive(Debug)]
pub enum SearchError<E> where E: Error {
/// A search operation selected a child index that was out of bounds.
ChildBounds {
/// The index of the child that was requested.
requested_index: usize,
/// The actual number of chidren (which `requested_index` exceeds).
child_count: usize
},
/// A search operation selected a parent index that was out of bounds.
ParentBounds {
/// The index of the parent that was requested.
requested_index: usize,
/// The actual number of parents (which `requested_index` exceeds).
parent_count: usize
},
/// A search operation encountered an error.
SelectionError(E),
}
/// Tracks the path through a graph that is followed when performing local search.
///
/// In this case, "local search" is a process that starts focused on a single
/// vertex and incrementally updates which vertex is the focus by traversing
/// parent or child edges. The history of such operations can be described as a
/// series of (vertex, edge) pairs, and a `Stack` encapsulates this
/// history.
///
/// A `Stack` points to a head, which is either a graph vertex (whose
/// incidental edges can then be traversed) or an unexpanded edge (if a
/// traversal operation chose to follow an unexpanded edge). Operations which
/// modify graph topology (such as expanding edges) may cause the search path's
/// internal state to fall out of sync with the graph's state, so graph elements
/// exposed using the read-only `Node` and `Edge` types.
///
/// A path may be consumed to yield a read-write view of the underlying graph
/// with the `to_head` method.
pub struct Stack<'a, T, S, A> where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a {
/// The graph that is being searched.
graph: &'a mut Graph<T, S, A>,
/// The edges that have been traversed.
path: Vec<EdgeId>,
/// The path head.
head: VertexId,
}
/// Indicates which edge of a vertex to traverse. Edges are denoted by a 0-based
/// index. This type is used by functions provided during graph search to
/// indicate which child or parent edges to traverse.
pub enum Traversal {
/// Traverse the given child.
Child(usize),
/// Traverse the given parent.
Parent(usize),
}
/// Iterates over elements of a search path, in the order in which they were
/// traversed, ending with the head.
pub struct StackIter<'a, 's, T, S, A> where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a, 'a: 's {
/// The path being iterated over.
path: &'s Stack<'a, T, S, A>,
/// The position through path.
position: usize,
}
/// Sum type for path elements. All elements except the head are represented
/// with the `StackItem::Item` variant.
pub enum StackItem<'a, T, S, A> where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a {
/// Non-head item, a (vertex, edge) pair.
Item(Edge<'a, T, S, A>),
/// The path head, which may resolve to a vertex or an unexpanded edge.
Head(Node<'a, T, S, A>),
}
impl<E> fmt::Display for SearchError<E> where E: Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SearchError::ChildBounds { requested_index, child_count } =>
write!(f, "Search chose child {}/{}", requested_index, child_count),
SearchError::ParentBounds { requested_index, parent_count } =>
write!(f, "Search chose parent {}/{}", requested_index, parent_count),
SearchError::SelectionError(ref e) => write!(f, "Error in search operation: {}", e),
}
}
}
impl<E> Error for SearchError<E> where E: Error {
fn description(&self) -> &str {
match *self {
SearchError::ChildBounds { requested_index: _, child_count: _ } => "child out of bounds",
SearchError::ParentBounds { requested_index: _, parent_count: _ } => "parent out of bounds",
SearchError::SelectionError(ref e) => e.description(),
}
}
fn cause(&self) -> Option<&Error> {
match *self {
SearchError::SelectionError(ref e) => Some(e),
_ => None,
}
}
}
impl<'a, T, S, A> Stack<'a, T, S, A> where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a {
/// Creates a new `Stack` from a mutable reference into a graph.
pub fn new(node: MutNode<'a, T, S, A>) -> Self {
Stack {
graph: node.graph,
path: Vec::new(),
head: node.id,
}
}
/// Returns the number of elements in the path. Since a path always has a
/// head, there is always at least 1 element.
pub fn len(&self) -> usize {
self.path.len() + 1
}
/// Removes the most recently traversed element from the path, if
/// any. Returns a handle for any edge that was removed.
pub fn pop<'s>(&'s mut self) -> Option<Edge<'s, T, S, A>> {
match self.path.pop() {
Some(edge_id) => {
self.head = self.graph.get_arc(edge_id).source;
Some(make_edge(self.graph, edge_id))
},
None => None,
}
}
/// Returns a read-only view of the head element.
pub fn head<'s>(&'s self) -> Node<'s, T, S, A> {
make_node(self.graph, self.head)
}
/// Consumes the path and returns a mutable view of its head.
pub fn to_head(self) -> MutNode<'a, T, S, A> {
MutNode { graph: self.graph, id: self.head, }
}
/// Grows the path by consulting a function of the current head. If this
/// function `f` returns `Ok(Some(Traversal::Child(i)))`, then the `i`th
/// child of the current head is pushed onto the path. If it returns
/// `Ok(Some(Traversal::Parent(i)))`, then the `i`th parent of the current
/// head is pushed onto the path.
///
/// The decision not to traverse any edge may be made by returning
/// `Ok(None)`, while `Err(E)` should be returned for any errors.
///
/// Returns an `Ok(Option(e))` for any edge `e` that is traversed, or
/// `Err(e)` if an error was encountered.
pub fn push<'s, F, E>(&'s mut self, mut f: F) -> Result<Option<Edge<'s, T, S, A>>, SearchError<E>>
where F: FnMut(&Node<'s, T, S, A>) -> Result<Option<Traversal>, E>, E: Error {
let node = make_node(self.graph, self.head);
match f(&node) {
Ok(Some(Traversal::Child(i))) => {
let children = node.get_child_list();
if i >= children.len() {
Err(SearchError::ChildBounds {
requested_index: i, child_count: children.len() })
} else {
let child = children.get_edge(i);
self.path.push(EdgeId(child.get_id()));
self.head = VertexId(child.get_target().get_id());
Ok(Some(child))
}
},
Ok(Some(Traversal::Parent(i))) => {
let parents = node.get_parent_list();
if i >= parents.len() {
Err(SearchError::ParentBounds {
requested_index: i, parent_count: parents.len() })
} else {
let parent = parents.get_edge(i);
self.path.push(EdgeId(parent.get_id()));
self.head = VertexId(parent.get_source().get_id());
Ok(Some(parent))
}
},
Ok(None) => Ok(None),
Err(e) => Err(SearchError::SelectionError(e)),
}
}
/// Returns an iterator over path elements. Iteration is in order of
/// traversal (i.e., the last element of the iteration is the path head).
pub fn iter<'s>(&'s self) -> StackIter<'a, 's, T, S, A> {
StackIter::new(self)
}
/// Returns the `i`th item of the path. Path items are indexed in order of
/// traversal (i.e., the last element is the path head).
pub fn item<'s>(&'s self, i: usize) -> Option<StackItem<'s, T, S, A>> {
if i == self.path.len() {
Some(StackItem::Head(self.head()))
} else {
match self.path.get(i) {
Some(edge_id) =>
Some(StackItem::Item(make_edge(self.graph, *edge_id))),
None => None,
}
}
}
}
impl<'a, 's, T, S, A> StackIter<'a, 's, T, S, A> where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a, 'a: 's {
/// Creates a new path iterator from a borrow of a path.
fn new(path: &'s Stack<'a, T, S, A>) -> Self {
StackIter {
path: path,
position: 0,
}
}
}
impl<'a, 's, T, S, A> Iterator for StackIter<'a, 's, T, S, A>
where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a, 'a: 's {
type Item = StackItem<'s, T, S, A>;
fn next(&mut self) -> Option<StackItem<'s, T, S, A>> {
let i = self.position;
self.position += 1;
self.path.item(i)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.path.len() - self.position;
(len, Some(len))
}
}
#[cfg(test)]
mod test {
use std::error::Error;
use std::fmt;
use super::{SearchError, StackItem, Traversal};
type Graph = ::Graph<&'static str, &'static str, ()>;
type Node<'a> = ::nav::Node<'a, &'static str, &'static str, ()>;
type Stack<'a> = super::Stack<'a, &'static str, &'static str, ()>;
fn add_edge(g: &mut Graph, source: &'static str, dest: &'static str) {
g.add_edge(source, |_| source, dest, |_| dest, ());
}
#[derive(Debug)]
struct MockError(());
impl Error for MockError {
fn description(&self) -> &str { "toy error" }
}
impl fmt::Display for MockError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "toy error")
}
}
#[test]
fn instantiation_ok() {
let mut g = Graph::new();
let root = g.add_root("root", "root");
let path = Stack::new(root);
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
}
#[test]
fn push_no_children_ok() {
let mut g = Graph::new();
let root = g.add_root("root", "root");
let mut path = Stack::new(root);
assert_eq!(1, path.len());
fn no_traversal<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
Ok(None)
}
match path.push(no_traversal) {
Ok(None) => (),
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
}
#[test]
fn push_no_children_err() {
let mut g = Graph::new();
let root = g.add_root("root", "root");
let mut path = Stack::new(root);
assert_eq!(1, path.len());
fn traverse_first_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
assert!(n.get_child_list().is_empty());
Ok(Some(Traversal::Child(0)))
}
match path.push(traverse_first_child) {
Err(SearchError::ChildBounds { requested_index, child_count }) => {
assert_eq!(0, requested_index);
assert_eq!(0, child_count);
},
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
}
#[test]
fn push_to_child_ok() {
let mut g = Graph::new();
add_edge(&mut g, "A", "B1");
add_edge(&mut g, "A", "B2");
add_edge(&mut g, "B1", "C");
add_edge(&mut g, "B2", "D");
fn traverse_second_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("A", *n.get_data());
let children = n.get_child_list();
assert_eq!(2, children.len());
assert_eq!("B1", *children.get_edge(0).get_target().get_data());
assert_eq!("B2", *children.get_edge(1).get_target().get_data());
Ok(Some(Traversal::Child(1)))
}
let mut path = Stack::new(g.get_node_mut(&"A").unwrap());
assert_eq!(1, path.len());
match path.push(traverse_second_child) {
Ok(Some(e)) => {
assert_eq!("A", *e.get_source().get_data());
assert_eq!("B2", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(2, path.len());
fn traverse_first_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("B2", *n.get_data());
assert_eq!(1, n.get_child_list().len());
Ok(Some(Traversal::Child(0)))
}
match path.push(traverse_first_child) {
Ok(Some(e)) => {
assert_eq!("B2", *e.get_source().get_data());
assert_eq!("D", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(3, path.len());
assert_eq!("D", *path.head().get_data());
}
#[test]
fn push_to_child_err_ok() {
let mut g = Graph::new();
add_edge(&mut g, "A", "B1");
add_edge(&mut g, "A", "B2");
add_edge(&mut g, "B1", "C");
add_edge(&mut g, "B2", "D");
fn traverse_err<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("A", *n.get_data());
Err(MockError(()))
}
let mut path = Stack::new(g.get_node_mut(&"A").unwrap());
assert_eq!(1, path.len());
match path.push(traverse_err) {
Err(SearchError::SelectionError(_)) => (),
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("A", *path.head().get_data())
}
#[test]
fn push_no_parents_ok() {
let mut g = Graph::new();
let root = g.add_root("root", "root");
let mut path = Stack::new(root);
assert_eq!(1, path.len());
fn no_traversal<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
Ok(None)
}
match path.push(no_traversal) {
Ok(None) => (),
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
}
#[test]
fn push_no_parents_err() {
let mut g = Graph::new();
let root = g.add_root("root", "root");
let mut path = Stack::new(root);
assert_eq!(1, path.len());
fn traverse_first_parent<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
assert!(n.get_parent_list().is_empty());
Ok(Some(Traversal::Parent(0)))
}
match path.push(traverse_first_parent) {
Err(SearchError::ParentBounds { requested_index, parent_count }) => {
assert_eq!(0, requested_index);
assert_eq!(0, parent_count);
},
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
}
#[test]
fn push_to_parent_ok() {
let mut g = Graph::new();
add_edge(&mut g, "A", "B1");
add_edge(&mut g, "A", "B2");
add_edge(&mut g, "B1", "C");
add_edge(&mut g, "B2", "D");
add_edge(&mut g, "C", "B2");
fn traverse_second_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("A", *n.get_data());
let children = n.get_child_list();
assert_eq!(2, children.len());
assert_eq!("B1", *children.get_edge(0).get_target().get_data());
assert_eq!("B2", *children.get_edge(1).get_target().get_data());
Ok(Some(Traversal::Child(1)))
}
let mut path = Stack::new(g.get_node_mut(&"A").unwrap());
assert_eq!(1, path.len());
match path.push(traverse_second_child) {
Ok(Some(e)) => {
assert_eq!("A", *e.get_source().get_data());
assert_eq!("B2", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(2, path.len());
assert_eq!("B2", *path.head().get_data());
fn traverse_first_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("B2", *n.get_data());
assert_eq!(1, n.get_child_list().len());
Ok(Some(Traversal::Child(0)))
}
match path.push(traverse_first_child) {
Ok(Some(e)) => {
assert_eq!("B2", *e.get_source().get_data());
assert_eq!("D", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(3, path.len());
assert_eq!("D", *path.head().get_data());
fn traverse_first_parent<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("D", *n.get_data());
assert_eq!(1, n.get_parent_list().len());
Ok(Some(Traversal::Parent(0)))
}
match path.push(traverse_first_parent) {
Ok(Some(e)) => {
assert_eq!("B2", *e.get_source().get_data());
assert_eq!("D", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(4, path.len());
assert_eq!("B2", *path.head().get_data());
fn traverse_second_parent<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("B2", *n.get_data());
assert_eq!(2, n.get_parent_list().len());
Ok(Some(Traversal::Parent(1)))
}
match path.push(traverse_second_parent) {
Ok(Some(e)) => {
assert_eq!("C", *e.get_source().get_data());
assert_eq!("B2", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(5, path.len());
assert_eq!("C", *path.head().get_data());
}
#[test]
fn push_to_parent_err_ok() {
let mut g = Graph::new();
add_edge(&mut g, "A", "B1");
add_edge(&mut g, "A", "B2");
add_edge(&mut g, "B1", "C");
add_edge(&mut g, "B2", "D");
fn traverse_err<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("A", *n.get_data());
Err(MockError(()))
}
let mut path = Stack::new(g.get_node_mut(&"A").unwrap());
assert_eq!(1, path.len());
match path.push(traverse_err) {
Err(SearchError::SelectionError(_)) => (),
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("A", *path.head().get_data());
}
#[test]
fn search_path_iter_empty_ok() {
let mut g = Graph::new();
g.add_root("root", "root");
let path = Stack::new(g.add_root("root", "root"));
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
let mut iter_items = path.iter();
assert_eq!((1, Some(1)), iter_items.size_hint());
match iter_items.next() {
Some(StackItem::Head(n)) => assert_eq!("root", *n.get_data()),
_ => panic!(),
}
assert!(iter_items.next().is_none());
}
#[test]
fn search_path_iter_items_ok() {
let mut g = Graph::new();
g.add_root("root", "root");
add_edge(&mut g, "root", "A");
add_edge(&mut g, "A", "B");
fn traverse_first_child<'a>(_: &Node<'a>) -> Result<Option<Traversal>, MockError> {
Ok(Some(Traversal::Child(0)))
}
let mut path = Stack::new(g.get_node_mut(&"root").unwrap());
match path.push(traverse_first_child) {
Ok(Some(e)) => assert_eq!("root", *e.get_source().get_data()),
_ => panic!(),
}
match path.push(traverse_first_child) {
Ok(Some(e)) => {
assert_eq!("A", *e.get_source().get_data());
assert_eq!("B", *e.get_target().get_data());
},
_ => panic!(),
}
match path.push(traverse_first_child) {
Err(SearchError::ChildBounds { requested_index, child_count })
if requested_index == 0 && child_count == 0 => (),
_ => panic!(),
}
let mut iter_items = path.iter();
assert_eq!((3, Some(3)), iter_items.size_hint());
match iter_items.next() {
Some(StackItem::Item(e)) => {
assert_eq!("root", *e.get_source().get_data());
assert_eq!("A", *e.get_target().get_data());
},
_ => panic!(),
}
match iter_items.next() {
Some(StackItem::Item(e)) => {
assert_eq!("A", *e.get_source().get_data());
assert_eq!("B", *e.get_target().get_data());
},
_ => panic!(),
}
match iter_items.next() {
Some(StackItem::Head(n)) =>
assert_eq!("B", *n.get_data()),
_ => panic!(),
}
assert!(iter_items.next().is_none());
}
#[test]
fn pop_empty_is_none_ok() {
let mut g = Graph::new();
let mut path = Stack::new(g.add_root("root", "root"));
assert_eq!(1, path.len());
assert!(path.pop().is_none());
}
#[test]
fn pop_ok() {
let mut g = Graph::new();
add_edge(&mut g, "root", "A");
let mut path = Stack::new(g.get_node_mut(&"root").unwrap());
assert_eq!(1, path.len());
fn traverse_first_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
Ok(Some(Traversal::Child(0)))
}
match path.push(traverse_first_child) {
Ok(Some(e)) => assert_eq!("root", *e.get_source().get_data()),
_ => panic!(),
}
assert_eq!(2, path.len());
assert_eq!("A", *path.head().get_data());
match path.pop() {
Some(e) => assert_eq!("root", *e.get_source().get_data()),
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
assert!(path.pop().is_none());
}
#[test]
fn to_head_empty_ok() {
let mut g = Graph::new();
add_edge(&mut g, "root", "A");
let path = Stack::new(g.get_node_mut(&"root").unwrap());
assert_eq!(1, path.len());
assert_eq!("root", *path.to_head().get_data());
}
#[test]
fn to_head_expanded_ok() {
let mut g = Graph::new();
add_edge(&mut g, "root", "A");
let mut path = Stack::new(g.get_node_mut(&"root").unwrap());
assert_eq!(1, path.len());
fn traverse_first_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
Ok(Some(Traversal::Child(0)))
}
match path.push(traverse_first_child) {
Ok(Some(e)) => assert_eq!("root", *e.get_source().get_data()),
_ => panic!(),
}
assert_eq!(2, path.len());
assert_eq!("A", *path.to_head().get_data());
}
}

View File

@ -1,289 +0,0 @@
use std::hash::Hash;
use std::iter::Iterator;
use ::Graph;
use ::hidden::base::{EdgeId, VertexId, RawEdge, RawVertex};
use ::symbol_map::SymbolId;
/// Immutable handle to a graph vertex ("node handle").
///
/// This zipper-like type enables traversal of a graph along the vertex's
/// incoming and outgoing edges.
pub struct Node<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: VertexId,
}
/// Creates a new `Node` for the given graph and gamestate. This method is not
/// exported by the crate because it exposes implementation details. It is used
/// to provide a public cross-module interface for creating new `Node`s.
pub fn make_node<'a, T, S, A>(graph: &'a Graph<T, S, A>, id: VertexId) -> Node<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
Node { graph: graph, id: id, }
}
impl<'a, T, S, A> Node<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
fn children(&self) -> &'a [EdgeId] {
&self.graph.get_vertex(self.id).children
}
/// Returns the canonical label that is used to address this `Node`.
///
/// Graph instances which project multiple labels to the same vertex will
/// consistently return a single value, regardless of which value was used
/// to obtain this node handle.
pub fn get_label(&self) -> &T {
&self.graph.get_state(self.id).unwrap()
}
/// Returns an immutable ID that is guaranteed to identify this vertex
/// uniquely within its graph. This ID may change when the graph is mutated.
pub fn get_id(&self) -> usize {
self.id.as_usize()
}
fn parents(&self) -> &'a [EdgeId] {
&self.graph.get_vertex(self.id).parents
}
/// Returns the data at this vertex.
pub fn get_data(&self) -> &'a S {
&self.graph.get_vertex(self.id).data
}
/// Returns true iff this vertex has no outgoing edges (regardless of
/// whether they are expanded).
pub fn is_leaf(&self) -> bool {
self.children().is_empty()
}
/// Returns true iff this vertex has no incoming edges.
pub fn is_root(&self) -> bool {
self.parents().is_empty()
}
/// Returns a traversible list of outgoing edges.
pub fn get_child_list(&self) -> ChildList<'a, T, S, A> {
ChildList { graph: self.graph, id: self.id, }
}
/// Returns a traversible list of incoming edges.
pub fn get_parent_list(&self) -> ParentList<'a, T, S, A> {
ParentList { graph: self.graph, id: self.id, }
}
}
/// A traversible list of a vertex's outgoing edges.
pub struct ChildList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: VertexId,
}
/// Creates a new `ChildList` for the given graph and gamestate. This method is
/// not exported by the crate because it exposes implementation details. It is
/// used to provide a public cross-module interface for creating new
/// `ChildList`s.
pub fn make_child_list<'a, T, S, A>(graph: &'a Graph<T, S, A>, id: VertexId) -> ChildList<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
ChildList { graph: graph, id: id, }
}
impl<'a, T, S, A> ChildList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
fn vertex(&self) -> &'a RawVertex<S> {
self.graph.get_vertex(self.id)
}
/// Returns the number of edges.
pub fn len(&self) -> usize {
self.vertex().children.len()
}
/// Returns true iff there are no outgoing edges.
pub fn is_empty(&self) -> bool {
self.vertex().children.is_empty()
}
/// Returns a node handle for the vertex these edges originate from.
pub fn get_source_node(&self) -> Node<'a, T, S, A> {
Node { graph: self.graph, id: self.id, }
}
/// Returns an edge handle for the `i`th edge.
pub fn get_edge(&self, i: usize) -> Edge<'a, T, S, A> {
Edge { graph: self.graph, id: self.vertex().children[i], }
}
/// Returns an iterator over child edges.
pub fn iter(&self) -> ChildListIter<'a, T, S, A> {
ChildListIter { graph: self.graph, id: self.id, i: 0, }
}
}
/// Iterator over a vertex's child edges.
pub struct ChildListIter<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: VertexId,
i: usize,
}
impl <'a, T, S, A> ChildListIter<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
fn children(&self) -> &'a [EdgeId] {
&self.graph.get_vertex(self.id).children
}
}
impl<'a, T, S, A> Iterator for ChildListIter<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
type Item = Edge<'a, T, S, A>;
fn next(&mut self) -> Option<Edge<'a, T, S, A>> {
let cs = self.children();
if self.i >= cs.len() {
None
} else {
let e = make_edge(self.graph, cs[self.i]);
self.i += 1;
Some(e)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let l = self.children().len();
if l <= self.i {
(0, Some(0))
} else {
(l - self.i, Some(l - self.i))
}
}
}
/// A traversible list of a vertex's incoming edges.
pub struct ParentList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: VertexId,
}
/// Creates a new `ParentList` for the given graph and gamestate. This method is
/// not exported by the crate because it exposes implementation details. It is
/// used to provide a public cross-module interface for creating new
/// `ParentList`s.
pub fn make_parent_list<'a, T, S, A>(graph: &'a Graph<T, S, A>, id: VertexId) -> ParentList<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
ParentList { graph: graph, id: id, }
}
impl<'a, T, S, A> ParentList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
fn vertex(&self) -> &'a RawVertex<S> {
self.graph.get_vertex(self.id)
}
/// Returns the number of edges.
pub fn len(&self) -> usize {
self.vertex().parents.len()
}
/// Returns true iff there are no incoming edges.
pub fn is_empty(&self) -> bool {
self.vertex().parents.is_empty()
}
/// Returns a node handle for the vertex these edges point to.
pub fn target_node(&self) -> Node<'a, T, S, A> {
Node { graph: self.graph, id: self.id, }
}
/// Returns an edge handle for the `i`th edge.
pub fn get_edge(&self, i: usize) -> Edge<'a, T, S, A> {
Edge { graph: self.graph, id: self.vertex().parents[i] }
}
/// Returns an iterator over parent edges.
pub fn iter(&self) -> ParentListIter<'a, T, S, A> {
ParentListIter { graph: self.graph, id: self.id, i: 0, }
}
}
/// Iterator over a vertex's parent edges.
pub struct ParentListIter<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: VertexId,
i: usize,
}
impl<'a, T, S, A> ParentListIter<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
fn parents(&self) -> &'a [EdgeId] {
&self.graph.get_vertex(self.id).parents
}
}
impl<'a, T, S, A> Iterator for ParentListIter<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
type Item = Edge<'a, T, S, A>;
fn next(&mut self) -> Option<Edge<'a, T, S, A>> {
let ps = self.parents();
if self.i >= ps.len() {
None
} else {
let e = make_edge(self.graph, ps[self.i]);
self.i += 1;
Some(e)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let l = self.parents().len();
if l <= self.i {
(0, Some(0))
} else {
(l - self.i, Some(l - self.i))
}
}
}
/// Immutable handle to a graph edge ("edge handle").
///
/// This zipper-like type enables traversal of a graph along the edge's source
/// and target vertices.
pub struct Edge<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: EdgeId,
}
/// Creates a new `Edge` for the given graph and gamestate. This method is not
/// exported by the crate because it exposes implementation details. It is used
/// to provide a public cross-module interface for creating new `Edge`s.
pub fn make_edge<'a, T, S, A>(graph: &'a Graph<T, S, A>, id: EdgeId) -> Edge<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
Edge { graph: graph, id: id, }
}
impl<'a, T, S, A> Edge<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
fn arc(&self) -> &'a RawEdge<A> {
self.graph.get_arc(self.id)
}
/// Returns an immutable ID that is guaranteed to identify this edge
/// uniquely within its graph. This ID may change when the graph is
/// mutated.
pub fn get_id(&self) -> usize {
self.id.as_usize()
}
/// Returns the data at this edge.
pub fn get_data(&self) -> &'a A {
&self.arc().data
}
/// Returns a node handle for this edge's source vertex.
pub fn get_source(&self) -> Node<'a, T, S, A> {
Node { graph: self.graph, id: self.arc().source, }
}
/// Returns the target of this edge. If the edge is unexpanded, no data will
/// be available. If it is expanded, a node handle will be available.
pub fn get_target(&self) -> Node<'a, T, S, A> {
Node { graph: self.graph, id: self.arc().target, }
}
}

View File

@ -1,18 +1,15 @@
extern crate symbol_map;
#[cfg(test)] extern crate crossbeam;
mod hidden;
pub(crate) mod base;
pub mod mutators;
pub mod nav;
pub mod search;
use std::hash::Hash;
use self::hidden::base::*;
use self::hidden::nav::{Node, make_node};
use self::hidden::mutators::{MutEdge, MutNode, make_mut_edge, make_mut_node};
use ::symbol_map::indexing::{Indexing, Insertion};
use ::symbol_map::SymbolId;
use base::{EdgeId, RawEdge, RawVertex, VertexId};
use mutators::{MutEdge, MutNode};
use nav::Node;
use symbol_map::indexing::{Indexing, Insertion};
use symbol_map::SymbolId;
/// A search graph.
///
@ -30,14 +27,20 @@ use ::symbol_map::SymbolId;
/// Vertices are addressed by content. To examine graph contents, obtain a node
/// handle with `get_node`. To modify graph contents, add new root vertices with
/// `add_root` and retrieve extant vertices with `get_node_mut`.
pub struct Graph<T, S, A> where T: Hash + Eq + Clone {
pub struct Graph<T, S, A>
where
T: Hash + Eq + Clone,
{
/// Lookup table that maps from game states to `VertexId`.
state_ids: symbol_map::indexing::HashIndexing<T, VertexId>,
vertices: Vec<RawVertex<S>>, // Indexed by VertexId.
arcs: Vec<RawEdge<A>>, // Indexed by EdgeId.
vertices: Vec<RawVertex<S>>, // Indexed by VertexId.
arcs: Vec<RawEdge<A>>, // Indexed by EdgeId.
}
impl<T, S, A> Graph<T, S, A> where T: Hash + Eq + Clone {
impl<T, S, A> Graph<T, S, A>
where
T: Hash + Eq + Clone,
{
/// Creates an empty `Graph` with no vertices or edges.
pub fn new() -> Self {
Graph {
@ -92,7 +95,11 @@ impl<T, S, A> Graph<T, S, A> where T: Hash + Eq + Clone {
let arc_id = EdgeId(self.arcs.len());
self.get_vertex_mut(source).children.push(arc_id);
self.get_vertex_mut(target).parents.push(arc_id);
self.arcs.push(RawEdge { data: data, source: source, target: target, });
self.arcs.push(RawEdge {
data: data,
source: source,
target: target,
});
arc_id
}
@ -101,7 +108,7 @@ impl<T, S, A> Graph<T, S, A> where T: Hash + Eq + Clone {
/// If `state` does not correspond to a known game state, returns `None`.
pub fn get_node<'s>(&'s self, state: &T) -> Option<Node<'s, T, S, A>> {
match self.state_ids.get(state) {
Some(symbol) => Some(make_node(self, *symbol.id())),
Some(symbol) => Some(Node::new(self, *symbol.id())),
None => None,
}
}
@ -111,7 +118,7 @@ impl<T, S, A> Graph<T, S, A> where T: Hash + Eq + Clone {
/// If `state` does not correspond to a known game state, returns `None`.
pub fn get_node_mut<'s>(&'s mut self, state: &T) -> Option<MutNode<'s, T, S, A>> {
match self.state_ids.get(state).map(|s| s.id().clone()) {
Some(id) => Some(make_mut_node(self, id)),
Some(id) => Some(MutNode::new(self, id)),
None => None,
}
}
@ -129,9 +136,9 @@ impl<T, S, A> Graph<T, S, A> where T: Hash + Eq + Clone {
Insertion::New(id) => {
self.add_raw_vertex(data);
id
},
}
};
make_mut_node(self, node_id)
MutNode::new(self, node_id)
}
/// Adds an edge from the vertex with state data `source` to the vertex with
@ -141,28 +148,37 @@ impl<T, S, A> Graph<T, S, A> where T: Hash + Eq + Clone {
///
/// The edge that is created will have the data `edge_data`. Returns a
/// mutable edge handle for that edge.
pub fn add_edge<'s, F, G>(&'s mut self, source: T, source_data: F, dest: T, dest_data: G,
edge_data: A) -> MutEdge<'s, T, S, A>
where F: for<'b> FnOnce(Node<'b, T, S, A>) -> S, G: for<'b> FnOnce(Node<'b, T, S, A>) -> S {
let source_id = match self.state_ids.get_or_insert(source).map(|s| s.id().clone()) {
Insertion::Present(id) => id,
Insertion::New(id) => {
let data = source_data(make_node(self, id));
self.add_raw_vertex(data);
id
},
};
let dest_id = match self.state_ids.get_or_insert(dest).map(|s| s.id().clone()) {
Insertion::Present(id) => id,
Insertion::New(id) => {
let data = dest_data(make_node(self, id));
self.add_raw_vertex(data);
id
},
};
let edge_id = self.add_raw_edge(edge_data, source_id, dest_id);
make_mut_edge(self, edge_id)
}
pub fn add_edge<'s, F, G>(
&'s mut self,
source: T,
source_data: F,
dest: T,
dest_data: G,
edge_data: A,
) -> MutEdge<'s, T, S, A>
where
F: for<'b> FnOnce(Node<'b, T, S, A>) -> S,
G: for<'b> FnOnce(Node<'b, T, S, A>) -> S,
{
let source_id = match self.state_ids.get_or_insert(source).map(|s| s.id().clone()) {
Insertion::Present(id) => id,
Insertion::New(id) => {
let data = source_data(Node::new(self, id));
self.add_raw_vertex(data);
id
}
};
let dest_id = match self.state_ids.get_or_insert(dest).map(|s| s.id().clone()) {
Insertion::Present(id) => id,
Insertion::New(id) => {
let data = dest_data(Node::new(self, id));
self.add_raw_vertex(data);
id
}
};
let edge_id = self.add_raw_edge(edge_data, source_id, dest_id);
MutEdge::new(self, edge_id)
}
/// Returns the number of vertices in the graph.
pub fn vertex_count(&self) -> usize {
@ -194,40 +210,38 @@ impl<T, S, A> Graph<T, S, A> where T: Hash + Eq + Clone {
/// As `retain_reachable_from`, but working over raw `VertexId`s instead of
/// root data.
fn retain_reachable_from_ids(&mut self, root_ids: &[VertexId]) {
self::hidden::mutators::mark_compact::Collector::retain_reachable(self, root_ids);
mutators::mark_compact::Collector::retain_reachable(self, root_ids);
}
}
#[cfg(test)]
mod test {
use ::crossbeam;
use crossbeam_utils::thread;
use std::sync::Arc;
use std::thread;
type Graph = ::Graph<&'static str, &'static str, &'static str>;
type Graph = crate::Graph<&'static str, &'static str, &'static str>;
#[test]
fn send_to_thread_safe_ok() {
let mut g = Graph::new();
g.add_edge("root", |_| "root_data", "0", |_| "0_data", "root_0_data");
g.add_edge("root", |_| "root_data", "1", |_| "1_data", "root_1_data");
let g = Arc::new(g);
let t1 = {
let g = g.clone();
thread::spawn(move || g.get_node(&"root").map(|n| n.get_id()))
};
let t2 = {
let g = g.clone();
thread::spawn(move || g.get_node(&"1").map(|n| n.get_id()))
};
match t1.join() {
Ok(Some(id)) => assert_eq!(id, 0),
_ => panic!(),
}
match t2.join() {
Ok(Some(id)) => assert_eq!(id, 2),
_ => panic!(),
}
let graph = Arc::new(g);
thread::scope(move |s| {
let g = graph.clone();
let t1 = s.spawn(move |_| g.get_node(&"root").map(|n| n.get_id()));
let g = graph.clone();
let t2 = s.spawn(move |_| g.get_node(&"1").map(|n| n.get_id()));
match t1.join() {
Ok(Some(id)) => assert_eq!(id, 0),
_ => panic!(),
}
match t2.join() {
Ok(Some(id)) => assert_eq!(id, 2),
_ => panic!(),
}
})
.unwrap();
}
#[test]
@ -236,22 +250,18 @@ mod test {
g.add_edge("root", |_| "root_data", "0", |_| "0_data", "root_0_data");
g.add_edge("root", |_| "root_data", "1", |_| "1_data", "root_1_data");
let g = &g;
let t1 = crossbeam::scope(
move |scope|
scope.spawn(move ||
g.get_node(&"root").map(|n| n.get_id())));
let t2 = crossbeam::scope(
move |scope|
scope.spawn(move ||
g.get_node(&"1").map(|n| n.get_id())));
match t1.join() {
Some(id) => assert_eq!(id, 0),
_ => panic!(),
}
match t2.join() {
Some(id) => assert_eq!(id, 2),
_ => panic!(),
}
thread::scope(|s| {
let t1 = s.spawn(move |_| g.get_node(&"root").map(|n| n.get_id()));
let t2 = s.spawn(move |_| g.get_node(&"1").map(|n| n.get_id()));
match t1.join() {
Ok(Some(id)) => assert_eq!(id, 0),
_ => panic!(),
}
match t2.join() {
Ok(Some(id)) => assert_eq!(id, 2),
_ => panic!(),
}
})
.unwrap();
}
}

View File

@ -1,8 +0,0 @@
//! Support for navigation of a graph that allows modifications to graph
//! topology and full read-write access to graph data.
//!
//! The data structures in this module require a read-write borrow of an
//! underlying graph. As a result, only one handle may be active at any given
//! time.
pub use super::hidden::mutators::{MutChildList, MutEdge, MutNode, MutParentList};

View File

@ -12,10 +12,10 @@ use std::hash::Hash;
use std::mem;
use std::ptr;
use ::Graph;
use ::hidden::base::{EdgeId, VertexId};
use ::symbol_map::SymbolId;
use ::symbol_map::indexing::{HashIndexing, Indexing};
use crate::Graph;
use crate::base::{EdgeId, VertexId};
use symbol_map::SymbolId;
use symbol_map::indexing::{HashIndexing, Indexing};
/// Permutes `data` so that element `i` of data is reassigned to be at index
/// `f(i)`.
@ -65,7 +65,7 @@ impl<'a, T, S, A> Collector<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a,
/// function is not exported by the crate, so you probably want the
/// `retain_reachable()` method of `MutNode` or the `retain_reachable_from`
/// method of `Graph`.
pub fn retain_reachable(graph: &'a mut Graph<T, S, A>, roots: &[VertexId]) {
pub(crate) fn retain_reachable(graph: &'a mut Graph<T, S, A>, roots: &[VertexId]) {
let mut c = Collector::new(graph);
c.mark(roots);
c.sweep();
@ -182,7 +182,7 @@ impl<'a, T, S, A> Collector<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a,
// Drop unmarked vertices.
self.graph.vertices.truncate(self.marked_state_count);
// Reassign and compact vertex parents.
for mut vertex in self.graph.vertices.iter_mut() {
for vertex in self.graph.vertices.iter_mut() {
let mut store_index = 0;
for scan_index in 0..vertex.parents.len() {
let old_arc_id = vertex.parents[scan_index];
@ -214,13 +214,13 @@ impl<'a, T, S, A> Collector<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a,
#[cfg(test)]
mod test {
use super::Collector;
use ::hidden::base::{EdgeId, VertexId, RawEdge, RawVertex};
use ::symbol_map::indexing::{HashIndexing, Indexing};
use crate::base::{EdgeId, VertexId, RawEdge, RawVertex};
use symbol_map::indexing::{HashIndexing, Indexing};
use std::collections::HashMap;
use std::mem;
type Graph = ::Graph<&'static str, &'static str, &'static str>;
type Graph = crate::Graph<&'static str, &'static str, &'static str>;
fn empty_graph() -> Graph {
let g = Graph::new();

View File

@ -1,16 +1,21 @@
//! Support for navigation of a graph that allows modifications to graph
//! topology and full read-write access to graph data.
//!
//! The data structures in this module require a read-write borrow of an
//! underlying graph. As a result, only one handle may be active at any given
//! time.
use std::clone::Clone;
use std::cmp::Eq;
use std::hash::Hash;
use ::Graph;
use ::hidden::base::*;
use ::hidden::nav::{ChildList, ChildListIter, Edge, Node, ParentList, ParentListIter};
use ::hidden::nav::{make_child_list, make_edge, make_node, make_parent_list};
use ::symbol_map::SymbolId;
use ::symbol_map::indexing::{Indexing, Insertion};
use crate::Graph;
use crate::base::{EdgeId, RawEdge, RawVertex, VertexId};
use crate::nav::{ChildList, ChildListIter, Edge, Node, ParentList, ParentListIter};
use symbol_map::SymbolId;
use symbol_map::indexing::{Indexing, Insertion};
pub mod path;
pub mod mark_compact;
pub(crate) mod mark_compact;
/// Mutable handle to a graph vertex ("node handle").
///
@ -21,19 +26,18 @@ pub mod mark_compact;
/// mutation of graph topology (adding edges). Edges may be added
/// using the handle returned by `get_child_adder` or `to_child_adder`.
pub struct MutNode<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a mut Graph<T, S, A>,
id: VertexId,
pub(crate) graph: &'a mut Graph<T, S, A>,
pub(crate) id: VertexId,
}
/// Creates a new `MutNode` for the given graph and gamestate. This method is
/// not exported by the crate because it exposes implementation details. It is
/// used to provide a public cross-module interface for creating new `MutNode`s.
pub fn make_mut_node<'a, T, S, A>(graph: &'a mut Graph<T, S, A>, id: VertexId) -> MutNode<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
MutNode { graph: graph, id: id, }
}
impl<'a, T, S, A> MutNode<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
/// Creates a new `MutNode` for the given graph and gamestate. This method is
/// not exported by the crate because it exposes implementation details.
pub(crate) fn new(graph: &'a mut Graph<T, S, A>, id: VertexId) -> Self {
MutNode { graph, id }
}
fn vertex<'s>(&'s self) -> &'s RawVertex<S> {
self.graph.get_vertex(self.id)
}
@ -81,7 +85,7 @@ impl<'a, T, S, A> MutNode<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A
/// Returns a traversible list of outgoing edges. Its lifetime will be
/// limited to a local borrow of `self`.
pub fn get_child_list<'s>(&'s self) -> ChildList<'s, T, S, A> {
make_child_list(self.graph, self.id)
ChildList::new(self.graph, self.id)
}
/// Returns a traversible list of outgoing edges. Its lifetime will be
@ -99,7 +103,7 @@ impl<'a, T, S, A> MutNode<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A
/// Returns a traversible list of incoming edges. Its lifetime will be
/// limited to a local borrow of `self`.
pub fn get_parent_list<'s>(&'s self) -> ParentList<'s, T, S, A> {
make_parent_list(self.graph, self.id)
ParentList::new(self.graph, self.id)
}
/// Returns a traversible list of incoming edges. Its lifetime will be
@ -119,13 +123,13 @@ impl<'a, T, S, A> MutNode<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A
/// `self`. The source graph is still considered to have a mutable borrow in
/// play, but the resulting node can be cloned freely.
pub fn to_node(self) -> Node<'a, T, S, A> {
make_node(self.graph, self.id)
Node::new(self.graph, self.id)
}
/// Returns a non-mutating node obtained by borrowing this node. Returns a
/// value whose lifetime is limited to a borrow of `self`.
pub fn get_node<'s>(&'s self) -> Node<'s, T, S, A> {
make_node(self.graph, self.id)
Node::new(self.graph, self.id)
}
/// Prunes the underlying graph by removing components not reachable from
@ -159,7 +163,7 @@ impl<'a, T, S, A> MutChildList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S:
/// Returns an edge handle for the `i`th edge.
pub fn get_edge<'s>(&'s self, i: usize) -> Edge<'s, T, S, A> {
make_edge(self.graph, self.vertex().children[i])
Edge::new(self.graph, self.vertex().children[i])
}
/// Returns an edge handle for the `i`th edge. Its lifetime will be limited
@ -179,7 +183,7 @@ impl<'a, T, S, A> MutChildList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S:
/// Returns a node handle for the vertex these edges originate from. Its
/// lifetime will be limited to a local borrow of `self`.
pub fn get_source_node<'s>(&'s self) -> Node<'s, T, S, A> {
make_node(self.graph, self.id)
Node::new(self.graph, self.id)
}
/// Returns a mutable node handle for the vertex these edges originate
@ -259,7 +263,7 @@ impl<'a, T, S, A> MutParentList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S:
/// Returns a node handle for the vertex these edges originate terminate
/// on. Its lifetime will be limited to a local borrow of `self`.
pub fn get_target_node<'s>(&'s self) -> Node<'s, T, S, A> {
make_node(self.graph, self.id)
Node::new(self.graph, self.id)
}
/// Returns a mutable node handle for the vertex these edges terminate
@ -278,7 +282,7 @@ impl<'a, T, S, A> MutParentList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S:
/// Returns a handle to the `i`th edge. Its lifetime will be limited to a
/// local borrow of `self`.
pub fn get_edge<'s>(&'s self, i: usize) -> Edge<'s, T, S, A> {
make_edge(self.graph, self.vertex().parents[i])
Edge::new(self.graph, self.vertex().parents[i])
}
/// Returns a mutable handle to the `i`th edge. Its lifetime will be limited
@ -350,15 +354,13 @@ pub struct MutEdge<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
id: EdgeId,
}
/// Creates a new `MutEdge` for the given graph and gamestate. This method is
/// not exported by the crate because it exposes implementation details. It is
/// used to provide a public cross-module interface for creating new `MutNode`s.
pub fn make_mut_edge<'a, T, S, A>(graph: &'a mut Graph<T, S, A>, id: EdgeId) -> MutEdge<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
MutEdge { graph: graph, id: id, }
impl<'a, T, S, A> MutEdge<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
/// Creates a new `MutEdge` for the given graph and gamestate. This method is
/// not exported by the crate because it exposes implementation details.
pub(crate) fn new(graph: &'a mut Graph<T, S, A>, id: EdgeId) -> Self {
MutEdge { graph, id }
}
impl<'a, T, S, A> MutEdge<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
fn arc(&self) -> &RawEdge<A> {
self.graph.get_arc(self.id)
}
@ -387,7 +389,7 @@ impl<'a, T, S, A> MutEdge<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A
/// be available. If it is expanded, a node handle will be available, with
/// its lifetime limited to a local borrow of `self`.
pub fn get_target<'s>(&'s self) -> Node<'s, T, S, A> {
make_node(self.graph, self.arc().target)
Node::new(self.graph, self.arc().target)
}
/// Returns the target of this edge. If the edge is unexpanded, an
@ -411,7 +413,7 @@ impl<'a, T, S, A> MutEdge<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A
/// Returns a node handle for the source of this edge. Its lifetime will be
/// limited to a local borrow of `self`.
pub fn get_source<'s>(&'s self) -> Node<'s, T, S, A> {
make_node(self.graph, self.arc().source)
Node::new(self.graph, self.arc().source)
}
/// Returns a mutable node handle for the source of this edge. Its lifetime
@ -434,6 +436,7 @@ impl<'a, T, S, A> MutEdge<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A
/// `self`. The source graph is still considered to have a mutable borrow in
/// play, but the resulting edge can be cloned freely.
pub fn to_edge(self) -> Edge<'a, T, S, A> {
make_edge(self.graph, self.id)
Edge::new(self.graph, self.id)
}
}

0
src/mutators/path.rs Normal file
View File

View File

@ -7,4 +7,282 @@
//! read-only references (such as atomic types and `std::cell::RefCell`) may be
//! modified through these structures.
pub use super::hidden::nav::{ChildList, ChildListIter, Edge, Node, ParentList, ParentListIter};
use std::hash::Hash;
use std::iter::Iterator;
use crate::Graph;
use crate::base::{EdgeId, VertexId, RawEdge, RawVertex};
use symbol_map::SymbolId;
/// Immutable handle to a graph vertex ("node handle").
///
/// This zipper-like type enables traversal of a graph along the vertex's
/// incoming and outgoing edges.
pub struct Node<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: VertexId,
}
impl<'a, T, S, A> Node<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
/// Creates a new `Node` for the given graph and gamestate. This method is
/// not exported by the crate because it exposes implementation details.
pub(crate) fn new(graph: &'a Graph<T, S, A>, id: VertexId) -> Self {
Node { graph, id }
}
fn children(&self) -> &'a [EdgeId] {
&self.graph.get_vertex(self.id).children
}
/// Returns the canonical label that is used to address this `Node`.
///
/// Graph instances which project multiple labels to the same vertex will
/// consistently return a single value, regardless of which value was used
/// to obtain this node handle.
pub fn get_label(&self) -> &T {
&self.graph.get_state(self.id).unwrap()
}
/// Returns an immutable ID that is guaranteed to identify this vertex
/// uniquely within its graph. This ID may change when the graph is mutated.
pub fn get_id(&self) -> usize {
self.id.as_usize()
}
fn parents(&self) -> &'a [EdgeId] {
&self.graph.get_vertex(self.id).parents
}
/// Returns the data at this vertex.
pub fn get_data(&self) -> &'a S {
&self.graph.get_vertex(self.id).data
}
/// Returns true iff this vertex has no outgoing edges (regardless of
/// whether they are expanded).
pub fn is_leaf(&self) -> bool {
self.children().is_empty()
}
/// Returns true iff this vertex has no incoming edges.
pub fn is_root(&self) -> bool {
self.parents().is_empty()
}
/// Returns a traversible list of outgoing edges.
pub fn get_child_list(&self) -> ChildList<'a, T, S, A> {
ChildList { graph: self.graph, id: self.id, }
}
/// Returns a traversible list of incoming edges.
pub fn get_parent_list(&self) -> ParentList<'a, T, S, A> {
ParentList { graph: self.graph, id: self.id, }
}
}
/// A traversible list of a vertex's outgoing edges.
pub struct ChildList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: VertexId,
}
impl<'a, T, S, A> ChildList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
/// Creates a new `ChildList` for the given graph and gamestate. This method
/// is not exported by the crate because it exposes implementation details.
pub(crate) fn new(graph: &'a Graph<T, S, A>, id: VertexId) -> Self {
ChildList { graph, id }
}
fn vertex(&self) -> &'a RawVertex<S> {
self.graph.get_vertex(self.id)
}
/// Returns the number of edges.
pub fn len(&self) -> usize {
self.vertex().children.len()
}
/// Returns true iff there are no outgoing edges.
pub fn is_empty(&self) -> bool {
self.vertex().children.is_empty()
}
/// Returns a node handle for the vertex these edges originate from.
pub fn get_source_node(&self) -> Node<'a, T, S, A> {
Node { graph: self.graph, id: self.id, }
}
/// Returns an edge handle for the `i`th edge.
pub fn get_edge(&self, i: usize) -> Edge<'a, T, S, A> {
Edge { graph: self.graph, id: self.vertex().children[i], }
}
/// Returns an iterator over child edges.
pub fn iter(&self) -> ChildListIter<'a, T, S, A> {
ChildListIter { graph: self.graph, id: self.id, i: 0, }
}
}
/// Iterator over a vertex's child edges.
pub struct ChildListIter<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: VertexId,
i: usize,
}
impl <'a, T, S, A> ChildListIter<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
fn children(&self) -> &'a [EdgeId] {
&self.graph.get_vertex(self.id).children
}
}
impl<'a, T, S, A> Iterator for ChildListIter<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
type Item = Edge<'a, T, S, A>;
fn next(&mut self) -> Option<Edge<'a, T, S, A>> {
let cs = self.children();
if self.i >= cs.len() {
None
} else {
let e = Edge::new(self.graph, cs[self.i]);
self.i += 1;
Some(e)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let l = self.children().len();
if l <= self.i {
(0, Some(0))
} else {
(l - self.i, Some(l - self.i))
}
}
}
/// A traversible list of a vertex's incoming edges.
pub struct ParentList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: VertexId,
}
impl<'a, T, S, A> ParentList<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
/// Creates a new `ParentList` for the given graph and gamestate. This
/// method is not exported by the crate because it exposes implementation
/// details.
pub(crate) fn new(graph: &'a Graph<T, S, A>, id: VertexId) -> Self {
ParentList { graph, id, }
}
fn vertex(&self) -> &'a RawVertex<S> {
self.graph.get_vertex(self.id)
}
/// Returns the number of edges.
pub fn len(&self) -> usize {
self.vertex().parents.len()
}
/// Returns true iff there are no incoming edges.
pub fn is_empty(&self) -> bool {
self.vertex().parents.is_empty()
}
/// Returns a node handle for the vertex these edges point to.
pub fn target_node(&self) -> Node<'a, T, S, A> {
Node { graph: self.graph, id: self.id, }
}
/// Returns an edge handle for the `i`th edge.
pub fn get_edge(&self, i: usize) -> Edge<'a, T, S, A> {
Edge { graph: self.graph, id: self.vertex().parents[i] }
}
/// Returns an iterator over parent edges.
pub fn iter(&self) -> ParentListIter<'a, T, S, A> {
ParentListIter { graph: self.graph, id: self.id, i: 0, }
}
}
/// Iterator over a vertex's parent edges.
pub struct ParentListIter<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: VertexId,
i: usize,
}
impl<'a, T, S, A> ParentListIter<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
fn parents(&self) -> &'a [EdgeId] {
&self.graph.get_vertex(self.id).parents
}
}
impl<'a, T, S, A> Iterator for ParentListIter<'a, T, S, A>
where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
type Item = Edge<'a, T, S, A>;
fn next(&mut self) -> Option<Edge<'a, T, S, A>> {
let ps = self.parents();
if self.i >= ps.len() {
None
} else {
let e = Edge::new(self.graph, ps[self.i]);
self.i += 1;
Some(e)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let l = self.parents().len();
if l <= self.i {
(0, Some(0))
} else {
(l - self.i, Some(l - self.i))
}
}
}
/// Immutable handle to a graph edge ("edge handle").
///
/// This zipper-like type enables traversal of a graph along the edge's source
/// and target vertices.
pub struct Edge<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
graph: &'a Graph<T, S, A>,
id: EdgeId,
}
impl<'a, T, S, A> Edge<'a, T, S, A> where T: Hash + Eq + Clone + 'a, S: 'a, A: 'a {
/// Creates a new `Edge` for the given graph and gamestate. This method is
/// not exported by the crate because it exposes implementation details.
pub(crate) fn new(graph: &'a Graph<T, S, A>, id: EdgeId) -> Edge<'a, T, S, A> {
Edge { graph, id }
}
fn arc(&self) -> &'a RawEdge<A> {
self.graph.get_arc(self.id)
}
/// Returns an immutable ID that is guaranteed to identify this edge
/// uniquely within its graph. This ID may change when the graph is
/// mutated.
pub fn get_id(&self) -> usize {
self.id.as_usize()
}
/// Returns the data at this edge.
pub fn get_data(&self) -> &'a A {
&self.arc().data
}
/// Returns a node handle for this edge's source vertex.
pub fn get_source(&self) -> Node<'a, T, S, A> {
Node { graph: self.graph, id: self.arc().source, }
}
/// Returns the target of this edge. If the edge is unexpanded, no data will
/// be available. If it is expanded, a node handle will be available.
pub fn get_target(&self) -> Node<'a, T, S, A> {
Node { graph: self.graph, id: self.arc().target, }
}
}

View File

@ -1,3 +1,704 @@
//! Data structures and algorithms for local graph search.
//!
//! The main data structure in this module is `Stack`, which provides
//! memory-safe construction of the path that is traversed when performing local
//! search on a graph.
use std::clone::Clone;
use std::cmp::Eq;
use std::error::Error;
use std::fmt;
use std::hash::Hash;
use std::iter::Iterator;
use crate::Graph;
use crate::base::{EdgeId, VertexId};
use crate::mutators::MutNode;
use crate::nav::{Edge, Node};
/// Errors that may arise during search.
#[derive(Debug)]
pub enum SearchError<E> where E: Error {
/// A search operation selected a child index that was out of bounds.
ChildBounds {
/// The index of the child that was requested.
requested_index: usize,
/// The actual number of chidren (which `requested_index` exceeds).
child_count: usize
},
/// A search operation selected a parent index that was out of bounds.
ParentBounds {
/// The index of the parent that was requested.
requested_index: usize,
/// The actual number of parents (which `requested_index` exceeds).
parent_count: usize
},
/// A search operation encountered an error.
SelectionError(E),
}
/// Tracks the path through a graph that is followed when performing local search.
///
/// In this case, "local search" is a process that starts focused on a single
/// vertex and incrementally updates which vertex is the focus by traversing
/// parent or child edges. The history of such operations can be described as a
/// series of (vertex, edge) pairs, and a `Stack` encapsulates this
/// history.
///
/// A `Stack` points to a head, which is either a graph vertex (whose
/// incidental edges can then be traversed) or an unexpanded edge (if a
/// traversal operation chose to follow an unexpanded edge). Operations which
/// modify graph topology (such as expanding edges) may cause the search path's
/// internal state to fall out of sync with the graph's state, so graph elements
/// exposed using the read-only `Node` and `Edge` types.
///
/// A path may be consumed to yield a read-write view of the underlying graph
/// with the `to_head` method.
pub struct Stack<'a, T, S, A> where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a {
/// The graph that is being searched.
graph: &'a mut Graph<T, S, A>,
/// The edges that have been traversed.
path: Vec<EdgeId>,
/// The path head.
head: VertexId,
}
/// Indicates which edge of a vertex to traverse. Edges are denoted by a 0-based
/// index. This type is used by functions provided during graph search to
/// indicate which child or parent edges to traverse.
pub enum Traversal {
/// Traverse the given child.
Child(usize),
/// Traverse the given parent.
Parent(usize),
}
/// Iterates over elements of a search path, in the order in which they were
/// traversed, ending with the head.
pub struct StackIter<'a, 's, T, S, A> where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a, 'a: 's {
/// The path being iterated over.
path: &'s Stack<'a, T, S, A>,
/// The position through path.
position: usize,
}
/// Sum type for path elements. All elements except the head are represented
/// with the `StackItem::Item` variant.
pub enum StackItem<'a, T, S, A> where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a {
/// Non-head item, a (vertex, edge) pair.
Item(Edge<'a, T, S, A>),
/// The path head, which may resolve to a vertex or an unexpanded edge.
Head(Node<'a, T, S, A>),
}
impl<E> fmt::Display for SearchError<E> where E: Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SearchError::ChildBounds { requested_index, child_count } =>
write!(f, "Search chose child {}/{}", requested_index, child_count),
SearchError::ParentBounds { requested_index, parent_count } =>
write!(f, "Search chose parent {}/{}", requested_index, parent_count),
SearchError::SelectionError(ref e) => write!(f, "Error in search operation: {}", e),
}
}
}
impl<E> Error for SearchError<E> where E: Error {
fn description(&self) -> &str {
match *self {
SearchError::ChildBounds { requested_index: _, child_count: _ } => "child out of bounds",
SearchError::ParentBounds { requested_index: _, parent_count: _ } => "parent out of bounds",
SearchError::SelectionError(ref e) => e.description(),
}
}
fn cause(&self) -> Option<&Error> {
match *self {
SearchError::SelectionError(ref e) => Some(e),
_ => None,
}
}
}
impl<'a, T, S, A> Stack<'a, T, S, A> where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a {
/// Creates a new `Stack` from a mutable reference into a graph.
pub fn new(node: MutNode<'a, T, S, A>) -> Self {
Stack {
graph: node.graph,
path: Vec::new(),
head: node.id,
}
}
/// Returns the number of elements in the path. Since a path always has a
/// head, there is always at least 1 element.
pub fn len(&self) -> usize {
self.path.len() + 1
}
/// Removes the most recently traversed element from the path, if
/// any. Returns a handle for any edge that was removed.
pub fn pop<'s>(&'s mut self) -> Option<Edge<'s, T, S, A>> {
match self.path.pop() {
Some(edge_id) => {
self.head = self.graph.get_arc(edge_id).source;
Some(Edge::new(self.graph, edge_id))
},
None => None,
}
}
/// Returns a read-only view of the head element.
pub fn head<'s>(&'s self) -> Node<'s, T, S, A> {
Node::new(self.graph, self.head)
}
/// Consumes the path and returns a mutable view of its head.
pub fn to_head(self) -> MutNode<'a, T, S, A> {
MutNode { graph: self.graph, id: self.head, }
}
/// Grows the path by consulting a function of the current head. If this
/// function `f` returns `Ok(Some(Traversal::Child(i)))`, then the `i`th
/// child of the current head is pushed onto the path. If it returns
/// `Ok(Some(Traversal::Parent(i)))`, then the `i`th parent of the current
/// head is pushed onto the path.
///
/// The decision not to traverse any edge may be made by returning
/// `Ok(None)`, while `Err(E)` should be returned for any errors.
///
/// Returns an `Ok(Option(e))` for any edge `e` that is traversed, or
/// `Err(e)` if an error was encountered.
pub fn push<'s, F, E>(&'s mut self, mut f: F) -> Result<Option<Edge<'s, T, S, A>>, SearchError<E>>
where F: FnMut(&Node<'s, T, S, A>) -> Result<Option<Traversal>, E>, E: Error {
let node = Node::new(self.graph, self.head);
match f(&node) {
Ok(Some(Traversal::Child(i))) => {
let children = node.get_child_list();
if i >= children.len() {
Err(SearchError::ChildBounds {
requested_index: i, child_count: children.len() })
} else {
let child = children.get_edge(i);
self.path.push(EdgeId(child.get_id()));
self.head = VertexId(child.get_target().get_id());
Ok(Some(child))
}
},
Ok(Some(Traversal::Parent(i))) => {
let parents = node.get_parent_list();
if i >= parents.len() {
Err(SearchError::ParentBounds {
requested_index: i, parent_count: parents.len() })
} else {
let parent = parents.get_edge(i);
self.path.push(EdgeId(parent.get_id()));
self.head = VertexId(parent.get_source().get_id());
Ok(Some(parent))
}
},
Ok(None) => Ok(None),
Err(e) => Err(SearchError::SelectionError(e)),
}
}
/// Returns an iterator over path elements. Iteration is in order of
/// traversal (i.e., the last element of the iteration is the path head).
pub fn iter<'s>(&'s self) -> StackIter<'a, 's, T, S, A> {
StackIter::new(self)
}
/// Returns the `i`th item of the path. Path items are indexed in order of
/// traversal (i.e., the last element is the path head).
pub fn item<'s>(&'s self, i: usize) -> Option<StackItem<'s, T, S, A>> {
if i == self.path.len() {
Some(StackItem::Head(self.head()))
} else {
match self.path.get(i) {
Some(edge_id) =>
Some(StackItem::Item(Edge::new(self.graph, *edge_id))),
None => None,
}
}
}
}
impl<'a, 's, T, S, A> StackIter<'a, 's, T, S, A> where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a, 'a: 's {
/// Creates a new path iterator from a borrow of a path.
fn new(path: &'s Stack<'a, T, S, A>) -> Self {
StackIter {
path: path,
position: 0,
}
}
}
impl<'a, 's, T, S, A> Iterator for StackIter<'a, 's, T, S, A>
where T: 'a + Hash + Eq + Clone, S: 'a, A: 'a, 'a: 's {
type Item = StackItem<'s, T, S, A>;
fn next(&mut self) -> Option<StackItem<'s, T, S, A>> {
let i = self.position;
self.position += 1;
self.path.item(i)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.path.len() - self.position;
(len, Some(len))
}
}
#[cfg(test)]
mod test {
use std::error::Error;
use std::fmt;
use super::{SearchError, StackItem, Traversal};
type Graph = crate::Graph<&'static str, &'static str, ()>;
type Node<'a> = crate::nav::Node<'a, &'static str, &'static str, ()>;
type Stack<'a> = super::Stack<'a, &'static str, &'static str, ()>;
fn add_edge(g: &mut Graph, source: &'static str, dest: &'static str) {
g.add_edge(source, |_| source, dest, |_| dest, ());
}
#[derive(Debug)]
struct MockError(());
impl Error for MockError {
fn description(&self) -> &str { "toy error" }
}
impl fmt::Display for MockError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "toy error")
}
}
#[test]
fn instantiation_ok() {
let mut g = Graph::new();
let root = g.add_root("root", "root");
let path = Stack::new(root);
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
}
#[test]
fn push_no_children_ok() {
let mut g = Graph::new();
let root = g.add_root("root", "root");
let mut path = Stack::new(root);
assert_eq!(1, path.len());
fn no_traversal<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
Ok(None)
}
match path.push(no_traversal) {
Ok(None) => (),
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
}
#[test]
fn push_no_children_err() {
let mut g = Graph::new();
let root = g.add_root("root", "root");
let mut path = Stack::new(root);
assert_eq!(1, path.len());
fn traverse_first_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
assert!(n.get_child_list().is_empty());
Ok(Some(Traversal::Child(0)))
}
match path.push(traverse_first_child) {
Err(SearchError::ChildBounds { requested_index, child_count }) => {
assert_eq!(0, requested_index);
assert_eq!(0, child_count);
},
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
}
#[test]
fn push_to_child_ok() {
let mut g = Graph::new();
add_edge(&mut g, "A", "B1");
add_edge(&mut g, "A", "B2");
add_edge(&mut g, "B1", "C");
add_edge(&mut g, "B2", "D");
fn traverse_second_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("A", *n.get_data());
let children = n.get_child_list();
assert_eq!(2, children.len());
assert_eq!("B1", *children.get_edge(0).get_target().get_data());
assert_eq!("B2", *children.get_edge(1).get_target().get_data());
Ok(Some(Traversal::Child(1)))
}
let mut path = Stack::new(g.get_node_mut(&"A").unwrap());
assert_eq!(1, path.len());
match path.push(traverse_second_child) {
Ok(Some(e)) => {
assert_eq!("A", *e.get_source().get_data());
assert_eq!("B2", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(2, path.len());
fn traverse_first_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("B2", *n.get_data());
assert_eq!(1, n.get_child_list().len());
Ok(Some(Traversal::Child(0)))
}
match path.push(traverse_first_child) {
Ok(Some(e)) => {
assert_eq!("B2", *e.get_source().get_data());
assert_eq!("D", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(3, path.len());
assert_eq!("D", *path.head().get_data());
}
#[test]
fn push_to_child_err_ok() {
let mut g = Graph::new();
add_edge(&mut g, "A", "B1");
add_edge(&mut g, "A", "B2");
add_edge(&mut g, "B1", "C");
add_edge(&mut g, "B2", "D");
fn traverse_err<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("A", *n.get_data());
Err(MockError(()))
}
let mut path = Stack::new(g.get_node_mut(&"A").unwrap());
assert_eq!(1, path.len());
match path.push(traverse_err) {
Err(SearchError::SelectionError(_)) => (),
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("A", *path.head().get_data())
}
#[test]
fn push_no_parents_ok() {
let mut g = Graph::new();
let root = g.add_root("root", "root");
let mut path = Stack::new(root);
assert_eq!(1, path.len());
fn no_traversal<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
Ok(None)
}
match path.push(no_traversal) {
Ok(None) => (),
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
}
#[test]
fn push_no_parents_err() {
let mut g = Graph::new();
let root = g.add_root("root", "root");
let mut path = Stack::new(root);
assert_eq!(1, path.len());
fn traverse_first_parent<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
assert!(n.get_parent_list().is_empty());
Ok(Some(Traversal::Parent(0)))
}
match path.push(traverse_first_parent) {
Err(SearchError::ParentBounds { requested_index, parent_count }) => {
assert_eq!(0, requested_index);
assert_eq!(0, parent_count);
},
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
}
#[test]
fn push_to_parent_ok() {
let mut g = Graph::new();
add_edge(&mut g, "A", "B1");
add_edge(&mut g, "A", "B2");
add_edge(&mut g, "B1", "C");
add_edge(&mut g, "B2", "D");
add_edge(&mut g, "C", "B2");
fn traverse_second_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("A", *n.get_data());
let children = n.get_child_list();
assert_eq!(2, children.len());
assert_eq!("B1", *children.get_edge(0).get_target().get_data());
assert_eq!("B2", *children.get_edge(1).get_target().get_data());
Ok(Some(Traversal::Child(1)))
}
let mut path = Stack::new(g.get_node_mut(&"A").unwrap());
assert_eq!(1, path.len());
match path.push(traverse_second_child) {
Ok(Some(e)) => {
assert_eq!("A", *e.get_source().get_data());
assert_eq!("B2", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(2, path.len());
assert_eq!("B2", *path.head().get_data());
fn traverse_first_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("B2", *n.get_data());
assert_eq!(1, n.get_child_list().len());
Ok(Some(Traversal::Child(0)))
}
match path.push(traverse_first_child) {
Ok(Some(e)) => {
assert_eq!("B2", *e.get_source().get_data());
assert_eq!("D", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(3, path.len());
assert_eq!("D", *path.head().get_data());
fn traverse_first_parent<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("D", *n.get_data());
assert_eq!(1, n.get_parent_list().len());
Ok(Some(Traversal::Parent(0)))
}
match path.push(traverse_first_parent) {
Ok(Some(e)) => {
assert_eq!("B2", *e.get_source().get_data());
assert_eq!("D", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(4, path.len());
assert_eq!("B2", *path.head().get_data());
fn traverse_second_parent<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("B2", *n.get_data());
assert_eq!(2, n.get_parent_list().len());
Ok(Some(Traversal::Parent(1)))
}
match path.push(traverse_second_parent) {
Ok(Some(e)) => {
assert_eq!("C", *e.get_source().get_data());
assert_eq!("B2", *e.get_target().get_data());
},
_ => panic!(),
}
assert_eq!(5, path.len());
assert_eq!("C", *path.head().get_data());
}
#[test]
fn push_to_parent_err_ok() {
let mut g = Graph::new();
add_edge(&mut g, "A", "B1");
add_edge(&mut g, "A", "B2");
add_edge(&mut g, "B1", "C");
add_edge(&mut g, "B2", "D");
fn traverse_err<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("A", *n.get_data());
Err(MockError(()))
}
let mut path = Stack::new(g.get_node_mut(&"A").unwrap());
assert_eq!(1, path.len());
match path.push(traverse_err) {
Err(SearchError::SelectionError(_)) => (),
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("A", *path.head().get_data());
}
#[test]
fn search_path_iter_empty_ok() {
let mut g = Graph::new();
g.add_root("root", "root");
let path = Stack::new(g.add_root("root", "root"));
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
let mut iter_items = path.iter();
assert_eq!((1, Some(1)), iter_items.size_hint());
match iter_items.next() {
Some(StackItem::Head(n)) => assert_eq!("root", *n.get_data()),
_ => panic!(),
}
assert!(iter_items.next().is_none());
}
#[test]
fn search_path_iter_items_ok() {
let mut g = Graph::new();
g.add_root("root", "root");
add_edge(&mut g, "root", "A");
add_edge(&mut g, "A", "B");
fn traverse_first_child<'a>(_: &Node<'a>) -> Result<Option<Traversal>, MockError> {
Ok(Some(Traversal::Child(0)))
}
let mut path = Stack::new(g.get_node_mut(&"root").unwrap());
match path.push(traverse_first_child) {
Ok(Some(e)) => assert_eq!("root", *e.get_source().get_data()),
_ => panic!(),
}
match path.push(traverse_first_child) {
Ok(Some(e)) => {
assert_eq!("A", *e.get_source().get_data());
assert_eq!("B", *e.get_target().get_data());
},
_ => panic!(),
}
match path.push(traverse_first_child) {
Err(SearchError::ChildBounds { requested_index, child_count })
if requested_index == 0 && child_count == 0 => (),
_ => panic!(),
}
let mut iter_items = path.iter();
assert_eq!((3, Some(3)), iter_items.size_hint());
match iter_items.next() {
Some(StackItem::Item(e)) => {
assert_eq!("root", *e.get_source().get_data());
assert_eq!("A", *e.get_target().get_data());
},
_ => panic!(),
}
match iter_items.next() {
Some(StackItem::Item(e)) => {
assert_eq!("A", *e.get_source().get_data());
assert_eq!("B", *e.get_target().get_data());
},
_ => panic!(),
}
match iter_items.next() {
Some(StackItem::Head(n)) =>
assert_eq!("B", *n.get_data()),
_ => panic!(),
}
assert!(iter_items.next().is_none());
}
#[test]
fn pop_empty_is_none_ok() {
let mut g = Graph::new();
let mut path = Stack::new(g.add_root("root", "root"));
assert_eq!(1, path.len());
assert!(path.pop().is_none());
}
#[test]
fn pop_ok() {
let mut g = Graph::new();
add_edge(&mut g, "root", "A");
let mut path = Stack::new(g.get_node_mut(&"root").unwrap());
assert_eq!(1, path.len());
fn traverse_first_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
Ok(Some(Traversal::Child(0)))
}
match path.push(traverse_first_child) {
Ok(Some(e)) => assert_eq!("root", *e.get_source().get_data()),
_ => panic!(),
}
assert_eq!(2, path.len());
assert_eq!("A", *path.head().get_data());
match path.pop() {
Some(e) => assert_eq!("root", *e.get_source().get_data()),
_ => panic!(),
}
assert_eq!(1, path.len());
assert_eq!("root", *path.head().get_data());
assert!(path.pop().is_none());
}
#[test]
fn to_head_empty_ok() {
let mut g = Graph::new();
add_edge(&mut g, "root", "A");
let path = Stack::new(g.get_node_mut(&"root").unwrap());
assert_eq!(1, path.len());
assert_eq!("root", *path.to_head().get_data());
}
#[test]
fn to_head_expanded_ok() {
let mut g = Graph::new();
add_edge(&mut g, "root", "A");
let mut path = Stack::new(g.get_node_mut(&"root").unwrap());
assert_eq!(1, path.len());
fn traverse_first_child<'a>(n: &Node<'a>) -> Result<Option<Traversal>, MockError> {
assert_eq!("root", *n.get_data());
Ok(Some(Traversal::Child(0)))
}
match path.push(traverse_first_child) {
Ok(Some(e)) => assert_eq!("root", *e.get_source().get_data()),
_ => panic!(),
}
assert_eq!(2, path.len());
assert_eq!("A", *path.to_head().get_data());
}
}
pub use super::hidden::mutators::path::{SearchError, Stack, StackItem, StackIter, Traversal};