From 4391d2a070f932c49fb194a76fb6069c3bc9eb24 Mon Sep 17 00:00:00 2001 From: FL03 Date: Wed, 25 Jan 2023 11:52:28 -0600 Subject: [PATCH 1/9] Update Signed-off-by: FL03 --- .artifacts/license/APACHE.LICENSE | 2 +- .artifacts/license/MIT.LICENSE | 2 +- .github/dependabot.yml | 4 + .gitpod.yml | 4 +- Cargo.toml | 18 ++ LICENSE | 4 +- README.md | 24 ++- algae/Cargo.toml | 64 +++--- algae/README.md | 10 - algae/tests/default.rs | 11 +- algae/tests/graphs.rs | 185 +++++++++--------- algae/tests/merkle.rs | 94 +++++++++ graph/Cargo.toml | 16 +- graph/README.md | 0 graph/src/components/mod.rs | 8 - graph/src/core/mod.rs | 6 - graph/src/core/specs/mod.rs | 8 - graph/src/{components => }/directed/graph.rs | 0 graph/src/{components => }/directed/mod.rs | 0 graph/src/errors.rs | 22 +++ graph/src/{core/specs => }/graphable.rs | 6 +- graph/src/lib.rs | 12 +- .../{core/primitives/nullnode.rs => nodes.rs} | 11 +- .../{core/primitives/mod.rs => primitives.rs} | 4 +- .../src/{components => }/undirected/graph.rs | 0 graph/src/{components => }/undirected/mod.rs | 0 graph/src/utils.rs | 5 + merkle/Cargo.toml | 22 ++- merkle/README.md | 19 -- merkle/src/lib.rs | 3 +- merkle/src/primitives.rs | 5 - merkle/src/{utils/misc.rs => utils.rs} | 24 ++- merkle/src/utils/mod.rs | 9 - merkle/src/utils/validate.rs | 28 --- mmr/Cargo.toml | 33 ++-- mmr/README.md | 19 -- scripts/publish.sh | 5 - scripts/rustup.sh | 3 + scripts/setup.sh | 4 - 39 files changed, 389 insertions(+), 305 deletions(-) delete mode 100644 algae/README.md create mode 100644 algae/tests/merkle.rs delete mode 100644 graph/README.md delete mode 100644 graph/src/components/mod.rs delete mode 100644 graph/src/core/mod.rs delete mode 100644 graph/src/core/specs/mod.rs rename graph/src/{components => }/directed/graph.rs (100%) rename graph/src/{components => }/directed/mod.rs (100%) create mode 100644 graph/src/errors.rs rename graph/src/{core/specs => }/graphable.rs (93%) rename graph/src/{core/primitives/nullnode.rs => nodes.rs} (60%) rename graph/src/{core/primitives/mod.rs => primitives.rs} (77%) rename graph/src/{components => }/undirected/graph.rs (100%) rename graph/src/{components => }/undirected/mod.rs (100%) create mode 100644 graph/src/utils.rs delete mode 100644 merkle/README.md delete mode 100644 merkle/src/primitives.rs rename merkle/src/{utils/misc.rs => utils.rs} (53%) delete mode 100644 merkle/src/utils/mod.rs delete mode 100644 merkle/src/utils/validate.rs delete mode 100644 mmr/README.md delete mode 100644 scripts/publish.sh create mode 100644 scripts/rustup.sh diff --git a/.artifacts/license/APACHE.LICENSE b/.artifacts/license/APACHE.LICENSE index 94cb917..b9f3f83 100644 --- a/.artifacts/license/APACHE.LICENSE +++ b/.artifacts/license/APACHE.LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2022 Scattered-Systems + Copyright 2023 Scattered-Systems, DAO LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/.artifacts/license/MIT.LICENSE b/.artifacts/license/MIT.LICENSE index ff3191a..a72851f 100644 --- a/.artifacts/license/MIT.LICENSE +++ b/.artifacts/license/MIT.LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2022 Joe McCain III +Copyright (c) 2023 Scattered-Systems, DAO LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 59ea3db..173af4d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,5 +1,9 @@ version: 2 updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: daily - package-ecosystem: github-actions directory: / schedule: diff --git a/.gitpod.yml b/.gitpod.yml index 7e55747..1c0938e 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,6 +1,8 @@ tasks: - init: | - J + sudo update -y && sudo upgrade -y && sudo autoremove -y + rustup default nightly + rustup target add wasm32-unknown-unknown wasm32-wasi --toolchain nightly cargo build --release --target wasm32-unknown-unknown --workspace command: cargo watch -x test --all diff --git a/Cargo.toml b/Cargo.toml index 33188c8..39cad12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,21 @@ +[workspace.package] +authors = ["FL03 (https://gitlab.com/FL03)", "Scattered-Systems (https://gitlab.com/scsys)"] +categories = [] +description = "Algae is a collection of core algorithms and data-structures, written in Rust" +edition = "2021" +homepage = "https://github.com/scattered-systems/algae/wiki" +keywords = ["algorithms", "data-structures"] +license = "Apache-2.0" +readme = "README.md" +repository = "https://github.com/scattered-systems/algae" +version = "0.1.18" + +[workspace.dependencies] +decanter = { features = ["derive", "wasm"], version = "0.1.3" } +scsys = { features = [], version = "0.1.41" } +serde = { features = ["derive"], version = "1" } +serde_json = "1" + [workspace] default-members = [ "algae" diff --git a/LICENSE b/LICENSE index 94cb917..f25b2b1 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2022 Scattered-Systems + Copyright 2023 Joe McCain III Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/README.md b/README.md index 4c6b246..58fd565 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,32 @@ # algae -[![Rust Clippy](https://github.com/scattered-systems/algae/actions/workflows/rust-clippy.yml/badge.svg)](https://github.com/scattered-systems/algae/actions/workflows/rust-clippy.yml) -[![Rust](https://github.com/scattered-systems/algae/actions/workflows/rust.yml/badge.svg)](https://github.com/scattered-systems/algae/actions/workflows/rust.yml) +[![crates.io](https://img.shields.io/crates/v/algae.svg)](https://crates.io/crates/algae) +[![docs.rs](https://docs.rs/algae/badge.svg)](https://docs.rs/algae) +[![Clippy](https://github.com/FL03/algae/actions/workflows/rust-clippy.yml/badge.svg)](https://github.com/FL03/algae/actions/workflows/rust-clippy.yml) +[![Rust](https://github.comFL03/algae/actions/workflows/rust.yml/badge.svg)](https://github.com/FL03/algae/actions/workflows/rust.yml) + +*** + +Welcome to algae, a collection of optimized data-structures and algorithms intended for use within blockchain environments. ## Developers ### Getting Started - git clone https://github.com/scattered-systems/algae + git clone https://github.com/FL03/algae #### Testing cargo build --release cargo test --all-features --release -### Resources +## Contributors + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. -* [Crate.io](https://crates.io/crates/algae) -* [Docs](https://docs.rs/algae) +## License -#### _References_ \ No newline at end of file +- [Apache-2.0](https://choosealicense.com/licenses/apache-2.0/) +- [MIT](https://choosealicense.com/licenses/mit/) diff --git a/algae/Cargo.toml b/algae/Cargo.toml index bb0ece4..bd4624c 100644 --- a/algae/Cargo.toml +++ b/algae/Cargo.toml @@ -1,51 +1,55 @@ [package] -authors = ["FL03 (https://gitlab.com/FL03)", "Scattered-Systems (https://gitlab.com/scsys)"] +authors.workspace = true categories = [] -description = "Algae is a collection of core algorithms and data-structures, written in Rust" -edition = "2021" -homepage = "https://github.com/scattered-systems/algae/wiki" +description.workspace = true +edition.workspace = true +homepage.workspace = true keywords = ["algorithms", "data-structures"] -license = "Apache-2.0" +license.workspace = true name = "algae" -readme = "README.md" -repository = "https://github.com/scattered-systems/algae" -version = "0.1.17" # TODO - Update the cargo package version +readme.workspace = true +repository.workspace = true +version.workspace = true [features] -default = ["core", "graph", "merkle", "mmr"] -full = ["core", "graph", "merkle", "mmr"] +default = ["core"] + +core = [ + "merkle", + "mmr", + "graph" +] -core = ["merkle", "mmr"] graph = ["algae-graph"] merkle = ["algae-merkle"] mmr = ["algae-mmr"] +wasm = [] + [lib] bench = false crate-type = ["cdylib", "rlib"] test = true -[dependencies] -scsys = { features = ["full"], version = "0.1.40" } -serde = { features = ["derive"], version = "1" } -serde_json = "1" - -[dependencies.algae-graph] -optional = true -path = "../graph" -version = "0.1.17" - -[dependencies.algae-merkle] -optional = true -path = "../merkle" -version = "0.1.17" - -[dependencies.algae-mmr] -optional = true -path = "../mmr" -version = "0.1.17" +[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" } +scsys.workspace = true +serde.workspace = true +serde_json.workspace = true + +[dev-dependencies] +decanter = { features = ["derive"], version = "0.1.3" } +hex-literal = "0.3" +vrf = "0.2" [package.metadata.docs.rs] all-features = true rustc-args = ["--cfg", "docsrs"] + +[target.wasm32-unknown-unknown] + +[target.wasm32-wasi] diff --git a/algae/README.md b/algae/README.md deleted file mode 100644 index 5b0d98a..0000000 --- a/algae/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Algae - -Algae boasts a collection of useful algorithms and data-structures - -## Getting Started - - git clone https://github.com/scattered-systems/algae.git - cargo test --all-features - -### Examples \ No newline at end of file diff --git a/algae/tests/default.rs b/algae/tests/default.rs index 827ee40..7dee5fa 100644 --- a/algae/tests/default.rs +++ b/algae/tests/default.rs @@ -1,9 +1,6 @@ #[cfg(test)] -mod tests { - - #[test] - fn it_compiles() { - let f = |i| i + 1; - assert_eq!(f(10), 11) - } +#[test] +fn lib_compiles() { + let f = |i| i + 1; + assert_eq!(f(10), 11) } diff --git a/algae/tests/graphs.rs b/algae/tests/graphs.rs index 9e11cc9..76525d3 100644 --- a/algae/tests/graphs.rs +++ b/algae/tests/graphs.rs @@ -1,107 +1,108 @@ -#[cfg(feature = "graphs")] +#[cfg(feature = "graph")] #[cfg(test)] -mod tests { - mod directed_graph_tests { - use algae::graphs::{directed::DirectedGraph, Graphable}; - - #[test] - fn test_add_node() { - let tmp = ["a", "b", "c"] - .iter() - .cloned() - .map(String::from) - .collect::>(); - - let mut graph = DirectedGraph::default(); - graph.add_node("a"); - graph.add_node("b"); - graph.add_node("c"); - assert_eq!( - graph.nodes(), - [&tmp[0], &tmp[1], &tmp[2]].iter().cloned().collect() - ); - } +mod directed_graph_tests { + use algae::graph::{directed::DirectedGraph, Graphable}; - #[test] - fn test_add_edge() { - let mut graph = DirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("c", "a", 7)); - graph.add_edge(("b", "c", 10)); - - let expected_edges = [ - (&String::from("a"), &String::from("b"), 5), - (&String::from("c"), &String::from("a"), 7), - (&String::from("b"), &String::from("c"), 10), - ]; - for edge in expected_edges.iter() { - assert!(graph.edges().contains(edge)); - } - } + #[test] + fn test_add_node() { + let tmp = ["a", "b", "c"] + .iter() + .cloned() + .map(String::from) + .collect::>(); - #[test] - fn test_neighbours() { - let mut graph = DirectedGraph::default(); + let mut graph = DirectedGraph::default(); + graph.add_node("a"); + graph.add_node("b"); + graph.add_node("c"); + assert_eq!( + graph.nodes(), + [&tmp[0], &tmp[1], &tmp[2]].iter().cloned().collect() + ); + } - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); + #[test] + fn test_add_edge() { + let mut graph = DirectedGraph::default(); - assert_eq!( - graph.neighbours("a").unwrap(), - &vec![(String::from("b"), 5)] - ); - } + graph.add_edge(("a", "b", 5)); + graph.add_edge(("c", "a", 7)); + graph.add_edge(("b", "c", 10)); - #[test] - fn test_contains() { - let mut graph = DirectedGraph::default(); - graph.add_node("a"); - graph.add_node("b"); - graph.add_node("c"); - assert!(graph.contains("a")); - assert!(graph.contains("b")); - assert!(graph.contains("c")); - assert!(!graph.contains("d")); + let expected_edges = [ + (&String::from("a"), &String::from("b"), 5), + (&String::from("c"), &String::from("a"), 7), + (&String::from("b"), &String::from("c"), 10), + ]; + for edge in expected_edges.iter() { + assert!(graph.edges().contains(edge)); } } - mod undirected_graph_tests { - use algae::graphs::{undirected::UndirectedGraph, Graphable}; - #[test] - fn test_add_edge() { - let mut graph = UndirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); - - let expected_edges = [ - (&String::from("a"), &String::from("b"), 5), - (&String::from("b"), &String::from("a"), 5), - (&String::from("c"), &String::from("a"), 7), - (&String::from("a"), &String::from("c"), 7), - (&String::from("b"), &String::from("c"), 10), - (&String::from("c"), &String::from("b"), 10), - ]; - for edge in expected_edges.iter() { - assert!(graph.edges().contains(edge)); - } - } + #[test] + fn test_neighbours() { + let mut graph = DirectedGraph::default(); - #[test] - fn test_neighbours() { - let mut graph = UndirectedGraph::default(); + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); + assert_eq!( + graph.neighbours("a").unwrap(), + &vec![(String::from("b"), 5)] + ); + } + + #[test] + fn test_contains() { + let mut graph = DirectedGraph::default(); + graph.add_node("a"); + graph.add_node("b"); + graph.add_node("c"); + assert!(graph.contains("a")); + assert!(graph.contains("b")); + assert!(graph.contains("c")); + assert!(!graph.contains("d")); + } +} - assert_eq!( - graph.neighbours("a").unwrap(), - &vec![(String::from("b"), 5), (String::from("c"), 7)] - ); +#[cfg(feature = "graph")] +#[cfg(test)] +mod undirected_graph_tests { + use algae::graph::{undirected::UndirectedGraph, Graphable}; + + #[test] + fn test_add_edge() { + let mut graph = UndirectedGraph::default(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + let expected_edges = [ + (&String::from("a"), &String::from("b"), 5), + (&String::from("b"), &String::from("a"), 5), + (&String::from("c"), &String::from("a"), 7), + (&String::from("a"), &String::from("c"), 7), + (&String::from("b"), &String::from("c"), 10), + (&String::from("c"), &String::from("b"), 10), + ]; + for edge in expected_edges.iter() { + assert!(graph.edges().contains(edge)); } } + + #[test] + fn test_neighbours() { + let mut graph = UndirectedGraph::default(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + assert_eq!( + graph.neighbours("a").unwrap(), + &vec![(String::from("b"), 5), (String::from("c"), 7)] + ); + } } diff --git a/algae/tests/merkle.rs b/algae/tests/merkle.rs new file mode 100644 index 0000000..128d15e --- /dev/null +++ b/algae/tests/merkle.rs @@ -0,0 +1,94 @@ +/* + 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 c819f1b..2633db6 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -1,15 +1,15 @@ [package] -authors = ["FL03 (https://gitlab.com/FL03)", "Scattered-Systems (https://gitlab.com/scsys)"] +authors.workspace = true categories = [] -description = "Algae is a collection of core algorithms and data-structures, written in Rust" -edition = "2021" -homepage = "https://github.com/scattered-systems/algae/wiki" +description.workspace = true +edition.workspace = true +homepage.workspace = true keywords = ["algorithms", "data-structures"] -license = "Apache-2.0" +license.workspace = true name = "algae-graph" -readme = "README.md" -repository = "https://github.com/scattered-systems/algae" -version = "0.1.17" # TODO - Update the cargo package version +readme.workspace = true +repository.workspace = true +version.workspace = true [lib] crate-type = ["cdylib", "rlib"] diff --git a/graph/README.md b/graph/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/graph/src/components/mod.rs b/graph/src/components/mod.rs deleted file mode 100644 index 31a91ca..0000000 --- a/graph/src/components/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - Appellation: components - Contrib: FL03 - Description: -*/ - -pub mod directed; -pub mod undirected; diff --git a/graph/src/core/mod.rs b/graph/src/core/mod.rs deleted file mode 100644 index 6f88df7..0000000 --- a/graph/src/core/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub use self::{primitives::*, specs::*, utils::*}; - -pub(crate) mod primitives; -pub(crate) mod specs; - -pub(crate) mod utils {} diff --git a/graph/src/core/specs/mod.rs b/graph/src/core/specs/mod.rs deleted file mode 100644 index 4b86203..0000000 --- a/graph/src/core/specs/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - Appellation: specs - Contrib: FL03 - Description: -*/ -pub use self::graphable::*; - -pub(crate) mod graphable; diff --git a/graph/src/components/directed/graph.rs b/graph/src/directed/graph.rs similarity index 100% rename from graph/src/components/directed/graph.rs rename to graph/src/directed/graph.rs diff --git a/graph/src/components/directed/mod.rs b/graph/src/directed/mod.rs similarity index 100% rename from graph/src/components/directed/mod.rs rename to graph/src/directed/mod.rs diff --git a/graph/src/errors.rs b/graph/src/errors.rs new file mode 100644 index 0000000..f0d6d16 --- /dev/null +++ b/graph/src/errors.rs @@ -0,0 +1,22 @@ +/* + Appellation: errors + Contrib: FL03 + Description: +*/ +use serde::{Deserialize, Serialize}; + +#[derive( + Copy, Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, +)] +pub enum Errors { + #[default] + NodeNotInGraph, +} + +impl std::fmt::Display for Errors { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self.clone() { + Self::NodeNotInGraph => write!(f, "accessing a node that is not in the graph"), + } + } +} diff --git a/graph/src/core/specs/graphable.rs b/graph/src/graphable.rs similarity index 93% rename from graph/src/core/specs/graphable.rs rename to graph/src/graphable.rs index ee3ca5f..9bfa1f9 100644 --- a/graph/src/core/specs/graphable.rs +++ b/graph/src/graphable.rs @@ -3,7 +3,7 @@ Contrib: FL03 Description: */ -use crate::{AdjacencyHashTable, NodeNotInGraph}; +use crate::{AdjacencyHashTable, Errors}; use std::collections::HashSet; pub trait Graphable: Clone + Default { @@ -39,9 +39,9 @@ pub trait Graphable: Clone + Default { } edges } - fn neighbours(&self, node: &str) -> Result<&Vec<(String, i32)>, NodeNotInGraph> { + fn neighbours(&self, node: &str) -> Result<&Vec<(String, i32)>, Errors> { match self.adjacency_table().get(node) { - None => Err(NodeNotInGraph), + None => Err(Errors::NodeNotInGraph), Some(i) => Ok(i), } } diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 92cb228..1d8d194 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -4,7 +4,13 @@ Description: */ #[doc(inline)] -pub use self::{components::*, core::*}; +pub use self::{errors::*, graphable::*, nodes::*, primitives::*, utils::*}; -pub(crate) mod components; -pub(crate) mod core; +pub(crate) mod errors; +pub(crate) mod graphable; +pub(crate) mod nodes; +pub(crate) mod primitives; +pub(crate) mod utils; + +pub mod directed; +pub mod undirected; diff --git a/graph/src/core/primitives/nullnode.rs b/graph/src/nodes.rs similarity index 60% rename from graph/src/core/primitives/nullnode.rs rename to graph/src/nodes.rs index 8d190b6..82fe14b 100644 --- a/graph/src/core/primitives/nullnode.rs +++ b/graph/src/nodes.rs @@ -8,10 +8,15 @@ use serde::{Deserialize, Serialize}; #[derive( Copy, Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, )] -pub struct NodeNotInGraph; +pub enum Nodes { + #[default] + NotInGraph, +} -impl std::fmt::Display for NodeNotInGraph { +impl std::fmt::Display for Nodes { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "accessing a node that is not in the graph") + match self.clone() { + Self::NotInGraph => write!(f, "accessing a node that is not in the graph"), + } } } diff --git a/graph/src/core/primitives/mod.rs b/graph/src/primitives.rs similarity index 77% rename from graph/src/core/primitives/mod.rs rename to graph/src/primitives.rs index cee5f78..891af07 100644 --- a/graph/src/core/primitives/mod.rs +++ b/graph/src/primitives.rs @@ -3,9 +3,9 @@ Contrib: FL03 Description: */ -pub use self::{nullnode::*, types::*}; +pub use self::{constants::*, types::*}; -pub(crate) mod nullnode; +pub(crate) mod constants {} pub(crate) mod types { use std::collections::HashMap; diff --git a/graph/src/components/undirected/graph.rs b/graph/src/undirected/graph.rs similarity index 100% rename from graph/src/components/undirected/graph.rs rename to graph/src/undirected/graph.rs diff --git a/graph/src/components/undirected/mod.rs b/graph/src/undirected/mod.rs similarity index 100% rename from graph/src/components/undirected/mod.rs rename to graph/src/undirected/mod.rs diff --git a/graph/src/utils.rs b/graph/src/utils.rs new file mode 100644 index 0000000..1cfb395 --- /dev/null +++ b/graph/src/utils.rs @@ -0,0 +1,5 @@ +/* + Appellation: utils + Contrib: FL03 + Description: +*/ diff --git a/merkle/Cargo.toml b/merkle/Cargo.toml index 5535210..02ef07f 100644 --- a/merkle/Cargo.toml +++ b/merkle/Cargo.toml @@ -1,15 +1,15 @@ [package] -authors = ["FL03 (https://gitlab.com/FL03)", "Scattered-Systems (https://gitlab.com/scsys)"] +authors.workspace = true categories = [] -description = "Algae is a collection of core algorithms and data-structures, written in Rust" -edition = "2021" -homepage = "https://github.com/scattered-systems/algae/wiki" +description.workspace = true +edition.workspace = true +homepage.workspace = true keywords = ["algorithms", "data-structures"] -license = "Apache-2.0" +license.workspace = true name = "algae-merkle" -readme = "README.md" -repository = "https://github.com/scattered-systems/algae" -version = "0.1.17" # TODO - Update the cargo package version +readme.workspace = true +repository.workspace = true +version.workspace = true [features] default = [ @@ -25,7 +25,7 @@ crate-type = ["cdylib", "rlib"] test = true [dependencies] -decanter = { features = ["derive", "wasm"], version = "0.1.2" } +decanter.workspace = true # scsys = { features = ["full"], version = "0.1.40" } anyhow = "1" @@ -43,3 +43,7 @@ vrf = "0.2" [package.metadata.docs.rs] all-features = true rustc-args = ["--cfg", "docsrs"] + +[target.wasm32-unknown-unknown] + +[target.wasm32-wasi] diff --git a/merkle/README.md b/merkle/README.md deleted file mode 100644 index 6d0b5d7..0000000 --- a/merkle/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# algae-merkle - - - -## Getting Started - - -### Examples - - use algae::merkle::MerkleTree; - - fn main() { - let merkle = MerkleTree:new(); - } - -## References - -* []() -* []() \ No newline at end of file diff --git a/merkle/src/lib.rs b/merkle/src/lib.rs index fbebbe9..87b83a5 100644 --- a/merkle/src/lib.rs +++ b/merkle/src/lib.rs @@ -6,12 +6,11 @@ #[cfg(test)] extern crate hex_literal; #[doc(inline)] -pub use self::{layers::*, nodes::*, payloads::*, primitives::*, shape::*, trees::*, utils::*}; +pub use self::{layers::*, nodes::*, payloads::*, shape::*, trees::*, utils::*}; pub(crate) mod layers; pub(crate) mod nodes; pub(crate) mod payloads; -pub(crate) mod primitives; pub(crate) mod shape; pub(crate) mod trees; pub(crate) mod utils; diff --git a/merkle/src/primitives.rs b/merkle/src/primitives.rs deleted file mode 100644 index a8d7282..0000000 --- a/merkle/src/primitives.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* - Appellation: primitives - Contrib: FL03 - Description: ... summary ... -*/ diff --git a/merkle/src/utils/misc.rs b/merkle/src/utils.rs similarity index 53% rename from merkle/src/utils/misc.rs rename to merkle/src/utils.rs index cb1665d..78dc159 100644 --- a/merkle/src/utils/misc.rs +++ b/merkle/src/utils.rs @@ -1,8 +1,9 @@ /* - Appellation: misc + Appellation: generate Contrib: FL03 Description: */ +use crate::proofs::proof_path; use decanter::prelude::{hasher, H256}; use serde::Serialize; @@ -26,3 +27,24 @@ pub fn merkle_hash(data: T) -> H256 { }; res } + +/// Verify that the datum hash with a vector of proofs will produce the Merkle root. Also need the +/// index of datum and `leaf_size`, the total number of leaves. +pub fn is_merkle_valid( + root: &H256, + datum: &H256, + proof: &[H256], + index: usize, + leaf_size: usize, +) -> bool { + let mut h: H256 = *datum; + let proof_index = proof_path(index, leaf_size); + for i in 0..proof.len() { + if proof_index[i] % 2 == 0 { + h = add_hash(&proof[i], &h); + } else { + h = add_hash(&h, &proof[i]); + } + } + *root == h +} diff --git a/merkle/src/utils/mod.rs b/merkle/src/utils/mod.rs deleted file mode 100644 index 323619f..0000000 --- a/merkle/src/utils/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -/* - Appellation: generate - Contrib: FL03 - Description: -*/ -pub use self::{misc::*, validate::*}; - -pub(crate) mod misc; -pub(crate) mod validate; diff --git a/merkle/src/utils/validate.rs b/merkle/src/utils/validate.rs deleted file mode 100644 index eeb6ab2..0000000 --- a/merkle/src/utils/validate.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* - Appellation: validate - Contrib: FL03 - Description: -*/ -use crate::{add_hash, proofs::proof_path}; -use decanter::prelude::H256; - -/// Verify that the datum hash with a vector of proofs will produce the Merkle root. Also need the -/// index of datum and `leaf_size`, the total number of leaves. -pub fn is_merkle_valid( - root: &H256, - datum: &H256, - proof: &[H256], - index: usize, - leaf_size: usize, -) -> bool { - let mut h: H256 = *datum; - let proof_index = proof_path(index, leaf_size); - for i in 0..proof.len() { - if proof_index[i] % 2 == 0 { - h = add_hash(&proof[i], &h); - } else { - h = add_hash(&h, &proof[i]); - } - } - *root == h -} diff --git a/mmr/Cargo.toml b/mmr/Cargo.toml index 902b4ec..bb90ce4 100644 --- a/mmr/Cargo.toml +++ b/mmr/Cargo.toml @@ -1,25 +1,30 @@ [package] -authors = ["FL03 (https://gitlab.com/FL03)", "Scattered-Systems (https://gitlab.com/scsys)"] +authors.workspace = true categories = [] -description = "algae-mmr implements a general merkle mountain range intended for use in blockchain environments" -edition = "2021" -homepage = "https://github.com/scattered-systems/algae/wiki" +description.workspace = true +edition.workspace = true +homepage.workspace = true keywords = ["algorithms", "data-structures"] -license = "Apache-2.0" +license.workspace = true name = "algae-mmr" -readme = "README.md" -repository = "https://github.com/scattered-systems/algae" -version = "0.1.17" # TODO - Update the cargo package version +readme.workspace = true +repository.workspace = true +version.workspace = true [features] - +default = [ + "decanter/derive" +] +wasm = [ + "decanter/wasm" +] [lib] crate-type = ["cdylib", "rlib"] test = true [dependencies] -decanter = { features = ["derive"], version = "0.1.2" } +decanter.workspace = true anyhow = "1" digest = "0.10" @@ -29,10 +34,14 @@ serde = { features = ["derive"], version = "1" } serde_json = "1" [dev-dependencies] -scsys = { default-features = false, features = ["gen"], version = "0.1.40" } +scsys = { default-features = false, features = ["gen"], version = "0.1.41" } hex-literal = "0.3.4" vrf = "0.2.4" [package.metadata.docs.rs] all-features = true -rustc-args = ["--cfg", "docsrs"] \ No newline at end of file +rustc-args = ["--cfg", "docsrs"] + +[target.wasm32-unknown-unknown] + +[target.wasm32-wasi] diff --git a/mmr/README.md b/mmr/README.md deleted file mode 100644 index 6f8bb76..0000000 --- a/mmr/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# algae-mmr - - - -## Getting Started - - -### Examples - - use algae::mmr::*; - - fn main() { - - } - -## References - -* []() -* []() \ No newline at end of file diff --git a/scripts/publish.sh b/scripts/publish.sh deleted file mode 100644 index aa2d2e6..0000000 --- a/scripts/publish.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -cargo publish --all-features -p algae-graph -cargo publish --all-features -p algae-merkle -cargo publish --all-features -p algae-mmr -cargo publish --all-features -p algae \ No newline at end of file diff --git a/scripts/rustup.sh b/scripts/rustup.sh new file mode 100644 index 0000000..f0e610e --- /dev/null +++ b/scripts/rustup.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +rustup default nightly +rustup target add wasm32-unknown-unknown wasm32-wasi --toolchain nightly diff --git a/scripts/setup.sh b/scripts/setup.sh index 20405f8..061b585 100644 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -1,7 +1,3 @@ #!/usr/bin/env bash - sudo apt update -y && sudo apt upgrade -y && sudo apt autoremove -y sudo apt install -y protobuf-compiler -rustup install nightly -rustup component add clippy rustfmt --toolchain nightly -rustup target add wasm32-unknown-unknown --toolchain nightly From 0146b6167078dc903dd327b5d4444ea421f45b23 Mon Sep 17 00:00:00 2001 From: FL03 Date: Wed, 25 Jan 2023 12:16:39 -0600 Subject: [PATCH 2/9] Update Signed-off-by: FL03 --- graph/src/{directed/graph.rs => directed.rs} | 0 graph/src/directed/mod.rs | 75 ------------------- graph/src/lib.rs | 3 +- graph/src/nodes.rs | 22 ------ .../{undirected/graph.rs => undirected.rs} | 0 graph/src/undirected/mod.rs | 47 ------------ graph/tests/default.rs | 11 +-- graph/tests/directed.rs | 64 ++++++++++++++++ graph/tests/undirected.rs | 37 +++++++++ merkle/tests/default.rs | 11 +-- mmr/tests/default.rs | 11 +-- 11 files changed, 114 insertions(+), 167 deletions(-) rename graph/src/{directed/graph.rs => directed.rs} (100%) delete mode 100644 graph/src/directed/mod.rs delete mode 100644 graph/src/nodes.rs rename graph/src/{undirected/graph.rs => undirected.rs} (100%) delete mode 100644 graph/src/undirected/mod.rs create mode 100644 graph/tests/directed.rs create mode 100644 graph/tests/undirected.rs diff --git a/graph/src/directed/graph.rs b/graph/src/directed.rs similarity index 100% rename from graph/src/directed/graph.rs rename to graph/src/directed.rs diff --git a/graph/src/directed/mod.rs b/graph/src/directed/mod.rs deleted file mode 100644 index 053861e..0000000 --- a/graph/src/directed/mod.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - Appellation: directed - Contrib: FL03 - Description: -*/ -pub use self::graph::*; - -pub(crate) mod graph; - -#[cfg(test)] -mod tests { - use crate::{directed::DirectedGraph, Graphable}; - - #[test] - fn test_add_node() { - let tmp = ["a", "b", "c"] - .iter() - .cloned() - .map(String::from) - .collect::>(); - - let mut graph = DirectedGraph::default(); - graph.add_node("a"); - graph.add_node("b"); - graph.add_node("c"); - assert_eq!( - graph.nodes(), - [&tmp[0], &tmp[1], &tmp[2]].iter().cloned().collect() - ); - } - - #[test] - fn test_add_edge() { - let mut graph = DirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("c", "a", 7)); - graph.add_edge(("b", "c", 10)); - - let expected_edges = [ - (&String::from("a"), &String::from("b"), 5), - (&String::from("c"), &String::from("a"), 7), - (&String::from("b"), &String::from("c"), 10), - ]; - for edge in expected_edges.iter() { - assert!(graph.edges().contains(edge)); - } - } - - #[test] - fn test_neighbours() { - let mut graph = DirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); - - assert_eq!( - graph.neighbours("a").unwrap(), - &vec![(String::from("b"), 5)] - ); - } - - #[test] - fn test_contains() { - let mut graph = DirectedGraph::default(); - graph.add_node("a"); - graph.add_node("b"); - graph.add_node("c"); - assert!(graph.contains("a")); - assert!(graph.contains("b")); - assert!(graph.contains("c")); - assert!(!graph.contains("d")); - } -} diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 1d8d194..0da79c3 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -4,11 +4,10 @@ Description: */ #[doc(inline)] -pub use self::{errors::*, graphable::*, nodes::*, primitives::*, utils::*}; +pub use self::{errors::*, graphable::*, primitives::*, utils::*}; pub(crate) mod errors; pub(crate) mod graphable; -pub(crate) mod nodes; pub(crate) mod primitives; pub(crate) mod utils; diff --git a/graph/src/nodes.rs b/graph/src/nodes.rs deleted file mode 100644 index 82fe14b..0000000 --- a/graph/src/nodes.rs +++ /dev/null @@ -1,22 +0,0 @@ -/* - Appellation: nullnode - Contrib: FL03 - Description: -*/ -use serde::{Deserialize, Serialize}; - -#[derive( - Copy, Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub enum Nodes { - #[default] - NotInGraph, -} - -impl std::fmt::Display for Nodes { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self.clone() { - Self::NotInGraph => write!(f, "accessing a node that is not in the graph"), - } - } -} diff --git a/graph/src/undirected/graph.rs b/graph/src/undirected.rs similarity index 100% rename from graph/src/undirected/graph.rs rename to graph/src/undirected.rs diff --git a/graph/src/undirected/mod.rs b/graph/src/undirected/mod.rs deleted file mode 100644 index 4c91df1..0000000 --- a/graph/src/undirected/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -/* - Appellation: directed - Contrib: FL03 - Description: -*/ -pub use self::graph::*; - -pub(crate) mod graph; - -#[cfg(test)] -mod tests { - use crate::{undirected::UndirectedGraph, Graphable}; - #[test] - fn test_add_edge() { - let mut graph = UndirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); - - let expected_edges = [ - (&String::from("a"), &String::from("b"), 5), - (&String::from("b"), &String::from("a"), 5), - (&String::from("c"), &String::from("a"), 7), - (&String::from("a"), &String::from("c"), 7), - (&String::from("b"), &String::from("c"), 10), - (&String::from("c"), &String::from("b"), 10), - ]; - for edge in expected_edges.iter() { - assert!(graph.edges().contains(edge)); - } - } - - #[test] - fn test_neighbours() { - let mut graph = UndirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); - - assert_eq!( - graph.neighbours("a").unwrap(), - &vec![(String::from("b"), 5), (String::from("c"), 7)] - ); - } -} diff --git a/graph/tests/default.rs b/graph/tests/default.rs index 827ee40..7dee5fa 100644 --- a/graph/tests/default.rs +++ b/graph/tests/default.rs @@ -1,9 +1,6 @@ #[cfg(test)] -mod tests { - - #[test] - fn it_compiles() { - let f = |i| i + 1; - assert_eq!(f(10), 11) - } +#[test] +fn lib_compiles() { + let f = |i| i + 1; + assert_eq!(f(10), 11) } diff --git a/graph/tests/directed.rs b/graph/tests/directed.rs new file mode 100644 index 0000000..ac12485 --- /dev/null +++ b/graph/tests/directed.rs @@ -0,0 +1,64 @@ +#[cfg(test)] +use algae_graph::{directed::DirectedGraph, Graphable}; + +#[test] +fn test_add_node() { + let tmp = ["a", "b", "c"] + .iter() + .cloned() + .map(String::from) + .collect::>(); + + let mut graph = DirectedGraph::default(); + graph.add_node("a"); + graph.add_node("b"); + graph.add_node("c"); + assert_eq!( + graph.nodes(), + [&tmp[0], &tmp[1], &tmp[2]].iter().cloned().collect() + ); +} + +#[test] +fn test_add_edge() { + let mut graph = DirectedGraph::default(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("c", "a", 7)); + graph.add_edge(("b", "c", 10)); + + let expected_edges = [ + (&String::from("a"), &String::from("b"), 5), + (&String::from("c"), &String::from("a"), 7), + (&String::from("b"), &String::from("c"), 10), + ]; + for edge in expected_edges.iter() { + assert!(graph.edges().contains(edge)); + } +} + +#[test] +fn test_neighbours() { + let mut graph = DirectedGraph::default(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + assert_eq!( + graph.neighbours("a").unwrap(), + &vec![(String::from("b"), 5)] + ); +} + +#[test] +fn test_contains() { + let mut graph = DirectedGraph::default(); + graph.add_node("a"); + graph.add_node("b"); + graph.add_node("c"); + assert!(graph.contains("a")); + assert!(graph.contains("b")); + assert!(graph.contains("c")); + assert!(!graph.contains("d")); +} diff --git a/graph/tests/undirected.rs b/graph/tests/undirected.rs new file mode 100644 index 0000000..89bf751 --- /dev/null +++ b/graph/tests/undirected.rs @@ -0,0 +1,37 @@ +#[cfg(test)] +use algae_graph::{undirected::UndirectedGraph, Graphable}; + +#[test] +fn test_add_edge() { + let mut graph = UndirectedGraph::default(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + let expected_edges = [ + (&String::from("a"), &String::from("b"), 5), + (&String::from("b"), &String::from("a"), 5), + (&String::from("c"), &String::from("a"), 7), + (&String::from("a"), &String::from("c"), 7), + (&String::from("b"), &String::from("c"), 10), + (&String::from("c"), &String::from("b"), 10), + ]; + for edge in expected_edges.iter() { + assert!(graph.edges().contains(edge)); + } +} + +#[test] +fn test_neighbours() { + let mut graph = UndirectedGraph::default(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + assert_eq!( + graph.neighbours("a").unwrap(), + &vec![(String::from("b"), 5), (String::from("c"), 7)] + ); +} diff --git a/merkle/tests/default.rs b/merkle/tests/default.rs index 827ee40..7dee5fa 100644 --- a/merkle/tests/default.rs +++ b/merkle/tests/default.rs @@ -1,9 +1,6 @@ #[cfg(test)] -mod tests { - - #[test] - fn it_compiles() { - let f = |i| i + 1; - assert_eq!(f(10), 11) - } +#[test] +fn lib_compiles() { + let f = |i| i + 1; + assert_eq!(f(10), 11) } diff --git a/mmr/tests/default.rs b/mmr/tests/default.rs index 827ee40..7dee5fa 100644 --- a/mmr/tests/default.rs +++ b/mmr/tests/default.rs @@ -1,9 +1,6 @@ #[cfg(test)] -mod tests { - - #[test] - fn it_compiles() { - let f = |i| i + 1; - assert_eq!(f(10), 11) - } +#[test] +fn lib_compiles() { + let f = |i| i + 1; + assert_eq!(f(10), 11) } From 4c6f989668bbbb76d5264c4d466d9476c0103461 Mon Sep 17 00:00:00 2001 From: FL03 Date: Fri, 3 Feb 2023 12:14:23 -0600 Subject: [PATCH 3/9] Update Signed-off-by: FL03 --- algae/src/lib.rs | 4 +- algae/src/list/linked/mod.rs | 7 ++ algae/src/list/linked/persistant.rs | 130 ++++++++++++++++++++++++++++ algae/src/list/mod.rs | 7 ++ algae/src/trees/btree/cmps/mod.rs | 10 --- algae/src/trees/btree/cmps/nodes.rs | 45 ---------- algae/src/trees/btree/cmps/props.rs | 96 -------------------- algae/src/trees/btree/mod.rs | 45 ---------- algae/src/trees/btree/specs.rs | 60 ------------- algae/src/trees/btree/tree.rs | 71 --------------- algae/src/trees/mod.rs | 7 -- 11 files changed, 146 insertions(+), 336 deletions(-) create mode 100644 algae/src/list/linked/mod.rs create mode 100644 algae/src/list/linked/persistant.rs create mode 100644 algae/src/list/mod.rs delete mode 100644 algae/src/trees/btree/cmps/mod.rs delete mode 100644 algae/src/trees/btree/cmps/nodes.rs delete mode 100644 algae/src/trees/btree/cmps/props.rs delete mode 100644 algae/src/trees/btree/mod.rs delete mode 100644 algae/src/trees/btree/specs.rs delete mode 100644 algae/src/trees/btree/tree.rs delete mode 100644 algae/src/trees/mod.rs diff --git a/algae/src/lib.rs b/algae/src/lib.rs index d0102da..f48e1e2 100644 --- a/algae/src/lib.rs +++ b/algae/src/lib.rs @@ -1,6 +1,6 @@ /* Appellation: algae - Creator: FL03 + Contrib: FL03 Description: Algae is a comprehensive collection of algorithms and data-structures */ @@ -11,7 +11,7 @@ pub use algae_merkle as merkle; #[cfg(feature = "mmr")] pub use algae_mmr as mmr; -pub mod trees; +pub mod list; pub mod prelude { #[cfg(feature = "graph")] diff --git a/algae/src/list/linked/mod.rs b/algae/src/list/linked/mod.rs new file mode 100644 index 0000000..5327b5b --- /dev/null +++ b/algae/src/list/linked/mod.rs @@ -0,0 +1,7 @@ +/* + Appellation: linked + Contrib: FL03 + Description: ... Summary ... +*/ + +pub mod persistant; diff --git a/algae/src/list/linked/persistant.rs b/algae/src/list/linked/persistant.rs new file mode 100644 index 0000000..54ef537 --- /dev/null +++ b/algae/src/list/linked/persistant.rs @@ -0,0 +1,130 @@ +/* + Appellation: persistant + Contrib: FL03 + Description: ... Summary ... +*/ +use std::rc::Rc; + +type Link = Option>>; + +struct Node { + elem: T, + next: Link, +} + +impl Node { + pub fn new(elem: T, next: Link) -> Self { + Self { elem, next } + } + pub fn data(&self) -> &T { + &self.elem + } + pub fn link(&self) -> &Link { + &self.next + } +} + +/// Singly-Linked, Persistant List +pub struct SLPList { + head: Link, +} + +impl SLPList { + pub fn new() -> Self { + Self::from(None) + } + pub fn prepend(&self, elem: T) -> Self { + SLPList::from(Some(Rc::new(Node::new(elem, self.head.clone())))) + } + + pub fn tail(&self) -> Self { + SLPList::from(self.head.as_ref().and_then(|node| node.link().clone())) + } + + pub fn head(&self) -> Option<&T> { + self.head.as_ref().map(|node| node.data()) + } + + pub fn iter(&self) -> Iter<'_, T> { + Iter { + next: self.head.as_deref(), + } + } +} + +impl From> for SLPList { + fn from(head: Link) -> SLPList { + SLPList { head } + } +} + +impl Default for SLPList { + fn default() -> Self { + Self::from(None) + } +} + +impl Drop for SLPList { + fn drop(&mut self) { + let mut head = self.head.take(); + while let Some(node) = head { + if let Ok(mut node) = Rc::try_unwrap(node) { + head = node.next.take(); + } else { + break; + } + } + } +} + +pub struct Iter<'a, T> { + next: Option<&'a Node>, +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.next.map(|node| { + self.next = node.next.as_deref(); + &node.elem + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_linked_list() { + let list = SLPList::default(); + assert_eq!(list.head(), None); + + let list = list.prepend(1).prepend(2).prepend(3); + assert_eq!(list.head(), Some(&3)); + + let list = list.tail(); + assert_eq!(list.head(), Some(&2)); + + let list = list.tail(); + assert_eq!(list.head(), Some(&1)); + + let list = list.tail(); + assert_eq!(list.head(), None); + + // Make sure empty tail works + let list = list.tail(); + assert_eq!(list.head(), None); + } + + #[test] + fn test_linked_list_iter() { + let list = SLPList::default().prepend(1).prepend(2).prepend(3); + + let mut iter = list.iter(); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&1)); + } +} diff --git a/algae/src/list/mod.rs b/algae/src/list/mod.rs new file mode 100644 index 0000000..7f6e183 --- /dev/null +++ b/algae/src/list/mod.rs @@ -0,0 +1,7 @@ +/* + Appellation: list + Contrib: FL03 + Description: ... Summary ... +*/ + +pub mod linked; diff --git a/algae/src/trees/btree/cmps/mod.rs b/algae/src/trees/btree/cmps/mod.rs deleted file mode 100644 index 3b9cdbf..0000000 --- a/algae/src/trees/btree/cmps/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -/* - Appellation: cmps - Contrib: FL03 - Description: ... Summary ... -*/ -#[doc(inline)] -pub use self::{nodes::*, props::*}; - -pub(crate) mod nodes; -pub(crate) mod props; diff --git a/algae/src/trees/btree/cmps/nodes.rs b/algae/src/trees/btree/cmps/nodes.rs deleted file mode 100644 index 5786882..0000000 --- a/algae/src/trees/btree/cmps/nodes.rs +++ /dev/null @@ -1,45 +0,0 @@ -/* - Appellation: nodes - Contrib: FL03 - Description: ... Summary ... -*/ -use serde::{Deserialize, Serialize}; -use std::convert::From; - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct Node { - pub children: Vec>, - pub keys: Vec, -} - -impl Node -where - T: Ord, -{ - pub fn new(children: Option>>, degree: usize, keys: Option>) -> Self { - let children = children.unwrap_or_else(|| Vec::with_capacity(degree)); - let keys = keys.unwrap_or_else(|| Vec::with_capacity(degree - 1)); - Node { children, keys } - } - - pub fn children(&self) -> &Vec> { - &self.children - } - - pub fn fetch_child(&mut self, index: usize) -> &mut Node { - &mut self.children[index] - } - - pub fn is_leaf(&self) -> bool { - self.children.is_empty() - } -} - -impl From for Node -where - T: Ord, -{ - fn from(data: usize) -> Self { - Self::new(None, data, None) - } -} diff --git a/algae/src/trees/btree/cmps/props.rs b/algae/src/trees/btree/cmps/props.rs deleted file mode 100644 index a0ad004..0000000 --- a/algae/src/trees/btree/cmps/props.rs +++ /dev/null @@ -1,96 +0,0 @@ -/* - Appellation: props - Contrib: FL03 - Description: ... Summary ... -*/ -use crate::trees::btree::{BaseObj, Node}; -use serde::{Deserialize, Serialize}; -use std::{convert::TryFrom, fmt::Debug}; - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct BTreeProps { - pub degree: usize, - pub max_keys: usize, - pub mid_key_index: usize, -} - -impl BTreeProps { - pub fn new(degree: usize) -> Self { - BTreeProps { - degree, - max_keys: degree - 1, - mid_key_index: (degree - 1) / 2, - } - } - - pub fn is_maxed_out(&self, node: &Node) -> bool - where - T: BaseObj, - { - node.keys.len() == self.max_keys - } - - pub fn split_child(&self, parent: &mut Node, child_index: usize) - where - T: BaseObj, - { - let child = parent.fetch_child(child_index); - let middle_key = child.keys[self.mid_key_index]; - let right_keys = match child.keys.split_off(self.mid_key_index).split_first() { - Some((_first, _others)) => { - // We don't need _first, as it will move to parent node. - _others.to_vec() - } - None => Vec::with_capacity(self.max_keys), - }; - let right_children = if !child.is_leaf() { - Some(child.children.split_off(self.mid_key_index + 1)) - } else { - None - }; - let new_child_node: Node = Node::new(right_children, self.degree, Some(right_keys)); - - parent.keys.insert(child_index, middle_key); - parent.children.insert(child_index + 1, new_child_node); - } - - pub fn insert_non_full(&mut self, node: &mut Node, key: T) - where - T: BaseObj, - { - let mut index: isize = isize::try_from(node.keys.len()).ok().unwrap() - 1; - while index >= 0 && node.keys[index as usize] >= key { - index -= 1; - } - - let mut u_index: usize = usize::try_from(index + 1).ok().unwrap(); - if node.is_leaf() { - // Just insert it, as we know this method will be called only when node is not full - node.keys.insert(u_index, key); - } else { - if self.is_maxed_out(&node.children[u_index]) { - self.split_child(node, u_index); - if node.keys[u_index] < key { - u_index += 1; - } - } - - self.insert_non_full(&mut node.children[u_index], key); - } - } - pub fn traverse_node(node: &Node, depth: usize) - where - T: BaseObj, - { - if node.is_leaf() { - print!(" {0:{<1$}{2:?}{0:}<1$} ", "", depth, node.keys); - } else { - let _depth = depth + 1; - for (index, key) in node.keys.iter().enumerate() { - Self::traverse_node(&node.children[index], _depth); - print!("{0:{<1$}{2:?}{0:}<1$}", "", depth, key); - } - Self::traverse_node(node.children.last().unwrap(), _depth); - } - } -} diff --git a/algae/src/trees/btree/mod.rs b/algae/src/trees/btree/mod.rs deleted file mode 100644 index 6fddd51..0000000 --- a/algae/src/trees/btree/mod.rs +++ /dev/null @@ -1,45 +0,0 @@ -/* - Appellation: btree - Creator: FL03 - Description: - ... Summary ... -*/ -#[doc(inline)] -pub use self::{cmps::*, primitives::*, specs::*, utils::*}; - -pub(crate) mod cmps; -pub(crate) mod specs; -pub(crate) mod tree; - -pub(crate) mod primitives { - use super::BaseObj; - use serde::{Deserialize, Serialize}; - use std::fmt::Debug; - - #[derive( - Copy, Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, - )] - pub struct Data(pub T); - - impl Data { - pub fn new(data: T) -> Self { - Self(data) - } - pub fn data(&self) -> &T { - &self.0 - } - } - - impl From<&T> for Data - where - T: Clone, - { - fn from(data: &T) -> Self { - Self::new(data.clone()) - } - } - - impl BaseObj for Data where T: Copy + Debug + Default + Ord {} -} - -pub(crate) mod utils {} diff --git a/algae/src/trees/btree/specs.rs b/algae/src/trees/btree/specs.rs deleted file mode 100644 index ed89a5a..0000000 --- a/algae/src/trees/btree/specs.rs +++ /dev/null @@ -1,60 +0,0 @@ -/* - Appellation: specs - Contrib: FL03 - Description: ... Summary ... -*/ -use super::{BTreeProps, Node}; -use std::fmt::Debug; - -pub trait BaseObj: Copy + Default + Debug + Ord {} - -pub trait BTreeSpec { - fn root(&self) -> &Node; - fn root_mut(&mut self) -> &mut Node; - fn props(&self) -> &BTreeProps; - fn add_child(&mut self, root: Node) -> &Self { - self.root_mut().children.insert(0, root); - self - } -} - -pub trait BTreeSpecExt: BTreeSpec { - fn swap_root(&mut self, with: &mut Node) { - std::mem::swap(with, self.root_mut()) - } - fn insert(&mut self, key: T) { - if self.props().is_maxed_out(self.root()) { - // Create an empty root and split the old root... - let mut new_root = Node::from(self.props().clone().degree); - self.swap_root(&mut new_root); - self.add_child(new_root); - self.props().clone().split_child(self.root_mut(), 0); - } - self.props().clone().insert_non_full(self.root_mut(), key); - } - - fn traverse(&self) { - BTreeProps::traverse_node(self.root(), 0); - println!(); - } - - fn search(&self, key: T) -> bool { - let mut current_node = self.root(); - let mut index: isize; - loop { - index = isize::try_from(current_node.keys.len()).ok().unwrap() - 1; - while index >= 0 && current_node.keys[index as usize] > key { - index -= 1; - } - - let u_index: usize = usize::try_from(index + 1).ok().unwrap(); - if index >= 0 && current_node.keys[u_index - 1] == key { - break true; - } else if current_node.is_leaf() { - break false; - } else { - current_node = ¤t_node.children[u_index]; - } - } - } -} diff --git a/algae/src/trees/btree/tree.rs b/algae/src/trees/btree/tree.rs deleted file mode 100644 index 20eacc1..0000000 --- a/algae/src/trees/btree/tree.rs +++ /dev/null @@ -1,71 +0,0 @@ -/* - Appellation: btree - Contrib: FL03 - Description: ... Summary ... -*/ -use crate::trees::btree::{BTreeProps, BTreeSpec, BTreeSpecExt, BaseObj, Node}; -use serde::{Deserialize, Serialize}; -use std::convert::From; - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct BTree { - pub root: Node, - pub props: BTreeProps, -} - -impl BTree -where - T: BaseObj, -{ - pub fn new(root: Node, props: BTreeProps) -> Self { - Self { root, props } - } -} - -impl BTreeSpec for BTree -where - T: BaseObj, -{ - fn root(&self) -> &Node { - &self.root - } - fn root_mut(&mut self) -> &mut Node { - &mut self.root - } - fn props(&self) -> &BTreeProps { - &self.props - } -} - -impl BTreeSpecExt for BTree where T: BaseObj {} - -/// Create a new tree given the branch factor -impl From for BTree -where - T: BaseObj, -{ - fn from(data: usize) -> Self { - let degree = 2 * data; - BTree::new(Node::from(degree), BTreeProps::new(degree)) - } -} - -#[cfg(test)] -mod tests { - use super::BTree; - use crate::trees::btree::{BTreeSpecExt, Data}; - - #[test] - fn test_search() { - let data = [10, 20, 30, 5, 6, 7, 11, 12, 15] - .iter() - .map(|i| Data::new(*i)) - .collect::>>(); - let mut tree = BTree::from(2); // Creates a new tree with a branch factor of 2 (degree of 4) - for i in data { - tree.insert(i); - } - assert!(tree.search(Data::new(15))); - assert!(!tree.search(Data::new(16))); - } -} diff --git a/algae/src/trees/mod.rs b/algae/src/trees/mod.rs deleted file mode 100644 index 038c57d..0000000 --- a/algae/src/trees/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* - Appellation: trees - Creator: FL03 - Description: Implements a collection of useful, non-linear data-structures -*/ - -pub mod btree; From 5bfa86a7dc770b319d76a67581570f97f690801f Mon Sep 17 00:00:00 2001 From: FL03 Date: Sun, 12 Mar 2023 09:47:39 -0500 Subject: [PATCH 4/9] Update --- Cargo.toml | 4 ++ algae/Cargo.toml | 7 ++- algae/tests/graphs.rs | 108 -------------------------------------- graph/Cargo.toml | 21 +++++--- graph/src/cmp/atable.rs | 87 ++++++++++++++++++++++++++++++ graph/src/cmp/edge.rs | 19 +++++++ graph/src/cmp/mod.rs | 18 +++++++ graph/src/directed.rs | 79 ++++++++++++++++++++++++---- graph/src/errors.rs | 35 +++++++----- graph/src/graphable.rs | 51 ------------------ graph/src/lib.rs | 76 +++++++++++++++++++++++---- graph/src/primitives.rs | 14 ----- graph/src/undirected.rs | 90 ++++++++++++++++++++++--------- graph/src/utils.rs | 5 -- graph/tests/directed.rs | 64 ---------------------- graph/tests/undirected.rs | 37 ------------- merkle/Cargo.toml | 14 ++--- mmr/Cargo.toml | 10 ++-- 18 files changed, 384 insertions(+), 355 deletions(-) delete mode 100644 algae/tests/graphs.rs create mode 100644 graph/src/cmp/atable.rs create mode 100644 graph/src/cmp/edge.rs create mode 100644 graph/src/cmp/mod.rs delete mode 100644 graph/src/graphable.rs delete mode 100644 graph/src/primitives.rs delete mode 100644 graph/src/utils.rs delete mode 100644 graph/tests/directed.rs delete mode 100644 graph/tests/undirected.rs diff --git a/Cargo.toml b/Cargo.toml index 39cad12..5f020ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,12 @@ version = "0.1.18" [workspace.dependencies] decanter = { features = ["derive", "wasm"], version = "0.1.3" } scsys = { features = [], version = "0.1.41" } + +itertools = "0.10" serde = { features = ["derive"], version = "1" } serde_json = "1" +smart-default = "0.6" +strum = { features = ["derive"], version = "0.24" } [workspace] default-members = [ diff --git a/algae/Cargo.toml b/algae/Cargo.toml index bd4624c..ab1083b 100644 --- a/algae/Cargo.toml +++ b/algae/Cargo.toml @@ -37,12 +37,17 @@ test = true 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" } + +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 = { features = ["derive"], version = "0.1.3" } +decanter.workspace = true hex-literal = "0.3" vrf = "0.2" diff --git a/algae/tests/graphs.rs b/algae/tests/graphs.rs deleted file mode 100644 index 76525d3..0000000 --- a/algae/tests/graphs.rs +++ /dev/null @@ -1,108 +0,0 @@ -#[cfg(feature = "graph")] -#[cfg(test)] -mod directed_graph_tests { - use algae::graph::{directed::DirectedGraph, Graphable}; - - #[test] - fn test_add_node() { - let tmp = ["a", "b", "c"] - .iter() - .cloned() - .map(String::from) - .collect::>(); - - let mut graph = DirectedGraph::default(); - graph.add_node("a"); - graph.add_node("b"); - graph.add_node("c"); - assert_eq!( - graph.nodes(), - [&tmp[0], &tmp[1], &tmp[2]].iter().cloned().collect() - ); - } - - #[test] - fn test_add_edge() { - let mut graph = DirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("c", "a", 7)); - graph.add_edge(("b", "c", 10)); - - let expected_edges = [ - (&String::from("a"), &String::from("b"), 5), - (&String::from("c"), &String::from("a"), 7), - (&String::from("b"), &String::from("c"), 10), - ]; - for edge in expected_edges.iter() { - assert!(graph.edges().contains(edge)); - } - } - - #[test] - fn test_neighbours() { - let mut graph = DirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); - - assert_eq!( - graph.neighbours("a").unwrap(), - &vec![(String::from("b"), 5)] - ); - } - - #[test] - fn test_contains() { - let mut graph = DirectedGraph::default(); - graph.add_node("a"); - graph.add_node("b"); - graph.add_node("c"); - assert!(graph.contains("a")); - assert!(graph.contains("b")); - assert!(graph.contains("c")); - assert!(!graph.contains("d")); - } -} - -#[cfg(feature = "graph")] -#[cfg(test)] -mod undirected_graph_tests { - use algae::graph::{undirected::UndirectedGraph, Graphable}; - - #[test] - fn test_add_edge() { - let mut graph = UndirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); - - let expected_edges = [ - (&String::from("a"), &String::from("b"), 5), - (&String::from("b"), &String::from("a"), 5), - (&String::from("c"), &String::from("a"), 7), - (&String::from("a"), &String::from("c"), 7), - (&String::from("b"), &String::from("c"), 10), - (&String::from("c"), &String::from("b"), 10), - ]; - for edge in expected_edges.iter() { - assert!(graph.edges().contains(edge)); - } - } - - #[test] - fn test_neighbours() { - let mut graph = UndirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); - - assert_eq!( - graph.neighbours("a").unwrap(), - &vec![(String::from("b"), 5), (String::from("c"), 7)] - ); - } -} diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 2633db6..fcb4687 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -1,16 +1,20 @@ [package] authors.workspace = true -categories = [] +categories.workspace = true description.workspace = true edition.workspace = true homepage.workspace = true -keywords = ["algorithms", "data-structures"] +keywords.workspace = true license.workspace = true name = "algae-graph" readme.workspace = true repository.workspace = true version.workspace = true +[features] +default = [] + + [lib] crate-type = ["cdylib", "rlib"] @@ -19,9 +23,10 @@ crate-type = ["cdylib", "rlib"] [dev-dependencies] [dependencies] -decanter = { features = ["derive"], version = "0.1.2" } -scsys = { features = ["full"], version = "0.1.40" } - -itertools = "0.10.5" -serde = { features = ["derive"], version = "1" } -serde_json = "1" +decanter.workspace = true +itertools.workspace = true +scsys.workspace = true +serde.workspace = true +serde_json.workspace = true +smart-default.workspace = true +strum.workspace = true diff --git a/graph/src/cmp/atable.rs b/graph/src/cmp/atable.rs new file mode 100644 index 0000000..f3c333c --- /dev/null +++ b/graph/src/cmp/atable.rs @@ -0,0 +1,87 @@ +/* + Appellation: atable + Contrib: FL03 + Description: an adjacency table +*/ +use super::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); + 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>); + +impl AdjacencyTable { + pub fn new() -> Self { + Self(HashMap::new()) + } + pub fn capacity(&self) -> usize { + self.0.capacity() + } + pub fn clear(&mut self) { + self.0.clear() + } + pub fn drain(&mut self) -> hash_map::Drain<'_, N, Vec<(N, V)>> { + self.0.drain() + } + pub fn entry(&mut self, key: N) -> hash_map::Entry<'_, N, Vec<(N, V)>> { + self.0.entry(key) + } + pub fn insert(&mut self, key: N, val: Vec<(N, V)>) -> Option> { + self.0.insert(key, val) + } + pub fn get(&self, key: &N) -> Option<&Vec<(N, V)>> { + self.0.get(key) + } + pub fn keys(&self) -> hash_map::Keys> { + self.0.keys() + } + pub fn table(self) -> HashMap> { + self.0 + } +} + +impl Extend<(N, Vec<(N, V)>)> for AdjacencyTable { + fn extend)>>(&mut self, iter: T) { + self.0.extend(iter) + } +} + +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) + } +} + +impl IntoIterator for AdjacencyTable { + type Item = (N, Vec<(N, V)>); + + type IntoIter = hash_map::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/graph/src/cmp/edge.rs b/graph/src/cmp/edge.rs new file mode 100644 index 0000000..fdc5b86 --- /dev/null +++ b/graph/src/cmp/edge.rs @@ -0,0 +1,19 @@ +/* + Appellation: edge + Contrib: FL03 + Description: an edge consists of two nodes and an optional edge value +*/ +use super::Node; + +pub trait Related {} + +pub struct Edge(N, N, V); + +impl Edge { + pub fn pair(self) -> (N, N) { + (self.0, self.1) + } + pub fn weight(self) -> V { + self.2 + } +} diff --git a/graph/src/cmp/mod.rs b/graph/src/cmp/mod.rs new file mode 100644 index 0000000..5cf945f --- /dev/null +++ b/graph/src/cmp/mod.rs @@ -0,0 +1,18 @@ +/* + Appellation: cmp + Contrib: FL03 + Description: components (cmp) for building effecient graph data-structures +*/ +pub use self::{atable::*, edge::*}; + +pub(crate) mod atable; +pub(crate) mod edge; + +/// [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 37731e8..86a1dce 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -1,21 +1,80 @@ /* - Appellation: graph [directed] - Contrib: FL03 - Description: + Appellation: directed + Contrib: FL03 + Description: ... Summary ... */ -use crate::{AdjacencyHashTable, Graphable}; +use super::{ + cmp::{AdjacencyTable, Node}, + Graph, +}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct DirectedGraph { - adjacency_table: AdjacencyHashTable, +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct DirectedGraph { + adjacency_table: AdjacencyTable, } -impl Graphable for DirectedGraph { - fn adjacency_table_mutable(&mut self) -> &mut AdjacencyHashTable { +impl Graph for DirectedGraph { + fn new() -> Self { + Self { + adjacency_table: AdjacencyTable::new(), + } + } + fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable { &mut self.adjacency_table } - fn adjacency_table(&self) -> &AdjacencyHashTable { + fn adjacency_table(&self) -> &AdjacencyTable { &self.adjacency_table } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_add_node() { + let mut graph = DirectedGraph::<&str, i64>::new(); + graph.add_node("a"); + graph.add_node("b"); + graph.add_node("c"); + assert_eq!(graph.nodes(), ["a", "b", "c"].iter().cloned().collect()); + } + + #[test] + fn test_add_edge() { + let mut graph = DirectedGraph::new(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("c", "a", 7)); + graph.add_edge(("b", "c", 10)); + + let expected_edges = [("a", "b", 5), ("c", "a", 7), ("b", "c", 10)]; + for edge in expected_edges.iter() { + assert_eq!(graph.edges().contains(edge), true); + } + } + + #[test] + fn test_neighbours() { + let mut graph = DirectedGraph::new(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + assert_eq!(graph.neighbours("a").unwrap(), &vec![("b", 5)]); + } + + #[test] + fn test_contains() { + let mut graph = DirectedGraph::<&str, i64>::new(); + graph.add_node("a"); + graph.add_node("b"); + graph.add_node("c"); + assert_eq!(graph.contains("a"), true); + assert_eq!(graph.contains("b"), true); + assert_eq!(graph.contains("c"), true); + assert_eq!(graph.contains("d"), false); + } +} diff --git a/graph/src/errors.rs b/graph/src/errors.rs index f0d6d16..4519fe3 100644 --- a/graph/src/errors.rs +++ b/graph/src/errors.rs @@ -1,22 +1,33 @@ /* - Appellation: errors - Contrib: FL03 - Description: + Appellation: errors + Contrib: FL03 + Description: ... Summary ... */ use serde::{Deserialize, Serialize}; +use smart_default::SmartDefault; +use strum::{Display, EnumString, EnumVariantNames}; #[derive( - Copy, Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, + Clone, + Copy, + Debug, + Deserialize, + Display, + EnumString, + EnumVariantNames, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize, + SmartDefault, )] -pub enum Errors { +#[strum(serialize_all = "snake_case")] +pub enum GraphError { + NodeInGraph, #[default] NodeNotInGraph, } -impl std::fmt::Display for Errors { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self.clone() { - Self::NodeNotInGraph => write!(f, "accessing a node that is not in the graph"), - } - } -} +impl std::error::Error for GraphError {} diff --git a/graph/src/graphable.rs b/graph/src/graphable.rs deleted file mode 100644 index 9bfa1f9..0000000 --- a/graph/src/graphable.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - Appellation: specs - Contrib: FL03 - Description: -*/ -use crate::{AdjacencyHashTable, Errors}; -use std::collections::HashSet; - -pub trait Graphable: Clone + Default { - fn add_edge(&mut self, edge: (&str, &str, i32)) { - self.add_node(edge.0); - self.add_node(edge.1); - - self.adjacency_table_mutable() - .entry(edge.0.to_string()) - .and_modify(|e| { - e.push((edge.1.to_string(), edge.2)); - }); - } - fn add_node(&mut self, node: &str) -> bool { - if !self.contains(node) { - self.adjacency_table_mutable() - .insert((*node).to_string(), Vec::new()); - return true; - } - false - } - fn adjacency_table(&self) -> &AdjacencyHashTable; - fn adjacency_table_mutable(&mut self) -> &mut AdjacencyHashTable; - fn contains(&self, node: &str) -> bool { - self.adjacency_table().get(node).is_some() - } - fn edges(&self) -> Vec<(&String, &String, i32)> { - let mut edges = Vec::new(); - for (from_node, from_node_neighbours) in self.adjacency_table() { - for (to_node, weight) in from_node_neighbours { - edges.push((from_node, to_node, *weight)); - } - } - edges - } - fn neighbours(&self, node: &str) -> Result<&Vec<(String, i32)>, Errors> { - match self.adjacency_table().get(node) { - None => Err(Errors::NodeNotInGraph), - Some(i) => Ok(i), - } - } - fn nodes(&self) -> HashSet<&String> { - self.adjacency_table().keys().collect() - } -} diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 0da79c3..b55eb64 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -1,15 +1,69 @@ /* - Appellation: algae-graph - Contrib: FL03 - Description: + Appellation: graphs + 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. */ -#[doc(inline)] -pub use self::{errors::*, graphable::*, primitives::*, utils::*}; +pub use self::{directed::*, undirected::*}; -pub(crate) mod errors; -pub(crate) mod graphable; -pub(crate) mod primitives; -pub(crate) mod utils; +pub(crate) mod directed; +pub(crate) mod undirected; -pub mod directed; -pub mod undirected; +pub mod cmp; +pub mod errors; + +use cmp::{AdjacencyTable, Node}; +use errors::GraphError; +use std::collections::HashSet; + +/// [Graph] describes the basic operations of a graph data-structure +pub trait Graph: Clone { + fn new() -> Self; + /// [Graph::add_edge] + fn add_edge(&mut self, edge: (N, N, V)) { + self.add_node(edge.0.clone()); + self.add_node(edge.1.clone()); + + self.adjacency_table_mut().entry(edge.0).and_modify(|e| { + e.push((edge.1, edge.2)); + }); + } + /// [Graph::add_node] + fn add_node(&mut self, node: N) -> bool { + match self.adjacency_table().get(&node) { + None => { + self.adjacency_table_mut().insert(node, Vec::new()); + true + } + _ => false, + } + } + /// [Graph::adjacency_table_mut] + fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable; + /// [Graph::adjacency_table] + fn adjacency_table(&self) -> &AdjacencyTable; + /// [Graph::contains] + fn contains(&self, node: N) -> bool { + self.adjacency_table().get(&node).is_some() + } + /// [Graph::edges] + fn edges(&self) -> Vec<(N, N, V)> { + let mut edges = Vec::new(); + for (from_node, from_node_neighbours) in self.adjacency_table().clone() { + for (to_node, weight) in from_node_neighbours { + edges.push((from_node.clone(), to_node, weight)); + } + } + edges + } + /// [Graph::neighbours] + fn neighbours(&self, node: N) -> Result<&Vec<(N, V)>, GraphError> { + match self.adjacency_table().get(&node) { + None => Err(GraphError::NodeNotInGraph), + Some(i) => Ok(i), + } + } + /// [Graph::nodes] + fn nodes(&self) -> HashSet { + self.adjacency_table().keys().cloned().collect() + } +} diff --git a/graph/src/primitives.rs b/graph/src/primitives.rs deleted file mode 100644 index 891af07..0000000 --- a/graph/src/primitives.rs +++ /dev/null @@ -1,14 +0,0 @@ -/* - Appellation: primitives - Contrib: FL03 - Description: -*/ -pub use self::{constants::*, types::*}; - -pub(crate) mod constants {} - -pub(crate) mod types { - use std::collections::HashMap; - - pub type AdjacencyHashTable = HashMap>; -} diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index ebcd1f5..47e0bd9 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -1,36 +1,78 @@ /* - Appellation: graph - Contrib: FL03 - Description: + Appellation: undirected + Contrib: FL03 + Description: ... Summary ... */ -use crate::{AdjacencyHashTable, Graphable}; +use super::{ + cmp::{AdjacencyTable, Node}, + Graph, +}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct UndirectedGraph { - adjacency_table: AdjacencyHashTable, +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct UndirectedGraph { + adjacency_table: AdjacencyTable, } -impl Graphable for UndirectedGraph { - fn adjacency_table_mutable(&mut self) -> &mut AdjacencyHashTable { +impl Graph for UndirectedGraph { + fn new() -> Self { + Self { + adjacency_table: AdjacencyTable::new(), + } + } + fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable { &mut self.adjacency_table } - fn adjacency_table(&self) -> &AdjacencyHashTable { + fn adjacency_table(&self) -> &AdjacencyTable { &self.adjacency_table } - fn add_edge(&mut self, edge: (&str, &str, i32)) { - self.add_node(edge.0); - self.add_node(edge.1); - - self.adjacency_table - .entry(edge.0.to_string()) - .and_modify(|e| { - e.push((edge.1.to_string(), edge.2)); - }); - self.adjacency_table - .entry(edge.1.to_string()) - .and_modify(|e| { - e.push((edge.0.to_string(), edge.2)); - }); + fn add_edge(&mut self, edge: (N, N, V)) { + self.add_node(edge.0.clone()); + self.add_node(edge.1.clone()); + + self.adjacency_table.entry(edge.0.clone()).and_modify(|e| { + e.push((edge.1.clone(), edge.2.clone())); + }); + self.adjacency_table.entry(edge.1).and_modify(|e| { + e.push((edge.0, edge.2)); + }); + } +} + +#[cfg(test)] +mod tests { + use super::Graph; + use super::UndirectedGraph; + + #[test] + fn test_add_edge() { + let mut graph = UndirectedGraph::new(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + let expected_edges = [ + ("a", "b", 5), + ("b", "a", 5), + ("c", "a", 7), + ("a", "c", 7), + ("b", "c", 10), + ("c", "b", 10), + ]; + for edge in expected_edges.iter() { + assert_eq!(graph.edges().contains(edge), true); + } + } + + #[test] + fn test_neighbours() { + let mut graph = UndirectedGraph::new(); + + graph.add_edge(("a", "b", 5)); + graph.add_edge(("b", "c", 10)); + graph.add_edge(("c", "a", 7)); + + assert_eq!(graph.neighbours("a").unwrap(), &vec![("b", 5), ("c", 7)]); } } diff --git a/graph/src/utils.rs b/graph/src/utils.rs deleted file mode 100644 index 1cfb395..0000000 --- a/graph/src/utils.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* - Appellation: utils - Contrib: FL03 - Description: -*/ diff --git a/graph/tests/directed.rs b/graph/tests/directed.rs deleted file mode 100644 index ac12485..0000000 --- a/graph/tests/directed.rs +++ /dev/null @@ -1,64 +0,0 @@ -#[cfg(test)] -use algae_graph::{directed::DirectedGraph, Graphable}; - -#[test] -fn test_add_node() { - let tmp = ["a", "b", "c"] - .iter() - .cloned() - .map(String::from) - .collect::>(); - - let mut graph = DirectedGraph::default(); - graph.add_node("a"); - graph.add_node("b"); - graph.add_node("c"); - assert_eq!( - graph.nodes(), - [&tmp[0], &tmp[1], &tmp[2]].iter().cloned().collect() - ); -} - -#[test] -fn test_add_edge() { - let mut graph = DirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("c", "a", 7)); - graph.add_edge(("b", "c", 10)); - - let expected_edges = [ - (&String::from("a"), &String::from("b"), 5), - (&String::from("c"), &String::from("a"), 7), - (&String::from("b"), &String::from("c"), 10), - ]; - for edge in expected_edges.iter() { - assert!(graph.edges().contains(edge)); - } -} - -#[test] -fn test_neighbours() { - let mut graph = DirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); - - assert_eq!( - graph.neighbours("a").unwrap(), - &vec![(String::from("b"), 5)] - ); -} - -#[test] -fn test_contains() { - let mut graph = DirectedGraph::default(); - graph.add_node("a"); - graph.add_node("b"); - graph.add_node("c"); - assert!(graph.contains("a")); - assert!(graph.contains("b")); - assert!(graph.contains("c")); - assert!(!graph.contains("d")); -} diff --git a/graph/tests/undirected.rs b/graph/tests/undirected.rs deleted file mode 100644 index 89bf751..0000000 --- a/graph/tests/undirected.rs +++ /dev/null @@ -1,37 +0,0 @@ -#[cfg(test)] -use algae_graph::{undirected::UndirectedGraph, Graphable}; - -#[test] -fn test_add_edge() { - let mut graph = UndirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); - - let expected_edges = [ - (&String::from("a"), &String::from("b"), 5), - (&String::from("b"), &String::from("a"), 5), - (&String::from("c"), &String::from("a"), 7), - (&String::from("a"), &String::from("c"), 7), - (&String::from("b"), &String::from("c"), 10), - (&String::from("c"), &String::from("b"), 10), - ]; - for edge in expected_edges.iter() { - assert!(graph.edges().contains(edge)); - } -} - -#[test] -fn test_neighbours() { - let mut graph = UndirectedGraph::default(); - - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); - - assert_eq!( - graph.neighbours("a").unwrap(), - &vec![(String::from("b"), 5), (String::from("c"), 7)] - ); -} diff --git a/merkle/Cargo.toml b/merkle/Cargo.toml index 02ef07f..9c8af81 100644 --- a/merkle/Cargo.toml +++ b/merkle/Cargo.toml @@ -25,16 +25,18 @@ crate-type = ["cdylib", "rlib"] test = true [dependencies] -decanter.workspace = true -# scsys = { features = ["full"], version = "0.1.40" } - anyhow = "1" hex = "0.4" -itertools = "0.10" log = "0.4" ring = { features = ["wasm32_c"], version = "0.16" } -serde = { features = ["derive"], version = "1" } -serde_json = "1" + +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] hex-literal = "0.3" diff --git a/mmr/Cargo.toml b/mmr/Cargo.toml index bb90ce4..2d1be0a 100644 --- a/mmr/Cargo.toml +++ b/mmr/Cargo.toml @@ -25,16 +25,18 @@ test = true [dependencies] decanter.workspace = true +itertools.workspace = true +serde.workspace = true +serde_json.workspace = true +smart-default.workspace = true +strum.workspace = true anyhow = "1" digest = "0.10" hex = "0.4" -itertools = "0.10" -serde = { features = ["derive"], version = "1" } -serde_json = "1" [dev-dependencies] -scsys = { default-features = false, features = ["gen"], version = "0.1.41" } +scsys.workspace = true hex-literal = "0.3.4" vrf = "0.2.4" From 0a895c734a00a68a55526870a6418d5d0001fa4f Mon Sep 17 00:00:00 2001 From: FL03 Date: Sun, 12 Mar 2023 10:04:55 -0500 Subject: [PATCH 5/9] Update --- graph/src/directed.rs | 8 ++++++++ graph/src/undirected.rs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/graph/src/directed.rs b/graph/src/directed.rs index 86a1dce..8c4a945 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -28,6 +28,14 @@ impl Graph for DirectedGraph { } } +impl From> for DirectedGraph { + fn from(adjacency_table: AdjacencyTable) -> Self { + Self { + adjacency_table + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index 47e0bd9..f144286 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -39,6 +39,14 @@ impl Graph for UndirectedGraph { } } +impl From> for UndirectedGraph { + fn from(adjacency_table: AdjacencyTable) -> Self { + Self { + adjacency_table + } + } +} + #[cfg(test)] mod tests { use super::Graph; From 04a2d38b0564a677e9313b5dba146f1b8a2bf11d Mon Sep 17 00:00:00 2001 From: FL03 Date: Sun, 12 Mar 2023 12:36:43 -0500 Subject: [PATCH 6/9] Update --- graph/src/directed.rs | 8 ++++---- graph/src/lib.rs | 6 ++++++ graph/src/undirected.rs | 7 +++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/graph/src/directed.rs b/graph/src/directed.rs index 8c4a945..fb64d16 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -5,7 +5,7 @@ */ use super::{ cmp::{AdjacencyTable, Node}, - Graph, + Graph, Subgraph, }; use serde::{Deserialize, Serialize}; @@ -28,11 +28,11 @@ impl Graph for DirectedGraph { } } +impl Subgraph for DirectedGraph {} + impl From> for DirectedGraph { fn from(adjacency_table: AdjacencyTable) -> Self { - Self { - adjacency_table - } + Self { adjacency_table } } } diff --git a/graph/src/lib.rs b/graph/src/lib.rs index b55eb64..580129a 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -67,3 +67,9 @@ pub trait Graph: Clone { self.adjacency_table().keys().cloned().collect() } } + +pub trait Subgraph: Graph { + fn is_subgraph(&self, graph: impl Graph) -> bool { + self.nodes().is_subset(&graph.nodes()) + } +} diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index f144286..ae714df 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -5,7 +5,7 @@ */ use super::{ cmp::{AdjacencyTable, Node}, - Graph, + Graph, Subgraph, }; use serde::{Deserialize, Serialize}; @@ -39,11 +39,10 @@ impl Graph for UndirectedGraph { } } +impl Subgraph for UndirectedGraph {} impl From> for UndirectedGraph { fn from(adjacency_table: AdjacencyTable) -> Self { - Self { - adjacency_table - } + Self { adjacency_table } } } From a21036204256520ca54b6bbc69b6178d0024c737 Mon Sep 17 00:00:00 2001 From: FL03 Date: Mon, 13 Mar 2023 21:59:15 -0500 Subject: [PATCH 7/9] Update --- graph/src/cmp/atable.rs | 19 +++++++++++++++++++ graph/src/undirected.rs | 1 + 2 files changed, 20 insertions(+) diff --git a/graph/src/cmp/atable.rs b/graph/src/cmp/atable.rs index f3c333c..5a0fab4 100644 --- a/graph/src/cmp/atable.rs +++ b/graph/src/cmp/atable.rs @@ -42,6 +42,9 @@ impl AdjacencyTable { pub fn clear(&mut self) { self.0.clear() } + pub fn contains_key(&self, key: &N) -> bool { + self.0.contains_key(key) + } pub fn drain(&mut self) -> hash_map::Drain<'_, N, Vec<(N, V)>> { self.0.drain() } @@ -54,12 +57,28 @@ impl AdjacencyTable { pub fn get(&self, key: &N) -> Option<&Vec<(N, V)>> { self.0.get(key) } + pub fn get_key_value(&self, key: &N) -> Option<(&N, &Vec<(N, V)>)> { + self.0.get_key_value(key) + } + pub fn get_mut(&mut self, key: &N) -> Option<&mut Vec<(N, V)>> { + self.0.get_mut(key) + } pub fn keys(&self) -> hash_map::Keys> { self.0.keys() } + pub fn len(&self) -> usize { + self.0.len() + } + pub fn table(self) -> HashMap> { self.0 } + pub fn values(&self) -> hash_map::Values> { + self.0.values() + } + pub fn values_mut(&mut self) -> hash_map::ValuesMut> { + self.0.values_mut() + } } impl Extend<(N, Vec<(N, V)>)> for AdjacencyTable { diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index ae714df..ff0da1d 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -40,6 +40,7 @@ impl Graph for UndirectedGraph { } impl Subgraph for UndirectedGraph {} + impl From> for UndirectedGraph { fn from(adjacency_table: AdjacencyTable) -> Self { Self { adjacency_table } From 80a1f234f9d285c4f5719cee0ddb37b3a57e3707 Mon Sep 17 00:00:00 2001 From: FL03 Date: Tue, 14 Mar 2023 20:24:52 -0500 Subject: [PATCH 8/9] Update --- graph/src/cmp/atable.rs | 15 ++++++++- graph/src/cmp/edge.rs | 25 ++++++++++---- graph/src/cmp/mod.rs | 3 +- graph/src/cmp/pair.rs | 15 +++++++++ graph/src/directed.rs | 42 +++++++++++++----------- graph/src/lib.rs | 51 +++++++++++++++++------------ graph/src/undirected.rs | 72 +++++++++++++++++++++++------------------ 7 files changed, 144 insertions(+), 79 deletions(-) create mode 100644 graph/src/cmp/pair.rs diff --git a/graph/src/cmp/atable.rs b/graph/src/cmp/atable.rs index 5a0fab4..e356af0 100644 --- a/graph/src/cmp/atable.rs +++ b/graph/src/cmp/atable.rs @@ -15,7 +15,9 @@ pub trait HashMapLike: { fn new() -> Self; fn capacity(&self) -> usize; - fn clear(&mut self); + 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) @@ -29,6 +31,14 @@ 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>); @@ -36,6 +46,9 @@ impl AdjacencyTable { pub fn new() -> Self { Self(HashMap::new()) } + pub fn with_capacity(capacity: usize) -> Self { + Self(HashMap::with_capacity(capacity)) + } pub fn capacity(&self) -> usize { self.0.capacity() } diff --git a/graph/src/cmp/edge.rs b/graph/src/cmp/edge.rs index fdc5b86..60fe040 100644 --- a/graph/src/cmp/edge.rs +++ b/graph/src/cmp/edge.rs @@ -3,17 +3,28 @@ Contrib: FL03 Description: an edge consists of two nodes and an optional edge value */ -use super::Node; +use super::{Node, Pair}; +use serde::{Deserialize, Serialize}; pub trait Related {} -pub struct Edge(N, N, V); +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Edge(N, N, V); -impl Edge { - pub fn pair(self) -> (N, N) { - (self.0, self.1) +impl Edge { + pub fn new(a: N, b: N, v: V) -> Self { + Self(a, b, v) } - pub fn weight(self) -> V { - self.2 + pub fn pair(&self) -> Pair { + Pair::new(self.0.clone(), self.1.clone()) + } + pub fn value(&self) -> V { + self.2.clone() + } +} + +impl From<(N, N, V)> for Edge { + fn from(data: (N, N, V)) -> Self { + Self(data.0, data.1, data.2) } } diff --git a/graph/src/cmp/mod.rs b/graph/src/cmp/mod.rs index 5cf945f..ad72b42 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::*}; +pub use self::{atable::*, edge::*, pair::*}; pub(crate) mod atable; pub(crate) mod edge; +pub(crate) mod pair; /// [Node] describes compatible vertices of the [super::Graph] pub trait Node: Clone + Eq + std::hash::Hash {} diff --git a/graph/src/cmp/pair.rs b/graph/src/cmp/pair.rs new file mode 100644 index 0000000..0aad2cd --- /dev/null +++ b/graph/src/cmp/pair.rs @@ -0,0 +1,15 @@ +/* + Appellation: pair + Contrib: FL03 + Description: a pair can either be scalar or vector; if vector, than direction matters +*/ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Pair(pub T, pub T); + +impl Pair { + pub fn new(a: T, b: T) -> Self { + Self(a, b) + } +} diff --git a/graph/src/directed.rs b/graph/src/directed.rs index fb64d16..3222d3c 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -10,11 +10,11 @@ use super::{ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct DirectedGraph { +pub struct DirectedGraph { adjacency_table: AdjacencyTable, } -impl Graph for DirectedGraph { +impl Graph for DirectedGraph { fn new() -> Self { Self { adjacency_table: AdjacencyTable::new(), @@ -26,11 +26,16 @@ impl Graph for DirectedGraph { fn adjacency_table(&self) -> &AdjacencyTable { &self.adjacency_table } + fn with_capacity(capacity: usize) -> Self { + Self { + adjacency_table: AdjacencyTable::with_capacity(capacity), + } + } } -impl Subgraph for DirectedGraph {} +impl Subgraph for DirectedGraph {} -impl From> for DirectedGraph { +impl From> for DirectedGraph { fn from(adjacency_table: AdjacencyTable) -> Self { Self { adjacency_table } } @@ -39,6 +44,9 @@ impl From> for DirectedGraph { #[cfg(test)] mod tests { use super::*; + use crate::cmp::Edge; + + const TEST_EDGES: [(&str, &str, usize); 3] = [("a", "b", 5), ("c", "a", 7), ("b", "c", 10)]; #[test] fn test_add_node() { @@ -53,13 +61,11 @@ mod tests { fn test_add_edge() { let mut graph = DirectedGraph::new(); - graph.add_edge(("a", "b", 5)); - graph.add_edge(("c", "a", 7)); - graph.add_edge(("b", "c", 10)); - - let expected_edges = [("a", "b", 5), ("c", "a", 7), ("b", "c", 10)]; - for edge in expected_edges.iter() { - assert_eq!(graph.edges().contains(edge), true); + for i in TEST_EDGES { + graph.add_edge(i.into()); + } + for edge in TEST_EDGES { + assert!(graph.contains_edge(&Edge::from(edge))); } } @@ -67,9 +73,9 @@ mod tests { fn test_neighbours() { let mut graph = DirectedGraph::new(); - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); + for i in TEST_EDGES { + graph.add_edge(i.into()); + } assert_eq!(graph.neighbours("a").unwrap(), &vec![("b", 5)]); } @@ -80,9 +86,9 @@ mod tests { graph.add_node("a"); graph.add_node("b"); graph.add_node("c"); - assert_eq!(graph.contains("a"), true); - assert_eq!(graph.contains("b"), true); - assert_eq!(graph.contains("c"), true); - assert_eq!(graph.contains("d"), false); + assert!(graph.contains_node(&"a")); + assert!(graph.contains_node(&"b")); + assert!(graph.contains_node(&"c")); + assert!(!graph.contains_node(&"d")); } } diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 580129a..5878405 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -11,23 +11,26 @@ pub(crate) mod undirected; pub mod cmp; pub mod errors; -use cmp::{AdjacencyTable, Node}; +use cmp::{AdjacencyTable, Edge, Node}; use errors::GraphError; use std::collections::HashSet; /// [Graph] describes the basic operations of a graph data-structure -pub trait Graph: Clone { +pub trait Graph: Clone { fn new() -> Self; - /// [Graph::add_edge] - fn add_edge(&mut self, edge: (N, N, V)) { - self.add_node(edge.0.clone()); - self.add_node(edge.1.clone()); + /// [Graph::add_edge] inserts a new [Edge] into the graph + fn add_edge(&mut self, edge: Edge) { + let pair = edge.pair(); + self.add_node(pair.0.clone()); + self.add_node(pair.1.clone()); - self.adjacency_table_mut().entry(edge.0).and_modify(|e| { - e.push((edge.1, edge.2)); - }); + self.adjacency_table_mut() + .entry(pair.0.clone()) + .and_modify(|e| { + e.push((pair.1, edge.value())); + }); } - /// [Graph::add_node] + /// [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) { None => { @@ -37,38 +40,44 @@ pub trait Graph: Clone { _ => false, } } - /// [Graph::adjacency_table_mut] + /// [Graph::adjacency_table_mut] returns an owned, mutable instance of the [AdjacencyTable] fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable; - /// [Graph::adjacency_table] + /// [Graph::adjacency_table] returns an owned instance of the [AdjacencyTable] fn adjacency_table(&self) -> &AdjacencyTable; - /// [Graph::contains] - fn contains(&self, node: N) -> bool { - self.adjacency_table().get(&node).is_some() + /// [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::edges] - fn edges(&self) -> Vec<(N, N, V)> { + /// [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::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 (to_node, weight) in from_node_neighbours { - edges.push((from_node.clone(), to_node, weight)); + edges.push((from_node.clone(), to_node, weight).into()); } } edges } - /// [Graph::neighbours] + /// [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) { None => Err(GraphError::NodeNotInGraph), Some(i) => Ok(i), } } - /// [Graph::nodes] + /// [Graph::nodes] returns a cloned [HashSet] of the graph's current [Node]s fn nodes(&self) -> HashSet { self.adjacency_table().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; } -pub trait Subgraph: Graph { +pub trait Subgraph: Graph { fn is_subgraph(&self, graph: impl Graph) -> bool { self.nodes().is_subset(&graph.nodes()) } diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index ff0da1d..c063054 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -4,44 +4,50 @@ Description: ... Summary ... */ use super::{ - cmp::{AdjacencyTable, Node}, + cmp::{AdjacencyTable, Edge, Node}, Graph, Subgraph, }; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct UndirectedGraph { +pub struct UndirectedGraph { adjacency_table: AdjacencyTable, } -impl Graph for UndirectedGraph { +impl Graph for UndirectedGraph { fn new() -> Self { Self { adjacency_table: AdjacencyTable::new(), } } + fn add_edge(&mut self, edge: Edge) { + let pair = edge.pair(); + self.add_node(pair.0.clone()); + self.add_node(pair.1.clone()); + + self.adjacency_table.entry(pair.0.clone()).and_modify(|e| { + e.push((pair.1.clone(), edge.value())); + }); + self.adjacency_table.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 adjacency_table(&self) -> &AdjacencyTable { &self.adjacency_table } - fn add_edge(&mut self, edge: (N, N, V)) { - self.add_node(edge.0.clone()); - self.add_node(edge.1.clone()); - - self.adjacency_table.entry(edge.0.clone()).and_modify(|e| { - e.push((edge.1.clone(), edge.2.clone())); - }); - self.adjacency_table.entry(edge.1).and_modify(|e| { - e.push((edge.0, edge.2)); - }); + fn with_capacity(capacity: usize) -> Self { + Self { + adjacency_table: AdjacencyTable::with_capacity(capacity), + } } } -impl Subgraph for UndirectedGraph {} +impl Subgraph for UndirectedGraph {} -impl From> for UndirectedGraph { +impl From> for UndirectedGraph { fn from(adjacency_table: AdjacencyTable) -> Self { Self { adjacency_table } } @@ -51,25 +57,29 @@ impl From> for UndirectedGraph { mod tests { use super::Graph; use super::UndirectedGraph; + use crate::cmp::Edge; + + const TEST_EDGES: [(&str, &str, usize); 3] = [("a", "b", 5), ("c", "a", 7), ("b", "c", 10)]; + + const EXPECTED: [(&str, &str, usize); 6] = [ + ("a", "b", 5), + ("b", "a", 5), + ("c", "a", 7), + ("a", "c", 7), + ("b", "c", 10), + ("c", "b", 10), + ]; #[test] fn test_add_edge() { let mut graph = UndirectedGraph::new(); - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); + for i in TEST_EDGES { + graph.add_edge(i.into()); + } - let expected_edges = [ - ("a", "b", 5), - ("b", "a", 5), - ("c", "a", 7), - ("a", "c", 7), - ("b", "c", 10), - ("c", "b", 10), - ]; - for edge in expected_edges.iter() { - assert_eq!(graph.edges().contains(edge), true); + for edge in EXPECTED { + assert!(graph.edges().contains(&Edge::from(edge))); } } @@ -77,9 +87,9 @@ mod tests { fn test_neighbours() { let mut graph = UndirectedGraph::new(); - graph.add_edge(("a", "b", 5)); - graph.add_edge(("b", "c", 10)); - graph.add_edge(("c", "a", 7)); + for i in TEST_EDGES { + graph.add_edge(i.into()); + } assert_eq!(graph.neighbours("a").unwrap(), &vec![("b", 5), ("c", 7)]); } From eaf56da884e8ff3df0838f27782793569e0f1b37 Mon Sep 17 00:00:00 2001 From: FL03 Date: Tue, 14 Mar 2023 20:26:45 -0500 Subject: [PATCH 9/9] Update --- graph/src/cmp/edge.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/graph/src/cmp/edge.rs b/graph/src/cmp/edge.rs index 60fe040..19f34b5 100644 --- a/graph/src/cmp/edge.rs +++ b/graph/src/cmp/edge.rs @@ -28,3 +28,9 @@ impl From<(N, N, V)> for Edge { Self(data.0, data.1, data.2) } } + +impl From<(Pair, V)> for Edge { + fn from(data: (Pair, V)) -> Self { + Self(data.0.0, data.0.1, data.1) + } +}