Update to Rust 2018 and use pub(crate)
for hidden APIs.
This commit is contained in:
parent
c1a24ff1a6
commit
d6de8fb161
@ -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.*"
|
||||
|
@ -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.
|
@ -1,4 +0,0 @@
|
||||
pub mod base;
|
||||
pub mod nav;
|
||||
pub mod mutators;
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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, }
|
||||
}
|
||||
}
|
166
src/lib.rs
166
src/lib.rs
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
@ -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();
|
@ -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
0
src/mutators/path.rs
Normal file
280
src/nav.rs
280
src/nav.rs
@ -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, }
|
||||
}
|
||||
}
|
||||
|
703
src/search.rs
703
src/search.rs
@ -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};
|
||||
|
Loading…
x
Reference in New Issue
Block a user