From 49c98cff2295276a57aa791c059ee8eb04f84fc3 Mon Sep 17 00:00:00 2001 From: FL03 Date: Fri, 17 Mar 2023 17:55:40 -0500 Subject: [PATCH 01/13] Update --- Cargo.toml | 2 +- algae/Cargo.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5f020ec..4695b9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["algorithms", "data-structures"] license = "Apache-2.0" readme = "README.md" repository = "https://github.com/scattered-systems/algae" -version = "0.1.18" +version = "0.1.19" [workspace.dependencies] decanter = { features = ["derive", "wasm"], version = "0.1.3" } diff --git a/algae/Cargo.toml b/algae/Cargo.toml index ab1083b..bc0a7b6 100644 --- a/algae/Cargo.toml +++ b/algae/Cargo.toml @@ -34,9 +34,9 @@ test = true [build-dependencies] [dependencies] -algae-graph = { features = [], optional = true, path = "../graph", version = "0.1.18" } -algae-merkle = { features = [], optional = true, path = "../merkle", version = "0.1.18" } -algae-mmr = { features = [], optional = true, path = "../mmr", version = "0.1.18" } +algae-graph = { features = [], optional = true, path = "../graph", version = "0.1.19" } +algae-merkle = { features = [], optional = true, path = "../merkle", version = "0.1.19" } +algae-mmr = { features = [], optional = true, path = "../mmr", version = "0.1.19" } decanter.workspace = true itertools.workspace = true From 9e0b72ae9d52191219c1212b0464f5a8c7618f32 Mon Sep 17 00:00:00 2001 From: FL03 Date: Fri, 17 Mar 2023 17:57:53 -0500 Subject: [PATCH 02/13] Update --- .github/workflows/{rust-clippy.yml => clippy.yml} | 10 +++++++--- .github/workflows/rust.yml | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) rename .github/workflows/{rust-clippy.yml => clippy.yml} (87%) diff --git a/.github/workflows/rust-clippy.yml b/.github/workflows/clippy.yml similarity index 87% rename from .github/workflows/rust-clippy.yml rename to .github/workflows/clippy.yml index 23b67a0..360a92c 100644 --- a/.github/workflows/rust-clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,11 +1,15 @@ name: Clippy on: + pull_request: + branches-ignore: [ "dev*" ] + tags: [ "v*.*.*" ] push: - branches: [ "main", "master", "prod" ] - tags: ["v*.*.*"] + branches-ignore: [ "dev*" ] + tags: [ "v*.*.*" ] + release: schedule: - - cron: '30 9 * * 5' + - cron: "30 9 * * *" workflow_dispatch: jobs: diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d0a5570..81a24db 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -5,10 +5,10 @@ env: on: pull_request: - branches: [ "main", "master", "prod" ] + branches-ignore: [ "dev*" ] tags: [ "v*.*.*" ] push: - branches: [ "main", "master", "prod" ] + branches-ignore: [ "dev*" ] tags: [ "v*.*.*" ] schedule: - cron: "30 9 * * *" From d2bf6045929d9f460b764ab36b7b6e69f7aefb14 Mon Sep 17 00:00:00 2001 From: FL03 Date: Fri, 17 Mar 2023 20:55:47 -0500 Subject: [PATCH 03/13] Update --- graph/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 5878405..f917c45 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -30,6 +30,12 @@ pub trait Graph: Clone { e.push((pair.1, edge.value())); }); } + /// [Graph::add_edges] insert several edges into the graph + fn add_edges(&mut self, iter: impl IntoIterator>) { + for i in iter { + self.add_edge(i) + } + } /// [Graph::add_node] if the given [Node] is not already in the [Graph], insert the [Node] and return true; else return false fn add_node(&mut self, node: N) -> bool { match self.adjacency_table().get(&node) { @@ -40,6 +46,12 @@ pub trait Graph: Clone { _ => false, } } + /// [Graph::add_nodes] insert several nodes into the graph + fn add_nodes(&mut self, iter: impl IntoIterator) { + for i in iter { + self.add_node(i); + } + } /// [Graph::adjacency_table_mut] returns an owned, mutable instance of the [AdjacencyTable] fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable; /// [Graph::adjacency_table] returns an owned instance of the [AdjacencyTable] From 64a39173c7b8a03186e861fe46c26e35d304bf13 Mon Sep 17 00:00:00 2001 From: FL03 Date: Sun, 19 Mar 2023 08:24:57 -0500 Subject: [PATCH 04/13] Update --- graph/Cargo.toml | 1 + graph/src/cmp/atable.rs | 1 - graph/src/cmp/edge.rs | 2 +- graph/src/cmp/matrix.rs | 28 ++++++++++++++++++++++++++++ graph/src/cmp/mod.rs | 3 ++- 5 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 graph/src/cmp/matrix.rs diff --git a/graph/Cargo.toml b/graph/Cargo.toml index fcb4687..f3fad95 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -25,6 +25,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] decanter.workspace = true itertools.workspace = true +ndarray = { features = [], version = "0.15" } scsys.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/graph/src/cmp/atable.rs b/graph/src/cmp/atable.rs index e356af0..e238489 100644 --- a/graph/src/cmp/atable.rs +++ b/graph/src/cmp/atable.rs @@ -82,7 +82,6 @@ impl AdjacencyTable { pub fn len(&self) -> usize { self.0.len() } - pub fn table(self) -> HashMap> { self.0 } diff --git a/graph/src/cmp/edge.rs b/graph/src/cmp/edge.rs index 19f34b5..dfe2bbb 100644 --- a/graph/src/cmp/edge.rs +++ b/graph/src/cmp/edge.rs @@ -31,6 +31,6 @@ impl From<(N, N, V)> for Edge { impl From<(Pair, V)> for Edge { fn from(data: (Pair, V)) -> Self { - Self(data.0.0, data.0.1, data.1) + Self(data.0 .0, data.0 .1, data.1) } } diff --git a/graph/src/cmp/matrix.rs b/graph/src/cmp/matrix.rs new file mode 100644 index 0000000..e99c08a --- /dev/null +++ b/graph/src/cmp/matrix.rs @@ -0,0 +1,28 @@ +/* + Appellation: atable + Contrib: FL03 + Description: an adjacency table +*/ +use super::Node; +use ndarray::{Array2, Dim, Shape}; +use serde::{Deserialize, Serialize}; +use std::collections::HashSet; +use std::iter::Extend; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AdjacencyMatrix { + matrix: Array2, + nodes: HashSet, +} + +impl AdjacencyMatrix { + pub fn new(capacity: usize) -> Self { + Self { + matrix: Array2::zeros((capacity, capacity)), + nodes: HashSet::with_capacity(capacity), + } + } + pub fn insert(&mut self, elem: N) -> bool { + self.nodes.insert(elem) + } +} diff --git a/graph/src/cmp/mod.rs b/graph/src/cmp/mod.rs index ad72b42..760245e 100644 --- a/graph/src/cmp/mod.rs +++ b/graph/src/cmp/mod.rs @@ -3,10 +3,11 @@ Contrib: FL03 Description: components (cmp) for building effecient graph data-structures */ -pub use self::{atable::*, edge::*, pair::*}; +pub use self::{atable::*, edge::*, matrix::*, pair::*}; pub(crate) mod atable; pub(crate) mod edge; +pub(crate) mod matrix; pub(crate) mod pair; /// [Node] describes compatible vertices of the [super::Graph] From e28a67d7fb519dd92736749c99c528838b819c0f Mon Sep 17 00:00:00 2001 From: FL03 Date: Mon, 20 Mar 2023 17:28:54 -0500 Subject: [PATCH 05/13] Update --- Cargo.toml | 2 +- algae/Cargo.toml | 11 ----- algae/tests/merkle.rs | 94 ------------------------------------------- graph/Cargo.toml | 14 +++++-- merkle/Cargo.toml | 5 +-- mmr/Cargo.toml | 4 +- 6 files changed, 16 insertions(+), 114 deletions(-) delete mode 100644 algae/tests/merkle.rs diff --git a/Cargo.toml b/Cargo.toml index 4695b9a..736a988 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ version = "0.1.19" [workspace.dependencies] decanter = { features = ["derive", "wasm"], version = "0.1.3" } -scsys = { features = [], version = "0.1.41" } +anyhow = "1" itertools = "0.10" serde = { features = ["derive"], version = "1" } serde_json = "1" diff --git a/algae/Cargo.toml b/algae/Cargo.toml index bc0a7b6..be2b898 100644 --- a/algae/Cargo.toml +++ b/algae/Cargo.toml @@ -38,18 +38,7 @@ algae-graph = { features = [], optional = true, path = "../graph", version = "0. algae-merkle = { features = [], optional = true, path = "../merkle", version = "0.1.19" } algae-mmr = { features = [], optional = true, path = "../mmr", version = "0.1.19" } -decanter.workspace = true -itertools.workspace = true -scsys.workspace = true -serde.workspace = true -serde_json.workspace = true -smart-default.workspace = true -strum.workspace = true - [dev-dependencies] -decanter.workspace = true -hex-literal = "0.3" -vrf = "0.2" [package.metadata.docs.rs] all-features = true diff --git a/algae/tests/merkle.rs b/algae/tests/merkle.rs deleted file mode 100644 index 128d15e..0000000 --- a/algae/tests/merkle.rs +++ /dev/null @@ -1,94 +0,0 @@ -/* - TODO: Update the hashes to match the Blake3 Hash Digests -*/ -#[cfg(feature = "merkle")] -#[cfg(test)] -/* - Map(A -> a) - def. - This notation abbreviates a is the hash of A; more formally, (A) maps to the hash (a) by the hashing function H -*/ -use algae::merkle::{is_merkle_valid, MerkleTree, MerkleTreeWrapper}; -use decanter::prelude::{Hashable, H256}; -use hex_literal::hex; - -macro_rules! gen_merkle_tree_data { - () => {{ - vec![ - (hex!("0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - ] - }}; -} - -macro_rules! gen_merkle_tree_data2 { - () => {{ - vec![ - (hex!("0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - ] - }}; -} - -/* - A -> a: ("0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d", "b69566be6e1720872f73651d1851a0eae0060a132cf0f64a0ffaea248de6cba0") - B -> b: ("0101010101010101010101010101010101010101010101010101010101010202", "965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f") - C -> c: (concat(a, b), 6b787718210e0b3b608814e04e61fde06d0df794319a12162f287412df3ec920") where a ahead of b -*/ -#[test] -fn test_merkle_root() { - let data: Vec = gen_merkle_tree_data!(); - let expected = - (hex!("6b787718210e0b3b608814e04e61fde06d0df794319a12162f287412df3ec920")).into(); - let a = MerkleTree::from(data); - assert_ne!(&a.root(), &expected); -} - -/* - A -> a: ("0101010101010101010101010101010101010101010101010101010101010202", "965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f") -*/ -#[test] -fn test_merkle_proof() { - let expected = - vec![hex!("965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f").into()]; - - let a = MerkleTree::from(gen_merkle_tree_data!()); - - assert_ne!(a.proof(0), expected) -} - -/* - A -> a: ("0101010101010101010101010101010101010101010101010101010101010202", "965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f") -*/ -#[test] -fn test_is_merkle_valid() { - let data: Vec = gen_merkle_tree_data2!(); - let merkle_tree = MerkleTree::from(data.clone()); - let index = 3; - let proof = merkle_tree.proof(index); - - assert!(is_merkle_valid( - &merkle_tree.root(), - &data[index].hash(), - &proof, - index, - data.len() - )); -} - -#[test] -fn test_vrf_tree() { - let data: Vec = gen_merkle_tree_data!(); - let merkle_tree = MerkleTree::from(data.clone()); - let proof = merkle_tree.proof(0); - assert!(is_merkle_valid( - &merkle_tree.root(), - &data[0].hash(), - &proof, - 0, - data.len() - )); -} diff --git a/graph/Cargo.toml b/graph/Cargo.toml index f3fad95..6dda31a 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -14,20 +14,28 @@ version.workspace = true [features] default = [] +wasm = [] [lib] crate-type = ["cdylib", "rlib"] [build-dependencies] -[dev-dependencies] - [dependencies] decanter.workspace = true itertools.workspace = true ndarray = { features = [], version = "0.15" } -scsys.workspace = true serde.workspace = true serde_json.workspace = true smart-default.workspace = true strum.workspace = true + +[dev-dependencies] + +[package.metadata.docs.rs] +all-features = true +rustc-args = ["--cfg", "docsrs"] + +[target.wasm32-unknown-unknown] + +[target.wasm32-wasi] diff --git a/merkle/Cargo.toml b/merkle/Cargo.toml index 9c8af81..c63a2ac 100644 --- a/merkle/Cargo.toml +++ b/merkle/Cargo.toml @@ -25,14 +25,12 @@ crate-type = ["cdylib", "rlib"] test = true [dependencies] -anyhow = "1" hex = "0.4" -log = "0.4" ring = { features = ["wasm32_c"], version = "0.16" } +anyhow.workspace = true decanter.workspace = true itertools.workspace = true -# scsys.workspace = true serde.workspace = true serde_json.workspace = true smart-default.workspace = true @@ -40,6 +38,7 @@ strum.workspace = true [dev-dependencies] hex-literal = "0.3" +log = "0.4" vrf = "0.2" [package.metadata.docs.rs] diff --git a/mmr/Cargo.toml b/mmr/Cargo.toml index 2d1be0a..d0459d4 100644 --- a/mmr/Cargo.toml +++ b/mmr/Cargo.toml @@ -24,6 +24,7 @@ crate-type = ["cdylib", "rlib"] test = true [dependencies] +anyhow.workspace = true decanter.workspace = true itertools.workspace = true serde.workspace = true @@ -31,12 +32,11 @@ serde_json.workspace = true smart-default.workspace = true strum.workspace = true -anyhow = "1" digest = "0.10" hex = "0.4" [dev-dependencies] -scsys.workspace = true +scsys = { features = [], version = "0.1.41" } hex-literal = "0.3.4" vrf = "0.2.4" From 88ddce2376364c5574ce00f1ad8469c74af56194 Mon Sep 17 00:00:00 2001 From: FL03 Date: Tue, 21 Mar 2023 17:47:23 -0500 Subject: [PATCH 06/13] Update --- merkle/src/lib.rs | 29 +++++++++++- merkle/src/proofs/proof.rs | 37 ++++++++++++++- merkle/src/shape.rs | 20 ++++---- merkle/src/{trees => }/tree.rs | 31 +++++-------- merkle/src/trees/interface.rs | 46 ------------------- merkle/src/trees/mod.rs | 83 ---------------------------------- merkle/src/utils.rs | 36 ++++++++++++++- merkle/tests/merkle.rs | 2 +- 8 files changed, 122 insertions(+), 162 deletions(-) rename merkle/src/{trees => }/tree.rs (55%) delete mode 100644 merkle/src/trees/interface.rs delete mode 100644 merkle/src/trees/mod.rs diff --git a/merkle/src/lib.rs b/merkle/src/lib.rs index 87b83a5..caf9fcc 100644 --- a/merkle/src/lib.rs +++ b/merkle/src/lib.rs @@ -6,13 +6,38 @@ #[cfg(test)] extern crate hex_literal; #[doc(inline)] -pub use self::{layers::*, nodes::*, payloads::*, shape::*, trees::*, utils::*}; +pub use self::{layers::*, nodes::*, payloads::*, shape::*, tree::*, utils::*}; pub(crate) mod layers; pub(crate) mod nodes; pub(crate) mod payloads; pub(crate) mod shape; -pub(crate) mod trees; +pub(crate) mod tree; pub(crate) mod utils; pub mod proofs; + +use decanter::prelude::{Hashable, H256}; +use proofs::merkle_proof; + +pub trait MerkleTreeSpec: Clone { + // Returns the dimension of the given tree + fn dim(&self) -> MerkleDimension; + // Returns the nodes of the given tree + fn nodes(&self) -> Vec; + // Returns the proof for the given index + fn proof(&self, index: usize) -> Vec { + merkle_proof(self.dim(), self.nodes(), index) + } + // Writes the injected nodes to the console for viewing purposes + fn print(&self) -> &Self { + for i in 0..self.dim().size { + println!("{:?}", self.nodes()[i]); + } + self + } + // Returns the root hash of the merkle tree + fn root(&self) -> H256 { + self.nodes()[self.dim().size() - 1] + } +} diff --git a/merkle/src/proofs/proof.rs b/merkle/src/proofs/proof.rs index 037d0d3..d175472 100644 --- a/merkle/src/proofs/proof.rs +++ b/merkle/src/proofs/proof.rs @@ -4,9 +4,44 @@ Description: ... Summary ... */ use super::path::proof_path; -use crate::MerkleDimension; +use crate::{MerkleDimension, MerkleTree, MerkleTreeSpec}; use decanter::prelude::H256; +pub struct Prover { + cursor: usize, + proof: Vec, + tree: MerkleTree, +} + +impl Prover { + pub fn new(tree: MerkleTree) -> Self { + Self { + cursor: 0, + proof: Vec::new(), + tree, + } + } + /// Returns the proof for the given index + pub fn prove(&mut self, index: usize) -> Vec { + self.proof = self.tree.proof(index); + self.proof.clone() + } +} + +impl Iterator for Prover { + type Item = Vec; + + fn next(&mut self) -> Option { + if self.cursor < self.proof.len() { + let item = self.prove(self.cursor); + self.cursor += 1; + Some(item) + } else { + None + } + } +} + // Returns the proof for the given index pub fn merkle_proof(dim: MerkleDimension, nodes: Vec, index: usize) -> Vec { let mut proof: Vec = Vec::new(); diff --git a/merkle/src/shape.rs b/merkle/src/shape.rs index c1d5810..e8351a9 100644 --- a/merkle/src/shape.rs +++ b/merkle/src/shape.rs @@ -14,7 +14,9 @@ pub trait MerkleShape { fn size(&self) -> usize; } -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive( + Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, +)] pub struct MerkleDimension { pub depth: usize, pub leafs: usize, @@ -40,20 +42,20 @@ impl MerkleShape for MerkleDimension { } } -impl std::convert::From> for MerkleDimension { +impl std::fmt::Display for MerkleDimension { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({}, {}, {})", self.depth, self.leafs, self.size) + } +} + +impl From> for MerkleDimension { fn from(data: Box) -> Self { Self::from(data.shape()) } } -impl std::convert::From<(usize, usize, usize)> for MerkleDimension { +impl From<(usize, usize, usize)> for MerkleDimension { fn from(data: (usize, usize, usize)) -> Self { Self::new(data.0, data.1, data.2) } } - -impl std::fmt::Display for MerkleDimension { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "({}, {}, {})", self.depth, self.leafs, self.size) - } -} diff --git a/merkle/src/trees/tree.rs b/merkle/src/tree.rs similarity index 55% rename from merkle/src/trees/tree.rs rename to merkle/src/tree.rs index ed0e573..ced672f 100644 --- a/merkle/src/trees/tree.rs +++ b/merkle/src/tree.rs @@ -3,13 +3,11 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::{ - create_merkle_tree, proofs::merkle_proof, MerkleDimension, MerkleShape, MerkleTreeWrapper, - MerkleTreeWrapperExt, -}; +use crate::{create_merkle_tree, MerkleDimension, MerkleShape, MerkleTreeSpec}; use decanter::prelude::{Hashable, H256}; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct MerkleTree { pub dim: MerkleDimension, pub nodes: Vec, @@ -19,42 +17,37 @@ impl MerkleTree { pub fn new(dim: MerkleDimension, nodes: Vec) -> Self { Self { dim, nodes } } - // Returns the proof for the given index - pub fn proof(&self, index: usize) -> Vec { - merkle_proof(self.dim.clone(), self.nodes.clone(), index) - } } -impl MerkleTreeWrapper for MerkleTree { - fn new(dim: MerkleDimension, nodes: Vec) -> Self { - Self { dim, nodes } - } - +impl MerkleTreeSpec for MerkleTree { fn dim(&self) -> MerkleDimension { self.dim.clone() } - fn nodes(&self) -> Vec { self.nodes.clone() } } -impl MerkleTreeWrapperExt for MerkleTree {} +impl std::fmt::Display for MerkleTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.nodes) + } +} -impl std::convert::From<&[T]> for MerkleTree { +impl From<&[T]> for MerkleTree { fn from(data: &[T]) -> Self { let (dim, nodes) = create_merkle_tree::(data); Self::new(MerkleDimension::from(dim), nodes) } } -impl std::convert::From<(Box, Vec)> for MerkleTree { +impl From<(Box, Vec)> for MerkleTree { fn from(data: (Box, Vec)) -> Self { Self::new(MerkleDimension::from(data.0), data.1) } } -impl std::convert::From> for MerkleTree { +impl From> for MerkleTree { fn from(data: Vec) -> Self { Self::from(create_merkle_tree(&data)) } diff --git a/merkle/src/trees/interface.rs b/merkle/src/trees/interface.rs deleted file mode 100644 index ece2c4f..0000000 --- a/merkle/src/trees/interface.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* - Appellation: tree - Contrib: FL03 - Description: - Merkle Tree def... -*/ -use crate::{Layer, Node}; -use decanter::prelude::{Hashable, H256}; -use serde::{Deserialize, Serialize}; -use std::string::ToString; - -/// Implements a complete merkle tree -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct Tree { - pub leaves: Vec, - pub root: Node, -} - -impl Tree { - pub fn new(leaves: Vec, root: Node) -> Self { - Self { leaves, root } - } - pub fn root_hash(&self) -> H256 { - self.root.hash() - } -} - -impl std::convert::From for Tree -where - ::Item: Clone + Serialize + ToString, -{ - fn from(data: II) -> Self { - let leaves = data.into_iter().collect::>(); - - let mut layer: Vec<_> = leaves.iter().cloned().map(Node::from).collect(); - - while layer.len() != 1 { - layer = Layer::from(layer).layer().clone(); - } - - match layer.pop() { - Some(root) => Self::new(leaves, root), - None => panic!("You should not have built an empty tree"), - } - } -} diff --git a/merkle/src/trees/mod.rs b/merkle/src/trees/mod.rs deleted file mode 100644 index d13c92e..0000000 --- a/merkle/src/trees/mod.rs +++ /dev/null @@ -1,83 +0,0 @@ -/* - Appellation: interfaces - Contrib: FL03 - Description: ... Summary ... -*/ -pub use self::{interface::*, tree::*, utils::*}; - -pub(crate) mod interface; -pub(crate) mod tree; - -use crate::{proofs::merkle_proof, MerkleDimension, MerkleShape}; -use decanter::prelude::{Hashable, H256}; - -pub trait MerkleTreeWrapper { - fn new(dim: MerkleDimension, nodes: Vec) -> Self; - fn create(data: &[T]) -> Self - where - Self: Sized, - T: Hashable, - { - let (dim, nodes) = create_merkle_tree(data); - - Self::new(dim.into(), nodes) - } - fn dim(&self) -> MerkleDimension; - fn nodes(&self) -> Vec; - // Returns the proof for the given index - fn proof(&self, index: usize) -> Vec { - merkle_proof(self.dim(), self.nodes(), index) - } - // Returns the root hash of the merkle tree - fn root(&self) -> H256 { - self.nodes()[self.dim().size() - 1] - } -} - -pub trait MerkleTreeWrapperExt: MerkleTreeWrapper { - // Writes the injected nodes to the console for viewing purposes - fn print(&self) -> &Self { - for i in 0..self.dim().size { - println!("{:?}", self.nodes()[i]); - } - self - } -} -pub(crate) mod utils { - use crate::{add_hash, MerkleDimension, MerkleShape}; - use decanter::prelude::{Hashable, H256}; - - pub fn create_merkle_tree(data: &[T]) -> (Box, Vec) - where - T: Hashable, - { - // unimplemented!() - let mut length = data.len(); - let mut nodes = Vec::new(); - let mut last_level = Vec::new(); - for i in data { - let h: H256 = i.hash(); - last_level.push(h); - nodes.push(h); - } - let mut depth = 1; - while length > 1 { - if length % 2 != 0 { - last_level.push(data[length - 1].hash()); - nodes.push(data[length - 1].hash()); - length += 1; - } - let mut temp = Vec::new(); - for i in 0..length / 2 { - let h: H256 = add_hash(&last_level[2 * i], &last_level[2 * i + 1]); - temp.push(h); - nodes.push(h); - } - last_level = temp.clone(); - length /= 2; - depth += 1; - } - let dim = MerkleDimension::new(depth, data.len(), nodes.len()); - (Box::new(dim), nodes) - } -} diff --git a/merkle/src/utils.rs b/merkle/src/utils.rs index 78dc159..2321ddf 100644 --- a/merkle/src/utils.rs +++ b/merkle/src/utils.rs @@ -4,7 +4,8 @@ Description: */ use crate::proofs::proof_path; -use decanter::prelude::{hasher, H256}; +use crate::{MerkleDimension, MerkleShape}; +use decanter::prelude::{hasher, Hashable, H256}; use serde::Serialize; /// @@ -18,6 +19,39 @@ pub fn add_hash(a: &H256, b: &H256) -> H256 { pub fn combine(a: &T, b: &T) -> String { format!("{}{}", a.to_string(), b.to_string()) } +/// Creates a Merkle tree from a slice of data +pub fn create_merkle_tree(data: &[T]) -> (Box, Vec) +where + T: Hashable, +{ + let mut length = data.len(); + let mut nodes = Vec::new(); + let mut last_level = Vec::new(); + for i in data { + let h: H256 = i.hash(); + last_level.push(h); + nodes.push(h); + } + let mut depth = 1; + while length > 1 { + if length % 2 != 0 { + last_level.push(data[length - 1].hash()); + nodes.push(data[length - 1].hash()); + length += 1; + } + let mut temp = Vec::new(); + for i in 0..length / 2 { + let h: H256 = add_hash(&last_level[2 * i], &last_level[2 * i + 1]); + temp.push(h); + nodes.push(h); + } + last_level = temp.clone(); + length /= 2; + depth += 1; + } + let dim = MerkleDimension::new(depth, data.len(), nodes.len()); + (Box::new(dim), nodes) +} /// Takes the hash of the given information to the second degree pub fn merkle_hash(data: T) -> H256 { diff --git a/merkle/tests/merkle.rs b/merkle/tests/merkle.rs index 732c8bd..7b251a6 100644 --- a/merkle/tests/merkle.rs +++ b/merkle/tests/merkle.rs @@ -7,7 +7,7 @@ def. This notation abbreviates a is the hash of A; more formally, (A) maps to the hash (a) by the hashing function H */ -use algae_merkle::{is_merkle_valid, MerkleTree, MerkleTreeWrapper}; +use algae_merkle::{is_merkle_valid, MerkleTree, MerkleTreeSpec}; use decanter::prelude::{Hashable, H256}; use hex_literal::hex; From 2f6d8dc36f10b082add417bde201e29bf23659d3 Mon Sep 17 00:00:00 2001 From: FL03 Date: Thu, 23 Mar 2023 10:15:58 -0500 Subject: [PATCH 07/13] Update --- graph/Cargo.toml | 1 - graph/src/cmp/matrix.rs | 28 -------- graph/src/cmp/mod.rs | 4 +- graph/src/directed.rs | 65 ++++++++++++----- graph/src/lib.rs | 62 +++++++++------- graph/src/search/bfs.rs | 79 +++++++++++++++++++++ graph/src/search/dfs.rs | 78 ++++++++++++++++++++ graph/src/search/mod.rs | 18 +++++ graph/src/store/matrix.rs | 77 ++++++++++++++++++++ graph/src/store/mod.rs | 22 ++++++ graph/src/{cmp/atable.rs => store/table.rs} | 10 +-- graph/src/undirected.rs | 55 ++++++++++---- 12 files changed, 400 insertions(+), 99 deletions(-) delete mode 100644 graph/src/cmp/matrix.rs create mode 100644 graph/src/search/bfs.rs create mode 100644 graph/src/search/dfs.rs create mode 100644 graph/src/search/mod.rs create mode 100644 graph/src/store/matrix.rs create mode 100644 graph/src/store/mod.rs rename graph/src/{cmp/atable.rs => store/table.rs} (95%) diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 6dda31a..2e5efce 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -24,7 +24,6 @@ crate-type = ["cdylib", "rlib"] [dependencies] decanter.workspace = true itertools.workspace = true -ndarray = { features = [], version = "0.15" } serde.workspace = true serde_json.workspace = true smart-default.workspace = true diff --git a/graph/src/cmp/matrix.rs b/graph/src/cmp/matrix.rs deleted file mode 100644 index e99c08a..0000000 --- a/graph/src/cmp/matrix.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* - Appellation: atable - Contrib: FL03 - Description: an adjacency table -*/ -use super::Node; -use ndarray::{Array2, Dim, Shape}; -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; -use std::iter::Extend; - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct AdjacencyMatrix { - matrix: Array2, - nodes: HashSet, -} - -impl AdjacencyMatrix { - pub fn new(capacity: usize) -> Self { - Self { - matrix: Array2::zeros((capacity, capacity)), - nodes: HashSet::with_capacity(capacity), - } - } - pub fn insert(&mut self, elem: N) -> bool { - self.nodes.insert(elem) - } -} diff --git a/graph/src/cmp/mod.rs b/graph/src/cmp/mod.rs index 760245e..30bb457 100644 --- a/graph/src/cmp/mod.rs +++ b/graph/src/cmp/mod.rs @@ -3,11 +3,9 @@ Contrib: FL03 Description: components (cmp) for building effecient graph data-structures */ -pub use self::{atable::*, edge::*, matrix::*, pair::*}; +pub use self::{edge::*, pair::*}; -pub(crate) mod atable; pub(crate) mod edge; -pub(crate) mod matrix; pub(crate) mod pair; /// [Node] describes compatible vertices of the [super::Graph] diff --git a/graph/src/directed.rs b/graph/src/directed.rs index 3222d3c..acada8b 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -3,32 +3,61 @@ Contrib: FL03 Description: ... Summary ... */ -use super::{ - cmp::{AdjacencyTable, Node}, - Graph, Subgraph, +use crate::{ + cmp::{Edge, Node}, + store::AdjacencyTable, + Contain, Graph, Subgraph, }; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct DirectedGraph { - adjacency_table: AdjacencyTable, +pub struct DirectedGraph +where + N: Node, + V: Clone + PartialEq, +{ + store: AdjacencyTable, +} + +impl AsMut> for DirectedGraph { + fn as_mut(&mut self) -> &mut AdjacencyTable { + &mut self.store + } +} + +impl AsRef> for DirectedGraph { + fn as_ref(&self) -> &AdjacencyTable { + &self.store + } +} + +impl Contain for DirectedGraph { + fn contains(&self, elem: &N) -> bool { + self.store.contains_key(elem) + } +} + +impl Contain> for DirectedGraph { + fn contains(&self, elem: &Edge) -> bool { + self.edges().contains(elem) + } } impl Graph for DirectedGraph { fn new() -> Self { Self { - adjacency_table: AdjacencyTable::new(), + store: AdjacencyTable::new(), } } - fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable { - &mut self.adjacency_table + fn store_mut(&mut self) -> &mut AdjacencyTable { + &mut self.store } - fn adjacency_table(&self) -> &AdjacencyTable { - &self.adjacency_table + fn store(&self) -> &AdjacencyTable { + &self.store } fn with_capacity(capacity: usize) -> Self { Self { - adjacency_table: AdjacencyTable::with_capacity(capacity), + store: AdjacencyTable::with_capacity(capacity), } } } @@ -37,7 +66,9 @@ impl Subgraph for DirectedGraph {} impl From> for DirectedGraph { fn from(adjacency_table: AdjacencyTable) -> Self { - Self { adjacency_table } + Self { + store: adjacency_table, + } } } @@ -65,7 +96,7 @@ mod tests { graph.add_edge(i.into()); } for edge in TEST_EDGES { - assert!(graph.contains_edge(&Edge::from(edge))); + assert!(graph.contains(&Edge::from(edge))); } } @@ -77,7 +108,7 @@ mod tests { graph.add_edge(i.into()); } - assert_eq!(graph.neighbours("a").unwrap(), &vec![("b", 5)]); + assert_eq!(graph.neighbors("a").unwrap(), &vec![("b", 5)]); } #[test] @@ -86,9 +117,7 @@ mod tests { graph.add_node("a"); graph.add_node("b"); graph.add_node("c"); - assert!(graph.contains_node(&"a")); - assert!(graph.contains_node(&"b")); - assert!(graph.contains_node(&"c")); - assert!(!graph.contains_node(&"d")); + assert!(graph.contains_many(["a", "b", "c"])); + assert!(graph.contains_some(["a", "b", "c", "d"])); } } diff --git a/graph/src/lib.rs b/graph/src/lib.rs index f917c45..dfd9fb5 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -10,13 +10,33 @@ pub(crate) mod undirected; pub mod cmp; pub mod errors; +pub mod search; +pub mod store; -use cmp::{AdjacencyTable, Edge, Node}; +use cmp::{Edge, Node}; use errors::GraphError; use std::collections::HashSet; +use store::AdjacencyTable; + +pub trait Contain { + /// [Contain::contains] returns true if the given element is in the [Contain] instance + fn contains(&self, elem: &T) -> bool; + /// [Contain::contains_many] returns true if all elements in the given iterator are in the [Contain] instance + fn contains_many(&self, iter: impl IntoIterator) -> bool { + iter.into_iter().all(|i| self.contains(&i)) + } + /// [Contain::contains_some] returns true if any element in the given iterator is in the [Contain] instance + fn contains_some(&self, iter: impl IntoIterator) -> bool { + iter.into_iter().any(|i| self.contains(&i)) + } +} /// [Graph] describes the basic operations of a graph data-structure -pub trait Graph: Clone { +pub trait Graph: Clone + Contain + Contain> +where + N: Node, + V: Clone + PartialEq, +{ fn new() -> Self; /// [Graph::add_edge] inserts a new [Edge] into the graph fn add_edge(&mut self, edge: Edge) { @@ -24,11 +44,9 @@ pub trait Graph: Clone { self.add_node(pair.0.clone()); self.add_node(pair.1.clone()); - self.adjacency_table_mut() - .entry(pair.0.clone()) - .and_modify(|e| { - e.push((pair.1, edge.value())); - }); + self.store_mut().entry(pair.0.clone()).and_modify(|e| { + e.push((pair.1, edge.value())); + }); } /// [Graph::add_edges] insert several edges into the graph fn add_edges(&mut self, iter: impl IntoIterator>) { @@ -38,9 +56,9 @@ pub trait Graph: Clone { } /// [Graph::add_node] if the given [Node] is not already in the [Graph], insert the [Node] and return true; else return false fn add_node(&mut self, node: N) -> bool { - match self.adjacency_table().get(&node) { + match self.store().get(&node) { None => { - self.adjacency_table_mut().insert(node, Vec::new()); + self.store_mut().insert(node, Vec::new()); true } _ => false, @@ -52,38 +70,30 @@ pub trait Graph: Clone { self.add_node(i); } } - /// [Graph::adjacency_table_mut] returns an owned, mutable instance of the [AdjacencyTable] - fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable; - /// [Graph::adjacency_table] returns an owned instance of the [AdjacencyTable] - fn adjacency_table(&self) -> &AdjacencyTable; - /// [Graph::contains_edge] checks to see if a given [Edge] is found in the [Graph] - fn contains_edge(&self, edge: &Edge) -> bool { - self.edges().contains(edge) - } - /// [Graph::contains_node] checks to see if a given [Node] is found in the [Graph] - fn contains_node(&self, node: &N) -> bool { - self.adjacency_table().get(node).is_some() - } + /// [Graph::store_mut] returns an owned, mutable instance of the [AdjacencyTable] + fn store_mut(&mut self) -> &mut AdjacencyTable; + /// [Graph::store] returns an owned instance of the [AdjacencyTable] + fn store(&self) -> &AdjacencyTable; /// [Graph::edges] returns all of the edges persisting within the graph fn edges(&self) -> Vec> { let mut edges = Vec::new(); - for (from_node, from_node_neighbours) in self.adjacency_table().clone() { + for (from_node, from_node_neighbours) in self.store().clone() { for (to_node, weight) in from_node_neighbours { edges.push((from_node.clone(), to_node, weight).into()); } } edges } - /// [Graph::neighbours] attempts to return a [Vec] that contains all of the connected [Node] and their values - fn neighbours(&self, node: N) -> Result<&Vec<(N, V)>, GraphError> { - match self.adjacency_table().get(&node) { + /// [Graph::neighbors] attempts to return a [Vec] that contains all of the connected [Node] and their values + fn neighbors(&self, node: N) -> Result<&Vec<(N, V)>, GraphError> { + match self.store().get(&node) { None => Err(GraphError::NodeNotInGraph), Some(i) => Ok(i), } } /// [Graph::nodes] returns a cloned [HashSet] of the graph's current [Node]s fn nodes(&self) -> HashSet { - self.adjacency_table().keys().cloned().collect() + self.store().keys().cloned().collect() } /// [Graph::with_capacity] is a method for creating a graph with a set number of nodes fn with_capacity(capacity: usize) -> Self; diff --git a/graph/src/search/bfs.rs b/graph/src/search/bfs.rs new file mode 100644 index 0000000..9c6827e --- /dev/null +++ b/graph/src/search/bfs.rs @@ -0,0 +1,79 @@ +/* + Appellation: bfs + Contrib: FL03 + Description: ... Summary ... +*/ +use crate::cmp::Node; +use crate::{Contain, Graph}; +use std::collections::{HashSet, VecDeque}; + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct BreadthFirstSearch { + queue: VecDeque, + visited: HashSet, +} + +impl BreadthFirstSearch { + pub fn new() -> Self { + Self { + queue: VecDeque::new(), + visited: HashSet::new(), + } + } + pub fn clear(&mut self) { + self.visited.clear(); + self.queue.clear(); + } + pub fn search(&mut self, graph: impl Graph, start: N) -> Vec + where + V: Clone + PartialEq, + { + self.queue.push_back(start); + + while let Some(node) = self.queue.pop_front() { + if !self.visited.contains(&node) { + self.visited.insert(node.clone()); + + if let Some(edges) = graph.store().get(&node) { + for (n, _) in edges { + self.queue.push_back(n.clone()); + } + } + } + } + + self.visited.iter().cloned().collect() + } +} + +impl Contain for BreadthFirstSearch { + fn contains(&self, elem: &N) -> bool { + self.visited.contains(elem) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cmp::Edge; + use crate::directed::DirectedGraph; + + const TEST_EDGES: [(&str, &str, usize); 5] = [ + ("a", "b", 5), + ("c", "a", 7), + ("b", "c", 10), + ("d", "c", 10), + ("e", "f", 10), + ]; + + #[test] + fn test_bfs() { + let mut graph = DirectedGraph::<&str, usize>::new(); + for i in TEST_EDGES { + graph.add_edge(Edge::from(i)); + } + let mut bfs = BreadthFirstSearch::new(); + bfs.search(graph, "a"); + assert!(bfs.contains_many(["b", "c", "a"])); + } +} diff --git a/graph/src/search/dfs.rs b/graph/src/search/dfs.rs new file mode 100644 index 0000000..63a0fba --- /dev/null +++ b/graph/src/search/dfs.rs @@ -0,0 +1,78 @@ +/* + Appellation: dfs + Contrib: FL03 + Description: ... Summary ... +*/ +use crate::cmp::Node; +use crate::{Contain, Graph}; + +#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct DepthFirstSearch { + stack: Vec, + visited: Vec, +} + +impl DepthFirstSearch { + pub fn new() -> Self { + Self { + stack: Vec::new(), + visited: Vec::new(), + } + } + pub fn clear(&mut self) { + self.stack.clear(); + self.visited.clear(); + } + pub fn search(&mut self, graph: impl Graph, start: N) -> Vec + where + V: Clone + PartialEq, + { + self.stack.push(start); + while let Some(node) = self.stack.pop() { + if !self.visited.contains(&node) { + self.visited.push(node.clone()); + if let Ok(neighbor) = graph.neighbors(node) { + for (node, _) in neighbor { + self.stack.push(node.clone()); + } + } + } + } + self.visited.clone() + } +} + +impl Contain for DepthFirstSearch { + fn contains(&self, elem: &N) -> bool { + self.visited.contains(elem) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cmp::Edge; + use crate::directed::DirectedGraph; + + const TEST_EDGES: [(&str, &str, usize); 5] = [ + ("a", "b", 5), + ("c", "a", 7), + ("b", "c", 10), + ("d", "c", 10), + ("e", "f", 10), + ]; + + #[test] + fn test_dfs() { + let mut graph = DirectedGraph::<&str, usize>::new(); + for i in TEST_EDGES { + graph.add_edge(Edge::from(i)); + } + // + let mut dfs = DepthFirstSearch::new(); + // + dfs.search(graph, "a"); + // + assert!(dfs.contains_many(["b", "c", "a"])); + } +} diff --git a/graph/src/search/mod.rs b/graph/src/search/mod.rs new file mode 100644 index 0000000..3408a0b --- /dev/null +++ b/graph/src/search/mod.rs @@ -0,0 +1,18 @@ +/* + Appellation: search + Contrib: FL03 + Description: ... Summary ... +*/ + +pub mod bfs; +pub mod dfs; + +use crate::{cmp::Node, Contain, Graph}; + +pub trait BreadthFirstSearchable { + fn breadth_first_search(&self, start: N) -> Vec; +} + +pub trait Searcher: Contain { + fn search(&mut self, graph: impl Graph, start: N) -> Vec; +} diff --git a/graph/src/store/matrix.rs b/graph/src/store/matrix.rs new file mode 100644 index 0000000..0e2f641 --- /dev/null +++ b/graph/src/store/matrix.rs @@ -0,0 +1,77 @@ +/* + Appellation: atable + Contrib: FL03 + Description: an adjacency table +*/ +use crate::cmp::Node; +use serde::{Deserialize, Serialize}; +use std::iter::Extend; + +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct AdjacencyMatrix +where + N: Node, + V: Clone + PartialEq, +{ + store: Vec<(N, Vec<(N, V)>)>, +} + +impl AdjacencyMatrix { + pub fn new() -> Self { + Self { store: Vec::new() } + } + /// Clears the matrix, removing all elements. + pub fn clear(&mut self) { + self.store.clear() + } + /// Returns the number of elements the matrix can hold without reallocating. + pub fn capacity(&self) -> usize { + self.store.capacity() + } + /// Returns a reference to the value corresponding to the key. + pub fn get(&self, key: &N) -> Option<&Vec<(N, V)>> { + self.store + .iter() + .find_map(|(k, v)| if k == key { Some(v) } else { None }) + } + /// Returns a reference to the key and its value(s). + pub fn get_key_value(&self, key: &N) -> Option<(&N, &Vec<(N, V)>)> { + self.store + .iter() + .find_map(|(k, v)| if k == key { Some((k, v)) } else { None }) + } + /// Returns a mutable reference to the value corresponding to the key. + pub fn get_mut(&mut self, key: &N) -> Option<&mut Vec<(N, V)>> { + self.store + .iter_mut() + .find_map(|(k, v)| if k == key { Some(v) } else { None }) + } + /// Returns an iterator over the elements of the matrix. + pub fn keys(&self) -> impl Iterator { + self.store.iter().map(|(k, _)| k) + } + /// Returns the number of elements in the matrix. + pub fn len(&self) -> usize { + self.store.len() + } + /// Pushes a new element to the matrix. + pub fn push(&mut self, elem: N, value: Vec<(N, V)>) { + self.store.push((elem, value)); + } + /// Shrinks the capacity of the matrix as much as possible. + pub fn shrink_to_fit(&mut self) { + self.store.shrink_to_fit() + } + /// Reserves capacity for at least `additional` more elements to be inserted in the matrix. + pub fn with_capacity(capacity: usize) -> Self { + Self { + store: Vec::with_capacity(capacity), + } + } +} + +impl Extend<(N, Vec<(N, V)>)> for AdjacencyMatrix { + fn extend)>>(&mut self, iter: T) { + self.store.extend(iter); + } +} diff --git a/graph/src/store/mod.rs b/graph/src/store/mod.rs new file mode 100644 index 0000000..ba0bfe0 --- /dev/null +++ b/graph/src/store/mod.rs @@ -0,0 +1,22 @@ +/* + Appellation: store + Contrib: FL03 + Description: +*/ +pub use self::{matrix::*, table::*}; + +pub(crate) mod matrix; +pub(crate) mod table; + +use crate::cmp::Node; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub enum Stores +where + N: Node, + V: Clone + PartialEq, +{ + Matrix(AdjacencyMatrix), + Table(AdjacencyTable), +} diff --git a/graph/src/cmp/atable.rs b/graph/src/store/table.rs similarity index 95% rename from graph/src/cmp/atable.rs rename to graph/src/store/table.rs index e238489..7b0324f 100644 --- a/graph/src/cmp/atable.rs +++ b/graph/src/store/table.rs @@ -3,7 +3,7 @@ Contrib: FL03 Description: an adjacency table */ -use super::Node; +use crate::cmp::Node; use serde::{Deserialize, Serialize}; use std::collections::{hash_map, HashMap}; use std::iter::Extend; @@ -31,14 +31,6 @@ pub trait HashMapLike: fn table_mut(&mut self) -> &mut HashMap; } -pub struct KeyValue(Vec<(K, Vec)>); - -impl KeyValue { - pub fn new() -> Self { - Self(Vec::new()) - } -} - #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct AdjacencyTable(HashMap>); diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index c063054..a68dbd6 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -3,21 +3,46 @@ Contrib: FL03 Description: ... Summary ... */ -use super::{ - cmp::{AdjacencyTable, Edge, Node}, - Graph, Subgraph, +use crate::{ + cmp::{Edge, Node}, + store::AdjacencyTable, + Contain, Graph, Subgraph, }; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct UndirectedGraph { - adjacency_table: AdjacencyTable, + store: AdjacencyTable, +} + +impl AsMut> for UndirectedGraph { + fn as_mut(&mut self) -> &mut AdjacencyTable { + &mut self.store + } +} + +impl AsRef> for UndirectedGraph { + fn as_ref(&self) -> &AdjacencyTable { + &self.store + } +} + +impl Contain for UndirectedGraph { + fn contains(&self, elem: &N) -> bool { + self.store.contains_key(elem) + } +} + +impl Contain> for UndirectedGraph { + fn contains(&self, elem: &Edge) -> bool { + self.edges().contains(elem) + } } impl Graph for UndirectedGraph { fn new() -> Self { Self { - adjacency_table: AdjacencyTable::new(), + store: AdjacencyTable::new(), } } fn add_edge(&mut self, edge: Edge) { @@ -25,22 +50,22 @@ impl Graph for UndirectedGraph { self.add_node(pair.0.clone()); self.add_node(pair.1.clone()); - self.adjacency_table.entry(pair.0.clone()).and_modify(|e| { + self.store.entry(pair.0.clone()).and_modify(|e| { e.push((pair.1.clone(), edge.value())); }); - self.adjacency_table.entry(pair.1).and_modify(|e| { + self.store.entry(pair.1).and_modify(|e| { e.push((pair.0, edge.value())); }); } - fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable { - &mut self.adjacency_table + fn store_mut(&mut self) -> &mut AdjacencyTable { + &mut self.store } - fn adjacency_table(&self) -> &AdjacencyTable { - &self.adjacency_table + fn store(&self) -> &AdjacencyTable { + &self.store } fn with_capacity(capacity: usize) -> Self { Self { - adjacency_table: AdjacencyTable::with_capacity(capacity), + store: AdjacencyTable::with_capacity(capacity), } } } @@ -49,7 +74,9 @@ impl Subgraph for UndirectedGraph {} impl From> for UndirectedGraph { fn from(adjacency_table: AdjacencyTable) -> Self { - Self { adjacency_table } + Self { + store: adjacency_table, + } } } @@ -91,6 +118,6 @@ mod tests { graph.add_edge(i.into()); } - assert_eq!(graph.neighbours("a").unwrap(), &vec![("b", 5), ("c", 7)]); + assert_eq!(graph.neighbors("a").unwrap(), &vec![("b", 5), ("c", 7)]); } } From 283f2280b17645ffc60a3eef1a3c262a28d13bad Mon Sep 17 00:00:00 2001 From: FL03 Date: Thu, 23 Mar 2023 10:30:48 -0500 Subject: [PATCH 08/13] Update --- graph/src/cmp/edge.rs | 3 ++- graph/src/cmp/mod.rs | 9 --------- graph/src/directed.rs | 6 +----- graph/src/lib.rs | 18 +++--------------- graph/src/search/bfs.rs | 2 +- graph/src/search/dfs.rs | 2 +- graph/src/search/mod.rs | 2 +- graph/src/specs.rs | 27 +++++++++++++++++++++++++++ graph/src/store/matrix.rs | 2 +- graph/src/store/mod.rs | 2 +- graph/src/store/table.rs | 25 +------------------------ graph/src/undirected.rs | 6 +----- 12 files changed, 40 insertions(+), 64 deletions(-) create mode 100644 graph/src/specs.rs diff --git a/graph/src/cmp/edge.rs b/graph/src/cmp/edge.rs index dfe2bbb..9aa961e 100644 --- a/graph/src/cmp/edge.rs +++ b/graph/src/cmp/edge.rs @@ -3,7 +3,8 @@ Contrib: FL03 Description: an edge consists of two nodes and an optional edge value */ -use super::{Node, Pair}; +use super::Pair; +use crate::Node; use serde::{Deserialize, Serialize}; pub trait Related {} diff --git a/graph/src/cmp/mod.rs b/graph/src/cmp/mod.rs index 30bb457..fa8f92e 100644 --- a/graph/src/cmp/mod.rs +++ b/graph/src/cmp/mod.rs @@ -7,12 +7,3 @@ pub use self::{edge::*, pair::*}; pub(crate) mod edge; pub(crate) mod pair; - -/// [Node] describes compatible vertices of the [super::Graph] -pub trait Node: Clone + Eq + std::hash::Hash {} - -impl Node for char {} - -impl Node for &str {} - -impl Node for String {} diff --git a/graph/src/directed.rs b/graph/src/directed.rs index acada8b..f3e14b4 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -3,11 +3,7 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::{ - cmp::{Edge, Node}, - store::AdjacencyTable, - Contain, Graph, Subgraph, -}; +use crate::{cmp::Edge, store::AdjacencyTable, Contain, Graph, Node, Subgraph}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] diff --git a/graph/src/lib.rs b/graph/src/lib.rs index dfd9fb5..831bef3 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -3,9 +3,10 @@ Contrib: FL03 Description: This library is dedicated to graphs, explicitly implementing generic directed and undirected data-structures while providing the tools to create new ones. */ -pub use self::{directed::*, undirected::*}; +pub use self::{directed::*, specs::*, undirected::*}; pub(crate) mod directed; +pub(crate) mod specs; pub(crate) mod undirected; pub mod cmp; @@ -13,24 +14,11 @@ pub mod errors; pub mod search; pub mod store; -use cmp::{Edge, Node}; +use cmp::Edge; use errors::GraphError; use std::collections::HashSet; use store::AdjacencyTable; -pub trait Contain { - /// [Contain::contains] returns true if the given element is in the [Contain] instance - fn contains(&self, elem: &T) -> bool; - /// [Contain::contains_many] returns true if all elements in the given iterator are in the [Contain] instance - fn contains_many(&self, iter: impl IntoIterator) -> bool { - iter.into_iter().all(|i| self.contains(&i)) - } - /// [Contain::contains_some] returns true if any element in the given iterator is in the [Contain] instance - fn contains_some(&self, iter: impl IntoIterator) -> bool { - iter.into_iter().any(|i| self.contains(&i)) - } -} - /// [Graph] describes the basic operations of a graph data-structure pub trait Graph: Clone + Contain + Contain> where diff --git a/graph/src/search/bfs.rs b/graph/src/search/bfs.rs index 9c6827e..11c5982 100644 --- a/graph/src/search/bfs.rs +++ b/graph/src/search/bfs.rs @@ -3,7 +3,7 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::cmp::Node; +use crate::Node; use crate::{Contain, Graph}; use std::collections::{HashSet, VecDeque}; diff --git a/graph/src/search/dfs.rs b/graph/src/search/dfs.rs index 63a0fba..acdb41e 100644 --- a/graph/src/search/dfs.rs +++ b/graph/src/search/dfs.rs @@ -3,7 +3,7 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::cmp::Node; +use crate::Node; use crate::{Contain, Graph}; #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] diff --git a/graph/src/search/mod.rs b/graph/src/search/mod.rs index 3408a0b..52ceb61 100644 --- a/graph/src/search/mod.rs +++ b/graph/src/search/mod.rs @@ -7,7 +7,7 @@ pub mod bfs; pub mod dfs; -use crate::{cmp::Node, Contain, Graph}; +use crate::{Contain, Graph, Node}; pub trait BreadthFirstSearchable { fn breadth_first_search(&self, start: N) -> Vec; diff --git a/graph/src/specs.rs b/graph/src/specs.rs new file mode 100644 index 0000000..7d970c0 --- /dev/null +++ b/graph/src/specs.rs @@ -0,0 +1,27 @@ +/* + Appellation: specs + Contrib: FL03 + Description: ... Summary ... +*/ + +pub trait Contain { + /// [Contain::contains] returns true if the given element is in the [Contain] instance + fn contains(&self, elem: &T) -> bool; + /// [Contain::contains_many] returns true if all elements in the given iterator are in the [Contain] instance + fn contains_many(&self, iter: impl IntoIterator) -> bool { + iter.into_iter().all(|i| self.contains(&i)) + } + /// [Contain::contains_some] returns true if any element in the given iterator is in the [Contain] instance + fn contains_some(&self, iter: impl IntoIterator) -> bool { + iter.into_iter().any(|i| self.contains(&i)) + } +} + +/// [Node] describes compatible vertices of the [crate::Graph] +pub trait Node: Clone + Eq + std::hash::Hash {} + +impl Node for char {} + +impl Node for &str {} + +impl Node for String {} diff --git a/graph/src/store/matrix.rs b/graph/src/store/matrix.rs index 0e2f641..34d7d80 100644 --- a/graph/src/store/matrix.rs +++ b/graph/src/store/matrix.rs @@ -3,7 +3,7 @@ Contrib: FL03 Description: an adjacency table */ -use crate::cmp::Node; +use crate::Node; use serde::{Deserialize, Serialize}; use std::iter::Extend; diff --git a/graph/src/store/mod.rs b/graph/src/store/mod.rs index ba0bfe0..ed75390 100644 --- a/graph/src/store/mod.rs +++ b/graph/src/store/mod.rs @@ -8,7 +8,7 @@ pub use self::{matrix::*, table::*}; pub(crate) mod matrix; pub(crate) mod table; -use crate::cmp::Node; +use crate::Node; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] diff --git a/graph/src/store/table.rs b/graph/src/store/table.rs index 7b0324f..fcf60ba 100644 --- a/graph/src/store/table.rs +++ b/graph/src/store/table.rs @@ -3,34 +3,11 @@ Contrib: FL03 Description: an adjacency table */ -use crate::cmp::Node; +use crate::Node; use serde::{Deserialize, Serialize}; use std::collections::{hash_map, HashMap}; use std::iter::Extend; -pub trait HashMapLike: - Extend<(K, V)> - + FromIterator<(K, V)> - + IntoIterator> -{ - fn new() -> Self; - fn capacity(&self) -> usize; - fn clear(&mut self) { - self.table_mut().clear() - } - fn drain(&mut self) -> hash_map::Drain<'_, K, V>; - fn entry(&mut self, key: K) -> hash_map::Entry<'_, K, V> { - self.table_mut().entry(key) - } - fn insert(&mut self, key: K, val: V) -> Option; - fn get(&self, key: &K) -> Option<&V>; - fn keys(&self) -> hash_map::Keys { - self.table().keys() - } - fn table(&self) -> &HashMap; - fn table_mut(&mut self) -> &mut HashMap; -} - #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct AdjacencyTable(HashMap>); diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index a68dbd6..8837cfb 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -3,11 +3,7 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::{ - cmp::{Edge, Node}, - store::AdjacencyTable, - Contain, Graph, Subgraph, -}; +use crate::{cmp::Edge, store::AdjacencyTable, Contain, Graph, Node, Subgraph}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] From 99a782d073d9621f2ecbef10d293a151e28009d0 Mon Sep 17 00:00:00 2001 From: FL03 Date: Thu, 23 Mar 2023 14:48:36 -0500 Subject: [PATCH 09/13] Update --- graph/src/directed.rs | 22 +++++++++++++++++----- graph/src/lib.rs | 8 ++++++-- graph/src/search/bfs.rs | 2 +- graph/src/search/dfs.rs | 2 +- graph/src/specs.rs | 4 ++-- graph/src/undirected.rs | 20 ++++++++++++++++---- 6 files changed, 43 insertions(+), 15 deletions(-) diff --git a/graph/src/directed.rs b/graph/src/directed.rs index f3e14b4..fd28456 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -61,10 +61,22 @@ impl Graph for DirectedGraph { impl Subgraph for DirectedGraph {} impl From> for DirectedGraph { - fn from(adjacency_table: AdjacencyTable) -> Self { - Self { - store: adjacency_table, - } + fn from(store: AdjacencyTable) -> Self { + Self { store } + } +} + +impl std::ops::Index for DirectedGraph { + type Output = Vec<(N, V)>; + + fn index(&self, index: N) -> &Self::Output { + self.store.get(&index).unwrap() + } +} + +impl std::ops::IndexMut for DirectedGraph { + fn index_mut(&mut self, index: N) -> &mut Self::Output { + self.store.get_mut(&index).unwrap() } } @@ -113,7 +125,7 @@ mod tests { graph.add_node("a"); graph.add_node("b"); graph.add_node("c"); - assert!(graph.contains_many(["a", "b", "c"])); + assert!(graph.contains_all(["a", "b", "c"])); assert!(graph.contains_some(["a", "b", "c", "d"])); } } diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 831bef3..550de34 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -16,11 +16,15 @@ pub mod store; use cmp::Edge; use errors::GraphError; -use std::collections::HashSet; +use std::{ + collections::HashSet, + ops::{Index, IndexMut}, +}; use store::AdjacencyTable; /// [Graph] describes the basic operations of a graph data-structure -pub trait Graph: Clone + Contain + Contain> +pub trait Graph: + Clone + Contain + Contain> + Index + IndexMut where N: Node, V: Clone + PartialEq, diff --git a/graph/src/search/bfs.rs b/graph/src/search/bfs.rs index 11c5982..9de23f1 100644 --- a/graph/src/search/bfs.rs +++ b/graph/src/search/bfs.rs @@ -74,6 +74,6 @@ mod tests { } let mut bfs = BreadthFirstSearch::new(); bfs.search(graph, "a"); - assert!(bfs.contains_many(["b", "c", "a"])); + assert!(bfs.contains_all(["b", "c", "a"])); } } diff --git a/graph/src/search/dfs.rs b/graph/src/search/dfs.rs index acdb41e..1085bd5 100644 --- a/graph/src/search/dfs.rs +++ b/graph/src/search/dfs.rs @@ -73,6 +73,6 @@ mod tests { // dfs.search(graph, "a"); // - assert!(dfs.contains_many(["b", "c", "a"])); + assert!(dfs.contains_all(["b", "c", "a"])); } } diff --git a/graph/src/specs.rs b/graph/src/specs.rs index 7d970c0..99a6463 100644 --- a/graph/src/specs.rs +++ b/graph/src/specs.rs @@ -7,8 +7,8 @@ pub trait Contain { /// [Contain::contains] returns true if the given element is in the [Contain] instance fn contains(&self, elem: &T) -> bool; - /// [Contain::contains_many] returns true if all elements in the given iterator are in the [Contain] instance - fn contains_many(&self, iter: impl IntoIterator) -> bool { + /// [Contain::contains_all] returns true if all elements in the given iterator are in the [Contain] instance + fn contains_all(&self, iter: impl IntoIterator) -> bool { iter.into_iter().all(|i| self.contains(&i)) } /// [Contain::contains_some] returns true if any element in the given iterator is in the [Contain] instance diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index 8837cfb..dd594ec 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -69,10 +69,22 @@ impl Graph for UndirectedGraph { impl Subgraph for UndirectedGraph {} impl From> for UndirectedGraph { - fn from(adjacency_table: AdjacencyTable) -> Self { - Self { - store: adjacency_table, - } + fn from(store: AdjacencyTable) -> Self { + Self { store } + } +} + +impl std::ops::Index for UndirectedGraph { + type Output = Vec<(N, V)>; + + fn index(&self, index: N) -> &Self::Output { + self.store.get(&index).unwrap() + } +} + +impl std::ops::IndexMut for UndirectedGraph { + fn index_mut(&mut self, index: N) -> &mut Self::Output { + self.store.get_mut(&index).unwrap() } } From a1350e80d4d525310bfef41270b52c12d268f75c Mon Sep 17 00:00:00 2001 From: FL03 Date: Thu, 23 Mar 2023 16:20:42 -0500 Subject: [PATCH 10/13] Update --- graph/src/lib.rs | 14 ++++++-------- graph/src/store/matrix.rs | 22 ++++++++++++++++++++++ graph/src/store/table.rs | 14 ++++++++++++++ graph/src/undirected.rs | 18 ++++++++---------- 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 550de34..4913cea 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -16,15 +16,12 @@ pub mod store; use cmp::Edge; use errors::GraphError; -use std::{ - collections::HashSet, - ops::{Index, IndexMut}, -}; +use std::{collections::HashSet, ops::IndexMut}; use store::AdjacencyTable; /// [Graph] describes the basic operations of a graph data-structure pub trait Graph: - Clone + Contain + Contain> + Index + IndexMut + Clone + Contain + Contain> + IndexMut> where N: Node, V: Clone + PartialEq, @@ -78,9 +75,10 @@ where } /// [Graph::neighbors] attempts to return a [Vec] that contains all of the connected [Node] and their values fn neighbors(&self, node: N) -> Result<&Vec<(N, V)>, GraphError> { - match self.store().get(&node) { - None => Err(GraphError::NodeNotInGraph), - Some(i) => Ok(i), + if self.nodes().contains(&node) { + Ok(&self[node]) + } else { + Err(GraphError::NodeNotInGraph) } } /// [Graph::nodes] returns a cloned [HashSet] of the graph's current [Node]s diff --git a/graph/src/store/matrix.rs b/graph/src/store/matrix.rs index 34d7d80..74825c5 100644 --- a/graph/src/store/matrix.rs +++ b/graph/src/store/matrix.rs @@ -75,3 +75,25 @@ impl Extend<(N, Vec<(N, V)>)> for AdjacencyMatrix self.store.extend(iter); } } + +impl std::ops::Index for AdjacencyMatrix +where + N: Node, + V: Clone + PartialEq, +{ + type Output = Vec<(N, V)>; + + fn index(&self, index: N) -> &Self::Output { + self.get(&index).unwrap() + } +} + +impl std::ops::IndexMut for AdjacencyMatrix +where + N: Node, + V: Clone + PartialEq, +{ + fn index_mut(&mut self, index: N) -> &mut Self::Output { + self.get_mut(&index).unwrap() + } +} diff --git a/graph/src/store/table.rs b/graph/src/store/table.rs index fcf60ba..2bf1255 100644 --- a/graph/src/store/table.rs +++ b/graph/src/store/table.rs @@ -85,3 +85,17 @@ impl IntoIterator for AdjacencyTable { self.0.into_iter() } } + +impl std::ops::Index for AdjacencyTable { + type Output = Vec<(N, V)>; + + fn index(&self, index: N) -> &Self::Output { + self.0.get(&index).unwrap() + } +} + +impl std::ops::IndexMut for AdjacencyTable { + fn index_mut(&mut self, index: N) -> &mut Self::Output { + self.0.get_mut(&index).unwrap() + } +} diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index dd594ec..9b97090 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -78,21 +78,19 @@ impl std::ops::Index for UndirectedGraph type Output = Vec<(N, V)>; fn index(&self, index: N) -> &Self::Output { - self.store.get(&index).unwrap() + &self.store[index] } } impl std::ops::IndexMut for UndirectedGraph { fn index_mut(&mut self, index: N) -> &mut Self::Output { - self.store.get_mut(&index).unwrap() + self.store.index_mut(index) } } #[cfg(test)] mod tests { - use super::Graph; - use super::UndirectedGraph; - use crate::cmp::Edge; + use super::*; const TEST_EDGES: [(&str, &str, usize); 3] = [("a", "b", 5), ("c", "a", 7), ("b", "c", 10)]; @@ -112,10 +110,10 @@ mod tests { for i in TEST_EDGES { graph.add_edge(i.into()); } - - for edge in EXPECTED { - assert!(graph.edges().contains(&Edge::from(edge))); - } + // assert that the graph contains all the edges + assert!(graph.contains_all(EXPECTED.into_iter().map(Edge::from).collect::>())); + // assert that the graph can be indexed + assert_eq!(graph["a"], vec![("b", 5), ("c", 7)]); } #[test] @@ -126,6 +124,6 @@ mod tests { graph.add_edge(i.into()); } - assert_eq!(graph.neighbors("a").unwrap(), &vec![("b", 5), ("c", 7)]); + assert_eq!(graph["a"], vec![("b", 5), ("c", 7)]); } } From fb9f35f755311e0b51e0ddc7c1907a880e703d8b Mon Sep 17 00:00:00 2001 From: FL03 Date: Fri, 24 Mar 2023 16:00:24 -0500 Subject: [PATCH 11/13] Update --- graph/src/cmp/mod.rs | 3 +- graph/src/cmp/neighbors.rs | 5 +++ graph/src/specs.rs | 25 +++++++++++++++ graph/src/store/mod.rs | 51 ++++++++++++++++++++++++++++++- graph/src/store/table.rs | 62 +++++++++++++++++++++++--------------- 5 files changed, 120 insertions(+), 26 deletions(-) create mode 100644 graph/src/cmp/neighbors.rs diff --git a/graph/src/cmp/mod.rs b/graph/src/cmp/mod.rs index fa8f92e..15df846 100644 --- a/graph/src/cmp/mod.rs +++ b/graph/src/cmp/mod.rs @@ -3,7 +3,8 @@ Contrib: FL03 Description: components (cmp) for building effecient graph data-structures */ -pub use self::{edge::*, pair::*}; +pub use self::{edge::*, neighbors::*, pair::*}; pub(crate) mod edge; +pub(crate) mod neighbors; pub(crate) mod pair; diff --git a/graph/src/cmp/neighbors.rs b/graph/src/cmp/neighbors.rs new file mode 100644 index 0000000..d6269ca --- /dev/null +++ b/graph/src/cmp/neighbors.rs @@ -0,0 +1,5 @@ +/* + Appellation: neighbors + Contrib: FL03 + Description: ... summary ... +*/ diff --git a/graph/src/specs.rs b/graph/src/specs.rs index 99a6463..3f56922 100644 --- a/graph/src/specs.rs +++ b/graph/src/specs.rs @@ -25,3 +25,28 @@ impl Node for char {} impl Node for &str {} impl Node for String {} + +impl Node for usize {} + +impl Node for u8 {} + +impl Node for u16 {} + +impl Node for u32 {} + +impl Node for u64 {} + +impl Node for u128 {} + +impl Node for isize {} + +impl Node for i8 {} + +impl Node for i16 {} + +impl Node for i32 {} + +impl Node for i64 {} + +impl Node for i128 {} + diff --git a/graph/src/store/mod.rs b/graph/src/store/mod.rs index ed75390..97cda83 100644 --- a/graph/src/store/mod.rs +++ b/graph/src/store/mod.rs @@ -8,8 +8,57 @@ pub use self::{matrix::*, table::*}; pub(crate) mod matrix; pub(crate) mod table; -use crate::Node; +use crate::{cmp::Edge, Contain, Node}; use serde::{Deserialize, Serialize}; +use std::ops::IndexMut; + +pub struct Entry +where + N: Node, + V: Clone + PartialEq, +{ + key: N, + value: Vec<(N, V)>, +} + +impl Entry { + pub fn new(key: N, value: Vec<(N, V)>) -> Self { + Self { key, value } + } + pub fn key(&self) -> &N { + &self.key + } + pub fn value(&self) -> &Vec<(N, V)> { + &self.value + } + pub fn value_mut(&mut self) -> &mut Vec<(N, V)> { + &mut self.value + } +} + +impl Contain<(N, V)> for Entry { + fn contains(&self, elem: &(N, V)) -> bool { + self.value.contains(elem) + } +} + +pub trait Store: Extend> + IndexMut> +where + N: Node, + V: Clone + PartialEq, +{ + fn clear(&mut self); + fn contains_key(&self, key: &N) -> bool; + fn drain(&mut self); + fn entry(&mut self, key: N); + fn get(&self, key: &N) -> Option<&Vec<(N, V)>> { + if self.contains_key(key) { + Some(&self[key.clone()]) + } else { + None + } + } +} #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub enum Stores diff --git a/graph/src/store/table.rs b/graph/src/store/table.rs index 2bf1255..c64c208 100644 --- a/graph/src/store/table.rs +++ b/graph/src/store/table.rs @@ -9,62 +9,76 @@ use std::collections::{hash_map, HashMap}; use std::iter::Extend; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct AdjacencyTable(HashMap>); +pub struct AdjacencyTable where N: Node { + store: HashMap> +} impl AdjacencyTable { pub fn new() -> Self { - Self(HashMap::new()) - } - pub fn with_capacity(capacity: usize) -> Self { - Self(HashMap::with_capacity(capacity)) + Self { + store: HashMap::new() + } } pub fn capacity(&self) -> usize { - self.0.capacity() + self.store.capacity() } pub fn clear(&mut self) { - self.0.clear() + self.store.clear() } pub fn contains_key(&self, key: &N) -> bool { - self.0.contains_key(key) + self.store.contains_key(key) } pub fn drain(&mut self) -> hash_map::Drain<'_, N, Vec<(N, V)>> { - self.0.drain() + self.store.drain() } pub fn entry(&mut self, key: N) -> hash_map::Entry<'_, N, Vec<(N, V)>> { - self.0.entry(key) + self.store.entry(key) } pub fn insert(&mut self, key: N, val: Vec<(N, V)>) -> Option> { - self.0.insert(key, val) + self.store.insert(key, val) } pub fn get(&self, key: &N) -> Option<&Vec<(N, V)>> { - self.0.get(key) + self.store.get(key) } pub fn get_key_value(&self, key: &N) -> Option<(&N, &Vec<(N, V)>)> { - self.0.get_key_value(key) + self.store.get_key_value(key) } pub fn get_mut(&mut self, key: &N) -> Option<&mut Vec<(N, V)>> { - self.0.get_mut(key) + self.store.get_mut(key) } pub fn keys(&self) -> hash_map::Keys> { - self.0.keys() + self.store.keys() } pub fn len(&self) -> usize { - self.0.len() + self.store.len() } pub fn table(self) -> HashMap> { - self.0 + self.store } pub fn values(&self) -> hash_map::Values> { - self.0.values() + self.store.values() } pub fn values_mut(&mut self) -> hash_map::ValuesMut> { - self.0.values_mut() + self.store.values_mut() + } + pub fn with_capacity(capacity: usize) -> Self { + Self { + store: HashMap::with_capacity(capacity) + } } } impl Extend<(N, Vec<(N, V)>)> for AdjacencyTable { fn extend)>>(&mut self, iter: T) { - self.0.extend(iter) + self.store.extend(iter) + } +} + +impl From>> for AdjacencyTable { + fn from(store: HashMap>) -> Self { + Self { + store + } } } @@ -72,7 +86,7 @@ impl FromIterator<(N, Vec<(N, V)>)> for AdjacencyTable { fn from_iter)>>(iter: T) -> Self { let mut map = HashMap::with_hasher(Default::default()); map.extend(iter); - AdjacencyTable(map) + AdjacencyTable::from(map) } } @@ -82,7 +96,7 @@ impl IntoIterator for AdjacencyTable { type IntoIter = hash_map::IntoIter>; fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() + self.store.into_iter() } } @@ -90,12 +104,12 @@ impl std::ops::Index for AdjacencyTable { type Output = Vec<(N, V)>; fn index(&self, index: N) -> &Self::Output { - self.0.get(&index).unwrap() + &self.store[&index] } } impl std::ops::IndexMut for AdjacencyTable { fn index_mut(&mut self, index: N) -> &mut Self::Output { - self.0.get_mut(&index).unwrap() + self.store.get_mut(&index).unwrap() } } From 8e0ef580f489de84843751806f5d3a80024d4435 Mon Sep 17 00:00:00 2001 From: FL03 Date: Sun, 26 Mar 2023 11:22:19 -0500 Subject: [PATCH 12/13] Update --- graph/src/cmp/edge.rs | 49 ++++++++++++++----- graph/src/cmp/mod.rs | 6 +-- graph/src/cmp/pair.rs | 18 ++++++- graph/src/directed.rs | 77 +++++++++++++++++++++++------- graph/src/lib.rs | 100 ++++++++++++++++++++++++++++++++++----- graph/src/search/bfs.rs | 35 +++++++------- graph/src/search/dfs.rs | 36 +++++++------- graph/src/search/mod.rs | 21 ++++---- graph/src/specs.rs | 33 ++++++++++++- graph/src/store/mod.rs | 20 +++++--- graph/src/store/table.rs | 15 +++--- graph/src/undirected.rs | 86 ++++++++++++++++++++++++++------- 12 files changed, 379 insertions(+), 117 deletions(-) diff --git a/graph/src/cmp/edge.rs b/graph/src/cmp/edge.rs index 9aa961e..0c152e9 100644 --- a/graph/src/cmp/edge.rs +++ b/graph/src/cmp/edge.rs @@ -4,34 +4,59 @@ Description: an edge consists of two nodes and an optional edge value */ use super::Pair; -use crate::Node; +use crate::{Node, Weight}; use serde::{Deserialize, Serialize}; pub trait Related {} #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Edge(N, N, V); +pub struct Edge +where + N: Node, + V: Weight, +{ + pair: Pair, + weight: V, +} -impl Edge { - pub fn new(a: N, b: N, v: V) -> Self { - Self(a, b, v) +impl Edge +where + N: Node, + V: Weight, +{ + pub fn new(pair: Pair, weight: V) -> Self { + Self { pair, weight } } pub fn pair(&self) -> Pair { - Pair::new(self.0.clone(), self.1.clone()) + self.pair.clone() } - pub fn value(&self) -> V { - self.2.clone() + pub fn value(&self) -> &V { + &self.weight } } -impl From<(N, N, V)> for Edge { +impl From<(N, N, V)> for Edge +where + N: Node, + V: Weight, +{ fn from(data: (N, N, V)) -> Self { - Self(data.0, data.1, data.2) + Self { + pair: Pair::new(data.0, data.1), + weight: data.2, + } } } -impl From<(Pair, V)> for Edge { +impl From<(Pair, V)> for Edge +where + N: Node, + V: Weight, +{ fn from(data: (Pair, V)) -> Self { - Self(data.0 .0, data.0 .1, data.1) + Self { + pair: data.0, + weight: data.1, + } } } diff --git a/graph/src/cmp/mod.rs b/graph/src/cmp/mod.rs index 15df846..d00028c 100644 --- a/graph/src/cmp/mod.rs +++ b/graph/src/cmp/mod.rs @@ -5,6 +5,6 @@ */ pub use self::{edge::*, neighbors::*, pair::*}; -pub(crate) mod edge; -pub(crate) mod neighbors; -pub(crate) mod pair; +mod edge; +mod neighbors; +mod pair; diff --git a/graph/src/cmp/pair.rs b/graph/src/cmp/pair.rs index 0aad2cd..a7eb6f7 100644 --- a/graph/src/cmp/pair.rs +++ b/graph/src/cmp/pair.rs @@ -6,10 +6,24 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Pair(pub T, pub T); +pub struct Pair(pub T, pub T) +where + T: Default; -impl Pair { +impl Pair +where + T: Default, +{ pub fn new(a: T, b: T) -> Self { Self(a, b) } } + +impl From<(T, T)> for Pair +where + T: Default, +{ + fn from(data: (T, T)) -> Self { + Self(data.0, data.1) + } +} diff --git a/graph/src/directed.rs b/graph/src/directed.rs index fd28456..40973cf 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -3,54 +3,82 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::{cmp::Edge, store::AdjacencyTable, Contain, Graph, Node, Subgraph}; +use crate::{cmp::Edge, store::AdjacencyTable}; +use crate::{Contain, Graph, GraphExt, Node, Subgraph, Weight}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct DirectedGraph where N: Node, - V: Clone + PartialEq, + V: Weight, { store: AdjacencyTable, } -impl AsMut> for DirectedGraph { +impl AsMut> for DirectedGraph +where + N: Node, + V: Weight, +{ fn as_mut(&mut self) -> &mut AdjacencyTable { &mut self.store } } -impl AsRef> for DirectedGraph { +impl AsRef> for DirectedGraph +where + N: Node, + V: Weight, +{ fn as_ref(&self) -> &AdjacencyTable { &self.store } } -impl Contain for DirectedGraph { +impl Contain for DirectedGraph +where + N: Node, + V: Weight, +{ fn contains(&self, elem: &N) -> bool { self.store.contains_key(elem) } } -impl Contain> for DirectedGraph { +impl Contain> for DirectedGraph +where + N: Node, + V: Weight, +{ fn contains(&self, elem: &Edge) -> bool { self.edges().contains(elem) } } -impl Graph for DirectedGraph { - fn new() -> Self { - Self { - store: AdjacencyTable::new(), - } - } +impl Graph for DirectedGraph +where + N: Node, + V: Weight, +{ fn store_mut(&mut self) -> &mut AdjacencyTable { &mut self.store } fn store(&self) -> &AdjacencyTable { &self.store } +} + +impl GraphExt for DirectedGraph +where + N: Node, + V: Weight, +{ + fn new() -> Self { + Self { + store: AdjacencyTable::new(), + } + } fn with_capacity(capacity: usize) -> Self { Self { store: AdjacencyTable::with_capacity(capacity), @@ -58,15 +86,28 @@ impl Graph for DirectedGraph { } } -impl Subgraph for DirectedGraph {} +impl Subgraph for DirectedGraph +where + N: Node, + V: Weight, +{ +} -impl From> for DirectedGraph { +impl From> for DirectedGraph +where + N: Node, + V: Weight, +{ fn from(store: AdjacencyTable) -> Self { Self { store } } } -impl std::ops::Index for DirectedGraph { +impl std::ops::Index for DirectedGraph +where + N: Node, + V: Weight, +{ type Output = Vec<(N, V)>; fn index(&self, index: N) -> &Self::Output { @@ -74,7 +115,11 @@ impl std::ops::Index for DirectedGraph { } } -impl std::ops::IndexMut for DirectedGraph { +impl std::ops::IndexMut for DirectedGraph +where + N: Node, + V: Weight, +{ fn index_mut(&mut self, index: N) -> &mut Self::Output { self.store.get_mut(&index).unwrap() } diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 4913cea..951e2f3 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -3,14 +3,14 @@ Contrib: FL03 Description: This library is dedicated to graphs, explicitly implementing generic directed and undirected data-structures while providing the tools to create new ones. */ -pub use self::{directed::*, specs::*, undirected::*}; +pub use self::{cmp::*, directed::*, errors::*, specs::*, undirected::*}; +pub(crate) mod cmp; pub(crate) mod directed; +mod errors; pub(crate) mod specs; pub(crate) mod undirected; -pub mod cmp; -pub mod errors; pub mod search; pub mod store; @@ -19,14 +19,18 @@ use errors::GraphError; use std::{collections::HashSet, ops::IndexMut}; use store::AdjacencyTable; +pub enum GraphType { + Directed, + Undirected, +} + /// [Graph] describes the basic operations of a graph data-structure pub trait Graph: Clone + Contain + Contain> + IndexMut> where N: Node, - V: Clone + PartialEq, + V: Weight, { - fn new() -> Self; /// [Graph::add_edge] inserts a new [Edge] into the graph fn add_edge(&mut self, edge: Edge) { let pair = edge.pair(); @@ -34,7 +38,7 @@ where self.add_node(pair.1.clone()); self.store_mut().entry(pair.0.clone()).and_modify(|e| { - e.push((pair.1, edge.value())); + e.push((pair.1, edge.value().clone())); }); } /// [Graph::add_edges] insert several edges into the graph @@ -59,10 +63,24 @@ where self.add_node(i); } } - /// [Graph::store_mut] returns an owned, mutable instance of the [AdjacencyTable] - fn store_mut(&mut self) -> &mut AdjacencyTable; - /// [Graph::store] returns an owned instance of the [AdjacencyTable] - fn store(&self) -> &AdjacencyTable; + /// [Graph::contains_edge] returns true if the given [Edge] is in the graph + fn contains_edge(&self, edge: &Edge) -> bool { + match self.store().get(&edge.pair().0) { + Some(edges) => edges.contains(&(edge.pair().1, edge.value().clone())), + None => false, + } + } + /// [Graph::contains_node] returns true if the given [Node] is in the graph + fn contains_node(&self, node: &N) -> bool { + self.store().contains_key(node) + } + /// [Graph::degree] returns the degree of the given [Node] + fn degree(&self, node: &N) -> Result { + match self.store().get(node) { + Some(edges) => Ok(edges.len()), + None => Err(GraphError::NodeNotInGraph), + } + } /// [Graph::edges] returns all of the edges persisting within the graph fn edges(&self) -> Vec> { let mut edges = Vec::new(); @@ -73,6 +91,52 @@ where } edges } + /// [Graph::edges_from] returns all of the edges originating from the given [Node] + fn edges_from(&self, node: &N) -> Result>, GraphError> { + match self.store().get(node) { + Some(edges) => Ok(edges + .iter() + .map(|(n, v)| Edge::from((node.clone(), n.clone(), v.clone()))) + .collect()), + None => Err(GraphError::NodeNotInGraph), + } + } + /// [Graph::edges_to] returns all of the edges terminating at the given [Node] + fn edges_to(&self, node: &N) -> Result>, GraphError> { + let mut edges = Vec::new(); + for (from_node, from_node_neighbours) in self.store().clone() { + for (to_node, weight) in from_node_neighbours { + if to_node == node.clone() { + edges.push((from_node.clone(), to_node, weight).into()); + } + } + } + Ok(edges) + } + /// [Graph::is_connected] returns true if the graph is connected + fn is_connected(&self) -> bool { + let mut visited = HashSet::new(); + let mut stack = vec![self.nodes().iter().next().unwrap().clone()]; + + while let Some(node) = stack.pop() { + if !visited.contains(&node) { + visited.insert(node.clone()); + stack.extend( + self.neighbors(node) + .unwrap() + .iter() + .cloned() + .map(|(n, _)| n), + ); + } + } + + visited.len() == self.nodes().len() + } + /// [Graph::store_mut] returns an owned, mutable instance of the [AdjacencyTable] + fn store_mut(&mut self) -> &mut AdjacencyTable; + /// [Graph::store] returns an owned instance of the [AdjacencyTable] + fn store(&self) -> &AdjacencyTable; /// [Graph::neighbors] attempts to return a [Vec] that contains all of the connected [Node] and their values fn neighbors(&self, node: N) -> Result<&Vec<(N, V)>, GraphError> { if self.nodes().contains(&node) { @@ -85,11 +149,23 @@ where fn nodes(&self) -> HashSet { self.store().keys().cloned().collect() } - /// [Graph::with_capacity] is a method for creating a graph with a set number of nodes +} + +pub trait GraphExt: Graph +where + N: Node, + V: Weight, +{ + fn new() -> Self; + /// [GraphExt::with_capacity] is a method for creating a graph with a set number of nodes fn with_capacity(capacity: usize) -> Self; } -pub trait Subgraph: Graph { +pub trait Subgraph: Graph +where + N: Node, + V: Weight, +{ fn is_subgraph(&self, graph: impl Graph) -> bool { self.nodes().is_subset(&graph.nodes()) } diff --git a/graph/src/search/bfs.rs b/graph/src/search/bfs.rs index 9de23f1..de9e81f 100644 --- a/graph/src/search/bfs.rs +++ b/graph/src/search/bfs.rs @@ -3,8 +3,8 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::Node; -use crate::{Contain, Graph}; +use super::Searcher; +use crate::{Contain, Graph, Node, Weight}; use std::collections::{HashSet, VecDeque}; #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -20,14 +20,24 @@ impl BreadthFirstSearch { visited: HashSet::new(), } } - pub fn clear(&mut self) { +} + +impl Contain for BreadthFirstSearch { + fn contains(&self, elem: &N) -> bool { + self.visited.contains(elem) + } +} + +impl Searcher for BreadthFirstSearch +where + N: Node, + V: Weight, +{ + fn reset(&mut self) { self.visited.clear(); self.queue.clear(); } - pub fn search(&mut self, graph: impl Graph, start: N) -> Vec - where - V: Clone + PartialEq, - { + fn search(&mut self, graph: impl Graph, start: N) -> Vec { self.queue.push_back(start); while let Some(node) = self.queue.pop_front() { @@ -46,17 +56,10 @@ impl BreadthFirstSearch { } } -impl Contain for BreadthFirstSearch { - fn contains(&self, elem: &N) -> bool { - self.visited.contains(elem) - } -} - #[cfg(test)] mod tests { use super::*; - use crate::cmp::Edge; - use crate::directed::DirectedGraph; + use crate::{DirectedGraph, Edge, GraphExt}; const TEST_EDGES: [(&str, &str, usize); 5] = [ ("a", "b", 5), @@ -67,7 +70,7 @@ mod tests { ]; #[test] - fn test_bfs() { + fn test_bfs_directed() { let mut graph = DirectedGraph::<&str, usize>::new(); for i in TEST_EDGES { graph.add_edge(Edge::from(i)); diff --git a/graph/src/search/dfs.rs b/graph/src/search/dfs.rs index 1085bd5..df6440f 100644 --- a/graph/src/search/dfs.rs +++ b/graph/src/search/dfs.rs @@ -3,8 +3,8 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::Node; -use crate::{Contain, Graph}; +use super::Searcher; +use crate::{Contain, Graph, Node, Weight}; #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct DepthFirstSearch { @@ -19,14 +19,25 @@ impl DepthFirstSearch { visited: Vec::new(), } } - pub fn clear(&mut self) { +} + +impl Contain for DepthFirstSearch { + fn contains(&self, elem: &N) -> bool { + self.visited.contains(elem) + } +} + +impl Searcher for DepthFirstSearch +where + N: Node, + V: Weight, +{ + fn reset(&mut self) { self.stack.clear(); self.visited.clear(); } - pub fn search(&mut self, graph: impl Graph, start: N) -> Vec - where - V: Clone + PartialEq, - { + + fn search(&mut self, graph: impl Graph, start: N) -> Vec { self.stack.push(start); while let Some(node) = self.stack.pop() { if !self.visited.contains(&node) { @@ -42,17 +53,10 @@ impl DepthFirstSearch { } } -impl Contain for DepthFirstSearch { - fn contains(&self, elem: &N) -> bool { - self.visited.contains(elem) - } -} - #[cfg(test)] mod tests { use super::*; - use crate::cmp::Edge; - use crate::directed::DirectedGraph; + use crate::{DirectedGraph, Edge, GraphExt}; const TEST_EDGES: [(&str, &str, usize); 5] = [ ("a", "b", 5), @@ -63,7 +67,7 @@ mod tests { ]; #[test] - fn test_dfs() { + fn test_dfs_directed() { let mut graph = DirectedGraph::<&str, usize>::new(); for i in TEST_EDGES { graph.add_edge(Edge::from(i)); diff --git a/graph/src/search/mod.rs b/graph/src/search/mod.rs index 52ceb61..b46b5c2 100644 --- a/graph/src/search/mod.rs +++ b/graph/src/search/mod.rs @@ -3,16 +3,21 @@ Contrib: FL03 Description: ... Summary ... */ +pub use self::{bfs::BreadthFirstSearch, dfs::DepthFirstSearch}; -pub mod bfs; -pub mod dfs; +mod bfs; +mod dfs; -use crate::{Contain, Graph, Node}; +use crate::{Contain, Graph, Node, Weight}; -pub trait BreadthFirstSearchable { - fn breadth_first_search(&self, start: N) -> Vec; -} - -pub trait Searcher: Contain { +/// [Searcher] is a trait that defines the behavior of a graph search algorithm. +pub trait Searcher: Contain +where + N: Node, + V: Weight, +{ + /// Search the graph for nodes. fn search(&mut self, graph: impl Graph, start: N) -> Vec; + /// Reset the search state. + fn reset(&mut self); } diff --git a/graph/src/specs.rs b/graph/src/specs.rs index 3f56922..6fcb5b4 100644 --- a/graph/src/specs.rs +++ b/graph/src/specs.rs @@ -18,7 +18,7 @@ pub trait Contain { } /// [Node] describes compatible vertices of the [crate::Graph] -pub trait Node: Clone + Eq + std::hash::Hash {} +pub trait Node: Clone + Default + Eq + std::hash::Hash {} impl Node for char {} @@ -50,3 +50,34 @@ impl Node for i64 {} impl Node for i128 {} +pub trait Weight: Clone + PartialEq {} + +impl Weight for char {} + +impl Weight for &str {} + +impl Weight for String {} + +impl Weight for usize {} + +impl Weight for u8 {} + +impl Weight for u16 {} + +impl Weight for u32 {} + +impl Weight for u64 {} + +impl Weight for u128 {} + +impl Weight for isize {} + +impl Weight for i8 {} + +impl Weight for i16 {} + +impl Weight for i32 {} + +impl Weight for i64 {} + +impl Weight for i128 {} diff --git a/graph/src/store/mod.rs b/graph/src/store/mod.rs index 97cda83..b4ea3ef 100644 --- a/graph/src/store/mod.rs +++ b/graph/src/store/mod.rs @@ -8,20 +8,24 @@ pub use self::{matrix::*, table::*}; pub(crate) mod matrix; pub(crate) mod table; -use crate::{cmp::Edge, Contain, Node}; +use crate::{cmp::Edge, Contain, Node, Weight}; use serde::{Deserialize, Serialize}; use std::ops::IndexMut; pub struct Entry where N: Node, - V: Clone + PartialEq, + V: Weight, { key: N, value: Vec<(N, V)>, } -impl Entry { +impl Entry +where + N: Node, + V: Weight, +{ pub fn new(key: N, value: Vec<(N, V)>) -> Self { Self { key, value } } @@ -36,7 +40,11 @@ impl Entry { } } -impl Contain<(N, V)> for Entry { +impl Contain<(N, V)> for Entry +where + N: Node, + V: Weight, +{ fn contains(&self, elem: &(N, V)) -> bool { self.value.contains(elem) } @@ -45,7 +53,7 @@ impl Contain<(N, V)> for Entry { pub trait Store: Extend> + IndexMut> where N: Node, - V: Clone + PartialEq, + V: Weight, { fn clear(&mut self); fn contains_key(&self, key: &N) -> bool; @@ -64,7 +72,7 @@ where pub enum Stores where N: Node, - V: Clone + PartialEq, + V: Weight, { Matrix(AdjacencyMatrix), Table(AdjacencyTable), diff --git a/graph/src/store/table.rs b/graph/src/store/table.rs index c64c208..f28b887 100644 --- a/graph/src/store/table.rs +++ b/graph/src/store/table.rs @@ -9,14 +9,17 @@ use std::collections::{hash_map, HashMap}; use std::iter::Extend; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct AdjacencyTable where N: Node { - store: HashMap> +pub struct AdjacencyTable +where + N: Node, +{ + store: HashMap>, } impl AdjacencyTable { pub fn new() -> Self { Self { - store: HashMap::new() + store: HashMap::new(), } } pub fn capacity(&self) -> usize { @@ -63,7 +66,7 @@ impl AdjacencyTable { } pub fn with_capacity(capacity: usize) -> Self { Self { - store: HashMap::with_capacity(capacity) + store: HashMap::with_capacity(capacity), } } } @@ -76,9 +79,7 @@ impl Extend<(N, Vec<(N, V)>)> for AdjacencyTable { impl From>> for AdjacencyTable { fn from(store: HashMap>) -> Self { - Self { - store - } + Self { store } } } diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index 9b97090..4159f20 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -3,54 +3,75 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::{cmp::Edge, store::AdjacencyTable, Contain, Graph, Node, Subgraph}; +use crate::{cmp::Edge, store::AdjacencyTable}; +use crate::{Contain, Graph, GraphExt, Node, Subgraph, Weight}; + use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct UndirectedGraph { +pub struct UndirectedGraph +where + N: Node, + V: Weight, +{ store: AdjacencyTable, } -impl AsMut> for UndirectedGraph { +impl AsMut> for UndirectedGraph +where + N: Node, + V: Weight, +{ fn as_mut(&mut self) -> &mut AdjacencyTable { &mut self.store } } -impl AsRef> for UndirectedGraph { +impl AsRef> for UndirectedGraph +where + N: Node, + V: Weight, +{ fn as_ref(&self) -> &AdjacencyTable { &self.store } } -impl Contain for UndirectedGraph { +impl Contain for UndirectedGraph +where + N: Node, + V: Weight, +{ fn contains(&self, elem: &N) -> bool { self.store.contains_key(elem) } } -impl Contain> for UndirectedGraph { +impl Contain> for UndirectedGraph +where + N: Node, + V: Weight, +{ fn contains(&self, elem: &Edge) -> bool { self.edges().contains(elem) } } -impl Graph for UndirectedGraph { - fn new() -> Self { - Self { - store: AdjacencyTable::new(), - } - } +impl Graph for UndirectedGraph +where + N: Node, + V: Weight, +{ fn add_edge(&mut self, edge: Edge) { let pair = edge.pair(); self.add_node(pair.0.clone()); self.add_node(pair.1.clone()); self.store.entry(pair.0.clone()).and_modify(|e| { - e.push((pair.1.clone(), edge.value())); + e.push((pair.1.clone(), edge.value().clone())); }); self.store.entry(pair.1).and_modify(|e| { - e.push((pair.0, edge.value())); + e.push((pair.0, edge.value().clone())); }); } fn store_mut(&mut self) -> &mut AdjacencyTable { @@ -59,6 +80,18 @@ impl Graph for UndirectedGraph { fn store(&self) -> &AdjacencyTable { &self.store } +} + +impl GraphExt for UndirectedGraph +where + N: Node, + V: Weight, +{ + fn new() -> Self { + Self { + store: AdjacencyTable::new(), + } + } fn with_capacity(capacity: usize) -> Self { Self { store: AdjacencyTable::with_capacity(capacity), @@ -66,15 +99,28 @@ impl Graph for UndirectedGraph { } } -impl Subgraph for UndirectedGraph {} +impl Subgraph for UndirectedGraph +where + N: Node, + V: Weight, +{ +} -impl From> for UndirectedGraph { +impl From> for UndirectedGraph +where + N: Node, + V: Weight, +{ fn from(store: AdjacencyTable) -> Self { Self { store } } } -impl std::ops::Index for UndirectedGraph { +impl std::ops::Index for UndirectedGraph +where + N: Node, + V: Weight, +{ type Output = Vec<(N, V)>; fn index(&self, index: N) -> &Self::Output { @@ -82,7 +128,11 @@ impl std::ops::Index for UndirectedGraph } } -impl std::ops::IndexMut for UndirectedGraph { +impl std::ops::IndexMut for UndirectedGraph +where + N: Node, + V: Weight, +{ fn index_mut(&mut self, index: N) -> &mut Self::Output { self.store.index_mut(index) } From 406ea1f242efb6dafbc042461bda1f9ee5c1ff6e Mon Sep 17 00:00:00 2001 From: FL03 Date: Mon, 3 Apr 2023 15:48:07 -0500 Subject: [PATCH 13/13] Update --- Cargo.toml | 2 +- merkle/Cargo.toml | 5 +- merkle/src/layers.rs | 19 +- merkle/src/lib.rs | 4 +- merkle/src/nodes.rs | 38 ++-- merkle/src/payloads.rs | 58 ++++-- merkle/src/utils.rs | 11 +- mmr/Cargo.toml | 6 +- mmr/src/actors/builders/mod.rs | 8 - mmr/src/actors/mod.rs | 7 - mmr/src/{actors/builders => }/builder.rs | 5 +- mmr/src/cmp/mod.rs | 10 + mmr/src/{cmps/nodes/node.rs => cmp/nodes.rs} | 0 mmr/src/cmp/payloads.rs | 53 +++++ mmr/src/{cmps/positions => cmp}/position.rs | 18 +- mmr/src/cmps/mod.rs | 9 - mmr/src/cmps/nodes/mod.rs | 8 - mmr/src/cmps/payloads/mod.rs | 8 - mmr/src/cmps/payloads/payload.rs | 35 ---- mmr/src/cmps/positions/mod.rs | 8 - mmr/src/core/mod.rs | 11 -- mmr/src/data/mod.rs | 8 - mmr/src/data/stores/store.rs | 76 ------- mmr/src/lib.rs | 14 +- mmr/src/{core => }/mmr.rs | 196 +++++++++---------- mmr/src/primitives.rs | 17 ++ mmr/src/{data => }/proofs/mod.rs | 0 mmr/src/{data => }/stores/mod.rs | 0 mmr/src/stores/store.rs | 65 ++++++ mmr/src/{core => }/utils.rs | 0 30 files changed, 354 insertions(+), 345 deletions(-) delete mode 100644 mmr/src/actors/builders/mod.rs delete mode 100644 mmr/src/actors/mod.rs rename mmr/src/{actors/builders => }/builder.rs (76%) create mode 100644 mmr/src/cmp/mod.rs rename mmr/src/{cmps/nodes/node.rs => cmp/nodes.rs} (100%) create mode 100644 mmr/src/cmp/payloads.rs rename mmr/src/{cmps/positions => cmp}/position.rs (71%) delete mode 100644 mmr/src/cmps/mod.rs delete mode 100644 mmr/src/cmps/nodes/mod.rs delete mode 100644 mmr/src/cmps/payloads/mod.rs delete mode 100644 mmr/src/cmps/payloads/payload.rs delete mode 100644 mmr/src/cmps/positions/mod.rs delete mode 100644 mmr/src/core/mod.rs delete mode 100644 mmr/src/data/mod.rs delete mode 100644 mmr/src/data/stores/store.rs rename mmr/src/{core => }/mmr.rs (92%) create mode 100644 mmr/src/primitives.rs rename mmr/src/{data => }/proofs/mod.rs (100%) rename mmr/src/{data => }/stores/mod.rs (100%) create mode 100644 mmr/src/stores/store.rs rename mmr/src/{core => }/utils.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 736a988..51771f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/scattered-systems/algae" version = "0.1.19" [workspace.dependencies] -decanter = { features = ["derive", "wasm"], version = "0.1.3" } +decanter = { features = ["derive", "wasm"], git = "https://github.com/FL03/decanter", branch = "v0.1.5", version = "0.1.5" } anyhow = "1" itertools = "0.10" diff --git a/merkle/Cargo.toml b/merkle/Cargo.toml index c63a2ac..0e5c0e2 100644 --- a/merkle/Cargo.toml +++ b/merkle/Cargo.toml @@ -25,12 +25,11 @@ crate-type = ["cdylib", "rlib"] test = true [dependencies] -hex = "0.4" -ring = { features = ["wasm32_c"], version = "0.16" } - anyhow.workspace = true decanter.workspace = true +hex = "0.4" itertools.workspace = true +ring = { features = ["wasm32_c"], version = "0.16" } serde.workspace = true serde_json.workspace = true smart-default.workspace = true diff --git a/merkle/src/layers.rs b/merkle/src/layers.rs index f5b090e..140be48 100644 --- a/merkle/src/layers.rs +++ b/merkle/src/layers.rs @@ -11,9 +11,14 @@ use std::string::ToString; // pub fn build_new_merkle_layer(left: MerkleNode, right: MerkleNode) #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct Layer(Vec>); +pub struct Layer(Vec>) +where + T: Default + ToString; -impl Layer { +impl Layer +where + T: Default + ToString, +{ pub fn new(data: Vec>) -> Self { let layer = data.into_iter().batching(|it| match it.next() { Some(l) => match it.next() { @@ -30,13 +35,19 @@ impl Layer { } } -impl std::convert::From>> for Layer { +impl From>> for Layer +where + T: Default + ToString, +{ fn from(data: Vec>) -> Self { Self::new(data) } } -impl std::convert::From<(Node, Node)> for Layer { +impl From<(Node, Node)> for Layer +where + T: Default + ToString, +{ fn from(data: (Node, Node)) -> Self { Self::new(vec![data.0, data.1]) } diff --git a/merkle/src/lib.rs b/merkle/src/lib.rs index caf9fcc..91d8661 100644 --- a/merkle/src/lib.rs +++ b/merkle/src/lib.rs @@ -13,11 +13,11 @@ pub(crate) mod nodes; pub(crate) mod payloads; pub(crate) mod shape; pub(crate) mod tree; -pub(crate) mod utils; +mod utils; pub mod proofs; -use decanter::prelude::{Hashable, H256}; +use decanter::prelude::H256; use proofs::merkle_proof; pub trait MerkleTreeSpec: Clone { diff --git a/merkle/src/nodes.rs b/merkle/src/nodes.rs index 7ae9ef9..ab5c7a0 100644 --- a/merkle/src/nodes.rs +++ b/merkle/src/nodes.rs @@ -8,25 +8,37 @@ use decanter::prelude::{Hashable, H256}; use serde::{Deserialize, Serialize}; use std::string::ToString; -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct Node { +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Node +where + T: Default + ToString, +{ pub data: Payload, pub hash: H256, } -impl Node { +impl Node +where + T: Default + ToString, +{ pub fn new(data: Payload, hash: H256) -> Self { Self { data, hash } } } -impl Hashable for Node { +impl Hashable for Node +where + T: Default + ToString, +{ fn hash(&self) -> H256 { - merkle_hash(&self.data) + merkle_hash(self.data.to_string()) } } -impl std::convert::From<(Node, Node)> for Node { +impl From<(Node, Node)> for Node +where + T: Default + ToString, +{ fn from(data: (Node, Node)) -> Self { let hash = merkle_hash(combine(&data.0.hash, &data.1.hash)); let data = Payload::Node(Box::new(data.0), Box::new(data.1)); @@ -34,17 +46,15 @@ impl std::convert::From<(Node, Node)> for Node { } } -impl std::convert::From for Node { - fn from(data: T) -> Self { - Self::new(Payload::from(data.clone()), merkle_hash(data)) - } -} - impl std::fmt::Display for Node where - T: Serialize + ToString, + T: Default + ToString, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) + let msg = serde_json::json!({ + "data": self.data.to_string(), + "hash": self.hash, + }); + write!(f, "{}", msg) } } diff --git a/merkle/src/payloads.rs b/merkle/src/payloads.rs index c59ef45..ff5f254 100644 --- a/merkle/src/payloads.rs +++ b/merkle/src/payloads.rs @@ -4,47 +4,73 @@ Description: ... Summary ... */ use crate::Node; +use decanter::prelude::Hashable; use serde::{Deserialize, Serialize}; +use smart_default::SmartDefault; use std::string::ToString; +use strum::Display; -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub enum Payload { +#[derive( + Clone, + Debug, + Deserialize, + Display, + Eq, + Hash, + Hashable, + Ord, + PartialEq, + PartialOrd, + Serialize, + SmartDefault, +)] +pub enum Payload +where + T: Default + ToString, +{ + #[default] Leaf(T), Node(Box>, Box>), } impl Payload where - T: ToString, + T: Default + ToString, { - pub fn new_leaf(data: T) -> Self { + pub fn leaf(data: T) -> Self { Self::Leaf(data) } - pub fn new_node(left: Box>, right: Box>) -> Self { + pub fn node(left: Box>, right: Box>) -> Self { Self::Node(left, right) } + pub fn is_leaf(&self) -> bool { + match self { + Self::Leaf(_) => true, + _ => false, + } + } + pub fn is_node(&self) -> bool { + match self { + Self::Node(_, _) => true, + _ => false, + } + } } -impl std::fmt::Display for Payload +impl From for Payload where - T: Serialize + ToString, + T: Default + ToString, { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) - } -} - -impl std::convert::From for Payload { fn from(data: T) -> Self { Self::Leaf(data) } } -impl std::convert::From<(Box>, Box>)> for Payload +impl From<(Box>, Box>)> for Payload where - T: ToString, + T: Default + ToString, { fn from(data: (Box>, Box>)) -> Self { - Self::new_node(data.0, data.1) + Self::node(data.0, data.1) } } diff --git a/merkle/src/utils.rs b/merkle/src/utils.rs index 2321ddf..e00de39 100644 --- a/merkle/src/utils.rs +++ b/merkle/src/utils.rs @@ -12,7 +12,7 @@ use serde::Serialize; pub fn add_hash(a: &H256, b: &H256) -> H256 { let c = [a.as_ref(), b.as_ref()].concat(); let combined = ring::digest::digest(&ring::digest::SHA256, &c); - ::from(combined) + hasher(combined).into() } /// Merges two hashes into a string @@ -54,12 +54,9 @@ where } /// Takes the hash of the given information to the second degree -pub fn merkle_hash(data: T) -> H256 { - let res: H256 = { - let tmp: H256 = hasher(&data).as_slice().to_owned().into(); - hasher(&tmp).as_slice().to_owned().into() - }; - res +pub fn merkle_hash(data: impl AsRef<[u8]>) -> H256 { + let tmp: H256 = hasher(data).into(); + hasher(&tmp).into() } /// Verify that the datum hash with a vector of proofs will produce the Merkle root. Also need the diff --git a/mmr/Cargo.toml b/mmr/Cargo.toml index d0459d4..5796c33 100644 --- a/mmr/Cargo.toml +++ b/mmr/Cargo.toml @@ -26,17 +26,15 @@ test = true [dependencies] anyhow.workspace = true decanter.workspace = true +digest = "0.10" +hex = "0.4" itertools.workspace = true serde.workspace = true serde_json.workspace = true smart-default.workspace = true strum.workspace = true -digest = "0.10" -hex = "0.4" - [dev-dependencies] -scsys = { features = [], version = "0.1.41" } hex-literal = "0.3.4" vrf = "0.2.4" diff --git a/mmr/src/actors/builders/mod.rs b/mmr/src/actors/builders/mod.rs deleted file mode 100644 index df7d0fb..0000000 --- a/mmr/src/actors/builders/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - Appellation: builders - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::builder::*; - -pub(crate) mod builder; diff --git a/mmr/src/actors/mod.rs b/mmr/src/actors/mod.rs deleted file mode 100644 index 4d5a844..0000000 --- a/mmr/src/actors/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* - Appellation: actors - Contrib: FL03 - Description: ... summary ... -*/ - -pub mod builders; diff --git a/mmr/src/actors/builders/builder.rs b/mmr/src/builder.rs similarity index 76% rename from mmr/src/actors/builders/builder.rs rename to mmr/src/builder.rs index 0bc3970..c5da5c5 100644 --- a/mmr/src/actors/builders/builder.rs +++ b/mmr/src/builder.rs @@ -6,6 +6,9 @@ use crate::MerkleMountainRange; use decanter::prelude::Hashable; -pub struct MerkleMountainRangeBuilder { +pub struct MerkleMountainRangeBuilder +where + T: ToString, +{ pub mmr: MerkleMountainRange, } diff --git a/mmr/src/cmp/mod.rs b/mmr/src/cmp/mod.rs new file mode 100644 index 0000000..539a0b1 --- /dev/null +++ b/mmr/src/cmp/mod.rs @@ -0,0 +1,10 @@ +/* + Appellation: cmp + Contrib: FL03 + Description: This module implements a host of components for building a Merkle Mountain Range. +*/ +pub use self::{nodes::*, payloads::*, position::*}; + +mod nodes; +mod payloads; +mod position; diff --git a/mmr/src/cmps/nodes/node.rs b/mmr/src/cmp/nodes.rs similarity index 100% rename from mmr/src/cmps/nodes/node.rs rename to mmr/src/cmp/nodes.rs diff --git a/mmr/src/cmp/payloads.rs b/mmr/src/cmp/payloads.rs new file mode 100644 index 0000000..5870b0c --- /dev/null +++ b/mmr/src/cmp/payloads.rs @@ -0,0 +1,53 @@ +/* + Appellation: rangemap + Contrib: FL03 + Description: ... summary ... +*/ +use decanter::prelude::{Hashable, H256}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Clone, Debug, Default, Deserialize, Eq, Hashable, PartialEq, Serialize)] +pub struct Payload +where + T: ToString, +{ + id: H256, + data: HashMap, +} + +impl Payload +where + T: ToString, +{ + pub fn new() -> Self { + Self { + id: H256::generate(), + data: HashMap::new(), + } + } + pub fn data(&self) -> &HashMap { + &self.data + } +} + +impl From> for Payload +where + T: ToString, +{ + fn from(data: HashMap) -> Self { + Self { + id: H256::generate(), + data, + } + } +} + +impl std::fmt::Display for Payload +where + T: ToString, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.id) + } +} diff --git a/mmr/src/cmps/positions/position.rs b/mmr/src/cmp/position.rs similarity index 71% rename from mmr/src/cmps/positions/position.rs rename to mmr/src/cmp/position.rs index 9f629b0..dc34777 100644 --- a/mmr/src/cmps/positions/position.rs +++ b/mmr/src/cmp/position.rs @@ -3,10 +3,12 @@ Contrib: FL03 Description: ... summary ... */ -use decanter::prelude::{hasher, Hashable, H256}; +use decanter::prelude::Hashable; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[derive( + Clone, Debug, Default, Deserialize, Eq, Hash, Hashable, Ord, PartialEq, PartialOrd, Serialize, +)] pub struct Position { pub height: usize, pub index: usize, @@ -16,6 +18,12 @@ impl Position { pub fn new(height: usize, index: usize) -> Self { Self { height, index } } + pub fn height(&self) -> usize { + self.height + } + pub fn index(&self) -> usize { + self.index + } } impl From<(usize, usize)> for Position { @@ -24,12 +32,6 @@ impl From<(usize, usize)> for Position { } } -impl Hashable for Position { - fn hash(&self) -> H256 { - hasher(&self).as_slice().to_owned().into() - } -} - impl std::fmt::Display for Position { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", serde_json::to_string(&self).unwrap()) diff --git a/mmr/src/cmps/mod.rs b/mmr/src/cmps/mod.rs deleted file mode 100644 index 9d07b0f..0000000 --- a/mmr/src/cmps/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -/* - Appellation: cmps - Contrib: FL03 - Description: -*/ - -pub mod nodes; -pub mod payloads; -pub mod positions; diff --git a/mmr/src/cmps/nodes/mod.rs b/mmr/src/cmps/nodes/mod.rs deleted file mode 100644 index 8cc5492..0000000 --- a/mmr/src/cmps/nodes/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - Appellation: nodes - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::node::*; - -pub(crate) mod node; diff --git a/mmr/src/cmps/payloads/mod.rs b/mmr/src/cmps/payloads/mod.rs deleted file mode 100644 index ee44695..0000000 --- a/mmr/src/cmps/payloads/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* -Appellation: stores -Contrib: FL03 -Description: ... summary ... -*/ -pub use self::payload::*; - -pub(crate) mod payload; diff --git a/mmr/src/cmps/payloads/payload.rs b/mmr/src/cmps/payloads/payload.rs deleted file mode 100644 index 729a2f9..0000000 --- a/mmr/src/cmps/payloads/payload.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - Appellation: rangemap - Contrib: FL03 - Description: ... summary ... -*/ -use decanter::prelude::{hasher, Hashable, H256}; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, convert::From}; - -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct RangeMap(pub HashMap); - -impl RangeMap { - pub fn data(&self) -> &HashMap { - &self.0 - } -} - -impl From> for RangeMap { - fn from(data: HashMap) -> Self { - Self(data) - } -} - -impl Hashable for RangeMap { - fn hash(&self) -> H256 { - hasher(&self).as_slice().to_owned().into() - } -} - -impl std::fmt::Display for RangeMap { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) - } -} diff --git a/mmr/src/cmps/positions/mod.rs b/mmr/src/cmps/positions/mod.rs deleted file mode 100644 index df3172c..0000000 --- a/mmr/src/cmps/positions/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - Appellation: positions - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::position::*; - -pub(crate) mod position; diff --git a/mmr/src/core/mod.rs b/mmr/src/core/mod.rs deleted file mode 100644 index 10b1442..0000000 --- a/mmr/src/core/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -/* - Appellation: core - Contrib: FL03 - Description: -*/ -pub use self::{mmr::*, primitives::*, utils::*}; - -pub(crate) mod mmr; -pub(crate) mod utils; - -pub(crate) mod primitives {} diff --git a/mmr/src/data/mod.rs b/mmr/src/data/mod.rs deleted file mode 100644 index 2281558..0000000 --- a/mmr/src/data/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - Appellation: data - Contrib: FL03 - Description: ... summary ... -*/ - -pub mod proofs; -pub mod stores; diff --git a/mmr/src/data/stores/store.rs b/mmr/src/data/stores/store.rs deleted file mode 100644 index c85658a..0000000 --- a/mmr/src/data/stores/store.rs +++ /dev/null @@ -1,76 +0,0 @@ -/* - Appellation: store - Contrib: FL03 - Description: ... summary ... -*/ -use decanter::prelude::{hasher, Hashable, H256}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct MMRStore(pub T); - -impl MMRStore { - pub fn new(data: T) -> Self { - Self(data) - } - pub fn data(&self) -> &T { - &self.0 - } -} - -impl Hashable for MMRStore -where - T: Serialize, -{ - fn hash(&self) -> H256 { - hasher(&self).as_slice().to_owned().into() - } -} - -impl std::convert::From<&MMRStore> for MMRStore -where - T: Clone, -{ - fn from(data: &MMRStore) -> Self { - Self(data.clone().0) - } -} - -impl std::convert::From for MMRStore { - fn from(data: T) -> Self { - Self(data) - } -} - -impl std::fmt::Display for MMRStore -where - T: Serialize, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use scsys::gen::generate_random_string; - use std::ops::Range; - #[test] - fn test_store_default() { - let a = MMRStore::::default(); - let b = MMRStore::::new(Default::default()); - assert_eq!(a, b) - } - - #[test] - fn test_store_hash() { - let data = Range { start: 0, end: 12 } - .map(|_| generate_random_string(12)) - .collect::>(); - - let a = MMRStore::>::from(data); - let b = MMRStore::from(&a); - assert_eq!(a, b) - } -} diff --git a/mmr/src/lib.rs b/mmr/src/lib.rs index 860c83c..ebdefe7 100644 --- a/mmr/src/lib.rs +++ b/mmr/src/lib.rs @@ -3,9 +3,13 @@ Contrib: FL03 Description: */ -pub use self::{actors::*, cmps::*, core::*, data::*}; +pub use self::{builder::*, mmr::*, primitives::*, utils::*}; -pub(crate) mod actors; -pub(crate) mod cmps; -pub(crate) mod core; -pub(crate) mod data; +mod builder; +mod mmr; +mod primitives; +mod utils; + +pub mod cmp; +pub mod proofs; +pub mod stores; diff --git a/mmr/src/core/mmr.rs b/mmr/src/mmr.rs similarity index 92% rename from mmr/src/core/mmr.rs rename to mmr/src/mmr.rs index bd3f71a..2fc27a2 100644 --- a/mmr/src/core/mmr.rs +++ b/mmr/src/mmr.rs @@ -3,22 +3,27 @@ Contrib: FL03 Description: ... summary ... */ -use crate::{ - is_node_right, nodes::MerkleNode, payloads::RangeMap, positions::Position, sibling_index, -}; +use crate::cmp::{MerkleNode, Position}; +use crate::{is_node_right, sibling_index, RangeMap}; use decanter::prelude::{hasher, Hashable, H256}; use digest::Digest; use serde::{Deserialize, Serialize}; use std::convert::From; -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct MerkleMountainRange { +#[derive(Clone, Debug, Default, Deserialize, Eq, Hashable, PartialEq, Serialize)] +pub struct MerkleMountainRange +where + T: ToString, +{ data: RangeMap, mmr: Vec, // todo convert these to a bitmap position: Position, } -impl MerkleMountainRange { +impl MerkleMountainRange +where + T: ToString, +{ pub fn new(mmr: Vec, data: RangeMap, position: Position) -> Self { Self { mmr, @@ -26,17 +31,81 @@ impl MerkleMountainRange { position, } } + /// This function adds a vec of leaf nodes to the mmr. + pub fn add_vec(&mut self, objects: Vec) { + for object in objects { + self.add_single::(object); + } + } + /// This function adds a new leaf node to the mmr. + pub fn add_single(&mut self, object: T) { + let node_hash: H256 = hasher(object.to_string()).into(); + let node = MerkleNode::from(node_hash); + self.data.insert(node_hash, object); + self.mmr.push(node); + if is_node_right(self.get_last_added_index()) { + self.add_single_no_leaf::(self.get_last_added_index()) + } + } + // This function adds non leaf nodes, eg nodes that are not directly a hash of data + // This is iterative and will continue to up and till it hits the top, will be a future left child + fn add_single_no_leaf(&mut self, index: usize) { + let mut hasher = D::new(); + hasher.update(self.mmr[sibling_index(index)].hash); + hasher.update(self.mmr[index].hash); + let new_hash: H256 = hasher.finalize().to_vec().into(); + let new_node = MerkleNode::from(new_hash); + self.mmr.push(new_node); + if is_node_right(self.get_last_added_index()) { + self.add_single_no_leaf::(self.get_last_added_index()) + } else { + self.position = self.calc_peak_height().into(); // because we have now stopped adding right nodes, we need to update the height of the mmr + } + } + fn bag_mmr(&self) -> Vec { + // lets find all peaks of the mmr + let mut peaks = Vec::new(); + self.find_bagging_indexes(self.position.height as i64, self.position.index, &mut peaks); + peaks + } + // This function calculates the peak height of the mmr + fn calc_peak_height(&self) -> (usize, usize) { + let mut height_counter = 0; + let mmr_len = self.get_last_added_index(); + let mut index: usize = (1 << (height_counter + 2)) - 2; + let mut actual_height_index = 0; + while mmr_len >= index { + // find the height of the tree by finding if we can subtract the height +1 + height_counter += 1; + actual_height_index = index; + index = (1 << (height_counter + 2)) - 2; + } + (height_counter, actual_height_index) + } + fn find_bagging_indexes(&self, mut height: i64, index: usize, peaks: &mut Vec) { + let mut new_index = index + (1 << (height + 1)) - 1; // go the potential right sibling + while (new_index > self.get_last_added_index()) && (height > 0) { + // lets go down left child till we hit a valid node or we reach height 0 + new_index -= 1 << height; + height -= 1; + } + if (new_index <= self.get_last_added_index()) && (height >= 0) { + // is this a valid peak which needs to be bagged + peaks.push(self.mmr[new_index].hash); + self.find_bagging_indexes(height, new_index, peaks); // lets go look for more peaks + } + } /// This function returns a reference to the data stored in the mmr /// It will return none if the hash does not exist pub fn get_object(&self, hash: &H256) -> Option<&T> { - self.data.0.get(hash) + self.data.get(hash) } /// This function returns a mut reference to the data stored in the MMR /// It will return none if the hash does not exist pub fn get_mut_object(&mut self, hash: &H256) -> Option<&mut T> { - self.data.0.get_mut(hash) + self.data.get_mut(hash) } pub fn get_hash(&self, index: usize) -> Option { @@ -128,43 +197,10 @@ impl MerkleMountainRange { } self.get_ordered_hash_proof(next_index, results); } - - /// This function will verify the provided proof. Internally it uses the get_hash_proof function to construct a - /// similar proof. This function will return true if the proof is valid - /// If the order does not match Lchild-Rchild-parent(Lchild)-Rchild-parent-.. the validation will fail - /// This function will only succeed if the given hash is of height 0 - pub fn verify_proof(&self, hashes: &Vec) -> bool { - if hashes.is_empty() { - return false; - } - if self.get_object(&hashes[0]).is_none() && self.get_object(&hashes[1]).is_none() { - // we only want to search for valid object's proofs, either 0 or 1 must be a valid object - return false; - } - let proof = self.get_hash_proof::(&hashes[0]); - hashes.eq(&proof) - } - - // This function calculates the peak height of the mmr - fn calc_peak_height(&self) -> (usize, usize) { - let mut height_counter = 0; - let mmr_len = self.get_last_added_index(); - let mut index: usize = (1 << (height_counter + 2)) - 2; - let mut actual_height_index = 0; - while mmr_len >= index { - // find the height of the tree by finding if we can subtract the height +1 - height_counter += 1; - actual_height_index = index; - index = (1 << (height_counter + 2)) - 2; - } - (height_counter, actual_height_index) - } - /// This function returns the peak height of the mmr pub fn get_peak_height(&self) -> usize { self.position.height } - /// This function will return the single merkle root of the MMR. pub fn get_merkle_root(&self) -> H256 { let mut peaks = self.bag_mmr(); @@ -187,76 +223,32 @@ impl MerkleMountainRange { // there was no other peaks, return the highest peak self.mmr[self.position.index].hash } - - /// This function adds a vec of leaf nodes to the mmr. - pub fn add_vec(&mut self, objects: Vec) { - for object in objects { - self.add_single::(object); - } - } - - /// This function adds a new leaf node to the mmr. - pub fn add_single(&mut self, object: T) { - let node_hash = object.hash(); - let node = MerkleNode::from(node_hash); - self.data.0.insert(node_hash, object); - self.mmr.push(node); - if is_node_right(self.get_last_added_index()) { - self.add_single_no_leaf::(self.get_last_added_index()) - } - } - - // This function adds non leaf nodes, eg nodes that are not directly a hash of data - // This is iterative and will continue to up and till it hits the top, will be a future left child - fn add_single_no_leaf(&mut self, index: usize) { - let mut hasher = D::new(); - hasher.update(self.mmr[sibling_index(index)].hash); - hasher.update(self.mmr[index].hash); - let new_hash: H256 = hasher.finalize().to_vec().into(); - let new_node = MerkleNode::from(new_hash); - self.mmr.push(new_node); - if is_node_right(self.get_last_added_index()) { - self.add_single_no_leaf::(self.get_last_added_index()) - } else { - self.position = self.calc_peak_height().into(); // because we have now stopped adding right nodes, we need to update the height of the mmr - } - } - // This function is just a private function to return the index of the last added node fn get_last_added_index(&self) -> usize { self.mmr.len() - 1 } - - fn bag_mmr(&self) -> Vec { - // lets find all peaks of the mmr - let mut peaks = Vec::new(); - self.find_bagging_indexes(self.position.height as i64, self.position.index, &mut peaks); - peaks - } - - fn find_bagging_indexes(&self, mut height: i64, index: usize, peaks: &mut Vec) { - let mut new_index = index + (1 << (height + 1)) - 1; // go the potential right sibling - while (new_index > self.get_last_added_index()) && (height > 0) { - // lets go down left child till we hit a valid node or we reach height 0 - new_index -= 1 << height; - height -= 1; + /// This function will verify the provided proof. Internally it uses the get_hash_proof function to construct a + /// similar proof. This function will return true if the proof is valid + /// If the order does not match Lchild-Rchild-parent(Lchild)-Rchild-parent-.. the validation will fail + /// This function will only succeed if the given hash is of height 0 + pub fn verify_proof(&self, hashes: &Vec) -> bool { + if hashes.is_empty() { + return false; } - if (new_index <= self.get_last_added_index()) && (height >= 0) { - // is this a valid peak which needs to be bagged - peaks.push(self.mmr[new_index].hash); - self.find_bagging_indexes(height, new_index, peaks); // lets go look for more peaks + if self.get_object(&hashes[0]).is_none() && self.get_object(&hashes[1]).is_none() { + // we only want to search for valid object's proofs, either 0 or 1 must be a valid object + return false; } + let proof = self.get_hash_proof::(&hashes[0]); + hashes.eq(&proof) } } -impl Hashable for MerkleMountainRange { - fn hash(&self) -> H256 { - hasher(&self).as_slice().to_owned().into() - } -} - -impl std::fmt::Display for MerkleMountainRange { +impl std::fmt::Display for MerkleMountainRange +where + T: ToString, +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) + write!(f, "{:?}", self.mmr) } } diff --git a/mmr/src/primitives.rs b/mmr/src/primitives.rs new file mode 100644 index 0000000..b73fd15 --- /dev/null +++ b/mmr/src/primitives.rs @@ -0,0 +1,17 @@ +/* + Appellation: primitives + Contrib: FL03 + Description: ... summary ... +*/ +pub use self::{constants::*, statics::*, types::*}; + +mod constants {} + +mod statics {} + +mod types { + use decanter::prelude::H256; + use std::collections::HashMap; + + pub type RangeMap = HashMap; +} diff --git a/mmr/src/data/proofs/mod.rs b/mmr/src/proofs/mod.rs similarity index 100% rename from mmr/src/data/proofs/mod.rs rename to mmr/src/proofs/mod.rs diff --git a/mmr/src/data/stores/mod.rs b/mmr/src/stores/mod.rs similarity index 100% rename from mmr/src/data/stores/mod.rs rename to mmr/src/stores/mod.rs diff --git a/mmr/src/stores/store.rs b/mmr/src/stores/store.rs new file mode 100644 index 0000000..0302ec9 --- /dev/null +++ b/mmr/src/stores/store.rs @@ -0,0 +1,65 @@ +/* + Appellation: store + Contrib: FL03 + Description: ... summary ... +*/ +use decanter::prelude::{Hashable, H256}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Hashable, PartialEq, Serialize)] +pub struct MMRStore +where + T: ToString, +{ + id: H256, + data: T, +} + +impl MMRStore +where + T: ToString, +{ + pub fn new(data: T) -> Self { + Self { + id: H256::generate(), + data, + } + } + pub fn data(&self) -> &T { + &self.data + } +} + +impl From for MMRStore +where + T: ToString, +{ + fn from(data: T) -> Self { + Self::new(data) + } +} + +impl std::fmt::Display for MMRStore +where + T: ToString, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let msg = serde_json::json!({ + "id": self.id, + "data": self.data.to_string(), + }); + write!(f, "{}", msg) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_store_default() { + let a = MMRStore::::default(); + let b = MMRStore::::new(Default::default()); + assert_eq!(a, b) + } +} diff --git a/mmr/src/core/utils.rs b/mmr/src/utils.rs similarity index 100% rename from mmr/src/core/utils.rs rename to mmr/src/utils.rs