1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//! **Gantz** is a programming execution representation.
//!
//! **Gantz** uses a directed graph for this representation. **Node**s represent expressions, while
//! the edges between nodes define the order of evaluation for each of these expressions.
//!
//! - **Inlet**s of a node describe the inputs to the expression.
//! - **Outlet**s of a node describe the outputs of the evaluated expression.
//!
//! **Gantz** allows for triggering evaluation of the graph in two ways:
//!
//! 1. **Push evaluation**. The graph allows for "pushing" evaluation from one or more outlets of a
//!    single node. This causes the "pushed" outlets to begin evaluation in visit-order of a
//!    breadth-first-search that ends when nodes are reached that either 1. only have outlets
//!    connecting to nodes that have already been evaluated or 2. have no outlets at all.
//!
//! 2. **Pull evaluation**. The graph allows for "pulling" evaluation from one or more inlets of a
//!    single node. This causes the "pulled" inlets to perform a depth-first search in order to
//!    find all connected nodes that either 1. Have no inlets or 2. have inlets that connect to
//!    already visited nodes. Once these "starting" nodes are found, evaluation is "pushed" from
//!    each of these nodes in the order in which they were visited.
//!
//! ## Current Questions

#[macro_use] extern crate gantz_derive;
extern crate petgraph;

pub mod node;

pub use node::Node;

/// The edge connecting two nodes.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Edge {
    outlet: node::Outlet,
    inlet: node::Inlet,
}

/// The index type used within the graph to uniquely identify a node or edge.
pub type Index = usize;

/// The **petgraph** graph data structure used to represent the execution graph.
pub type PetGraph = petgraph::stable_graph::StableGraph<Node, Edge, petgraph::Directed, Index>;

/// The graph type
pub struct Graph {
    graph: PetGraph,
}

impl Graph {
    /// Push evaluation of the graph from the specified outlets on the given node.
    /// 
    /// 1. For each outlet on the given node, evaluate the output and use it to evaluate the input
    ///    for each child inlet connected to this outlet.
    /// 2. Repeat this for each child in BFS order.
    pub fn push_evaluation(&mut self, node: node::Index) {
        let mut bfs = petgraph::visit::Bfs::new(&self.graph, node);
        while let Some(parent) = bfs.next(&self.graph) {
            let n_outlets = self.graph[parent].n_outlets();
            for outlet in (0..n_outlets).map(node::Outlet) {
                // For every outgoing neighbour of the parent, if it has an input that is connected
                // to this outlet, process it.
                self.graph[parent].state.proc_outlet_at_index(outlet);
                let mut children = self.graph.neighbors(parent).detach();
                while let Some((e, child)) = children.next(&self.graph) {
                    let edge = self.graph[e];
                    if edge.outlet == outlet {
                        let (parent, child) = self.graph.index_twice_mut(parent, child);
                        child.proc_inlet_at_index(edge.inlet, parent.outlet_ref(outlet));
                    }
                }
            }
        }
    }

    /// Pull evaluation of the graph from the specified outlets on the given node.
    ///
    /// This causes the graph to performa DFS through each of the inlets to find the deepest nodes.
    /// Evaluation occurs as though a "push" was sent simultaneously to each of the deepest nodes.
    /// Outlets are only calculated once per node. If an outlet value is required more than once,
    /// it will be borrowed accordingly.
    pub fn pull_evaluation(&mut self, _node: node::Index, _inlets: &[node::Inlet]) {
        unimplemented!();
    }
}