Node comparison within a given DAG.

This commit is contained in:
Jeremy Wall 2022-08-04 19:24:29 -04:00
parent 3d50dda7f8
commit 3f2ddf308b
3 changed files with 125 additions and 4 deletions

View File

@ -19,12 +19,18 @@ use node::Node;
mod hash;
mod node;
#[derive(PartialEq, Debug)]
pub enum NodeCompare {
After,
Before,
Equivalent,
Uncomparable,
}
#[derive(Debug)]
pub enum EdgeError {
NoSuchDependents,
}
// TODO(jwall): In order to avoid copies it is probably smart to have some concept of
// a node pool.
/// A Merkle-DAG implementation. This is a modification on the standard Merkle Tree data structure
/// but instead of a tree it is a DAG and as a result can have multiple roots. A merkle-dag specifies
@ -98,7 +104,53 @@ where
&self.nodes
}
// TODO(jwall): How to specify a partial ordering for nodes in a graph?
/// Compare two nodes by id in the graph. If the left id is an ancestor of the right node
/// then `returns `NodeCompare::Before`. If the right id is an ancestor of the left node
/// then returns `NodeCompare::After`. If both id's are equal then the returns
/// `NodeCompare::Equivalent`. If neither id are parts of the same subgraph then returns
/// `NodeCompare::Uncomparable`.
pub fn compare(&self, left: &[u8; HASH_LEN], right: &[u8; HASH_LEN]) -> NodeCompare {
if left == right {
NodeCompare::Equivalent
} else {
// Is left node an ancestor of right node?
if self.search_graph(right, left) {
NodeCompare::Before
// is right node an ancestor of left node?
} else if self.search_graph(left, right) {
NodeCompare::After
} else {
NodeCompare::Uncomparable
}
}
}
fn search_graph(&self, root_id: &[u8; HASH_LEN], search_id: &[u8; HASH_LEN]) -> bool {
if root_id == search_id {
return true;
}
let root_node = match self.get_node_by_id(root_id) {
Some(n) => n,
None => {
return false;
}
};
let mut stack = vec![root_node];
while !stack.is_empty() {
let node = stack.pop().unwrap();
let deps = node.dependency_ids();
for dep in deps {
if search_id == dep {
return true;
}
stack.push(match self.get_node_by_id(dep) {
Some(n) => n,
None => panic!("Invalid DAG STATE encountered"),
})
}
}
return false;
}
}
impl<N, HW, const HASH_LEN: usize> Default for DAG<N, HW, HASH_LEN>

View File

@ -19,7 +19,6 @@ use crate::DAG;
fn edge_strategy(nodes_count: usize) -> impl Strategy<Value = (Vec<String>, BTreeSet<usize>)> {
prop::collection::vec(".*", 4..nodes_count).prop_flat_map(|payloads| {
let nodes_len = payloads.len();
// TODO(jwall): Generate valid DAGs
// select a random set of payloads to be roots.
// select a random set of non root payloads to be dependencies
(

View File

@ -83,3 +83,73 @@ fn test_adding_nodes_is_idempotent_regardless_of_dep_order() {
assert_eq!(root_size, dag.get_roots().len());
assert_eq!(nodes_size, dag.get_nodes().len());
}
#[test]
fn test_node_comparison_equivalent() {
let mut dag = DAG::<&str, DefaultHasher, 8>::new();
let quake_node_id = dag.add_node("quake", BTreeSet::new()).unwrap();
assert_eq!(
dag.compare(&quake_node_id, &quake_node_id),
NodeCompare::Equivalent
);
}
#[test]
fn test_node_comparison_before() {
let mut dag = DAG::<&str, DefaultHasher, 8>::new();
let quake_node_id = dag.add_node("quake", BTreeSet::new()).unwrap();
let qualm_node_id = dag
.add_node("qualm", BTreeSet::from([quake_node_id.clone()]))
.unwrap();
let quell_node_id = dag
.add_node("quell", BTreeSet::from([qualm_node_id.clone()]))
.unwrap();
assert_eq!(
dag.compare(&quake_node_id, &qualm_node_id),
NodeCompare::Before
);
assert_eq!(
dag.compare(&quake_node_id, &quell_node_id),
NodeCompare::Before
);
}
#[test]
fn test_node_comparison_after() {
let mut dag = DAG::<&str, DefaultHasher, 8>::new();
let quake_node_id = dag.add_node("quake", BTreeSet::new()).unwrap();
let qualm_node_id = dag
.add_node("qualm", BTreeSet::from([quake_node_id.clone()]))
.unwrap();
let quell_node_id = dag
.add_node("quell", BTreeSet::from([qualm_node_id.clone()]))
.unwrap();
assert_eq!(
dag.compare(&qualm_node_id, &quake_node_id),
NodeCompare::After
);
assert_eq!(
dag.compare(&quell_node_id, &quake_node_id),
NodeCompare::After
);
}
#[test]
fn test_node_comparison_no_shared_graph() {
let mut dag = DAG::<&str, DefaultHasher, 8>::new();
let quake_node_id = dag.add_node("quake", BTreeSet::new()).unwrap();
let qualm_node_id = dag.add_node("qualm", BTreeSet::new()).unwrap();
let quell_node_id = dag.add_node("quell", BTreeSet::new()).unwrap();
assert_eq!(
dag.compare(&qualm_node_id, &quake_node_id),
NodeCompare::Uncomparable
);
assert_eq!(
dag.compare(&quell_node_id, &quake_node_id),
NodeCompare::Uncomparable
);
assert_eq!(
dag.compare(&quell_node_id, &qualm_node_id),
NodeCompare::Uncomparable
);
}