diff --git a/.artifacts/license/APACHE.LICENSE b/.artifacts/license/APACHE.LICENSE index b9f3f83..622e1d9 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 2023 Scattered-Systems, DAO LLC + Copyright 2024 Scattered-Systems, LLC 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/.artifacts/license/MIT.LICENSE b/.artifacts/license/MIT.LICENSE index a72851f..2e7ec0f 100644 --- a/.artifacts/license/MIT.LICENSE +++ b/.artifacts/license/MIT.LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2023 Scattered-Systems, DAO LLC +Copyright (c) 2024 Scattered-Systems, 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 173af4d..bd930de 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,6 +12,14 @@ updates: directory: /algae schedule: interval: daily + - package-ecosystem: cargo + directory: /core + schedule: + interval: daily + - package-ecosystem: cargo + directory: /graph + schedule: + interval: daily - package-ecosystem: cargo directory: /merkle schedule: @@ -20,3 +28,7 @@ updates: directory: /mmr schedule: interval: daily + - package-ecosystem: cargo + directory: /queue + schedule: + interval: daily diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 9a0bec2..50eadf4 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,26 +1,31 @@ name: Clippy +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: - pull_request: - branches: [ main, master, v*.*.*, ] push: - branches-ignore: [ "beta*", "dev*", "next*" ] - tags: [ "nightly*", "v*.*.*" ] + branches: [ main ] + tags: [ nightly*, v*.*.*] release: + repository_dispatch: + types: [ clippy ] schedule: - - cron: "30 9 * * *" # 9:30am UTC + - cron: 30 21 * * 0 # Every Sunday at 9:30PM UTC workflow_dispatch: +permissions: + actions: read + contents: read + security-events: write + jobs: clippy: name: Clippy - permissions: - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status - contents: read - security-events: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust toolchain uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 with: @@ -28,16 +33,16 @@ jobs: toolchain: stable components: clippy override: true - - name: Setup + - name: Setup (langspace) run: cargo install clippy-sarif sarif-fmt - - name: clippy + - name: Analyze run: cargo clippy --all-features --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt continue-on-error: true - - name: Upload analysis results to GitHub - uses: github/codeql-action/upload-sarif@v2 + - name: Upload results + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: rust-clippy-results.sarif - wait-for-processing: true + wait-for-processing: true \ No newline at end of file diff --git a/.github/workflows/crates.yml b/.github/workflows/crates.yml new file mode 100644 index 0000000..e15f13d --- /dev/null +++ b/.github/workflows/crates.yml @@ -0,0 +1,55 @@ +name: Publish + +concurrency: + cancel-in-progress: true + group: ${{ github.event.repository.name }} + +env: + CARGO_TERM_COLOR: always + +on: + release: + types: [ created ] + workflow_dispatch: + inputs: + publish: + default: true + description: 'Publish' + required: true + type: boolean + +permissions: write-all + +jobs: + base: + name: Publish (core) + runs-on: ubuntu-latest + strategy: + matrix: + feature: [ core ] + env: + PACKAGE_NAME: ${{ github.event.repository.name }}-${{ matrix.feature }} + steps: + - uses: actions/checkout@v4 + - name: Publish (${{env.PACKAGE_NAME}}) + run: cargo publish --all-features -v -p ${{ env.PACKAGE_NAME }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + features: + name: Publish (features) + runs-on: ubuntu-latest + strategy: + matrix: + feature: [ graph, merkle, mmr, queue ] + env: + PACKAGE_NAME: ${{ github.event.repository.name }}-${{ matrix.feature }} + steps: + - uses: actions/checkout@v4 + - name: Publish (${{env.PACKAGE_NAME}}) + run: cargo publish --all-features -v -p ${{ env.PACKAGE_NAME }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + publish: + name: Publish (sdk) + needs: features + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Publish (sdk) + run: cargo publish --all-features -v -p ${{ github.event.repository.name }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0c3120b..041d759 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,75 +1,80 @@ name: Rust +concurrency: + cancel-in-progress: false + group: ${{ github.event.repository.name }}-rust + env: CARGO_TERM_COLOR: always on: - pull_request: - branches: [ main, master, v*.*.*, ] push: - branches-ignore: [ "beta*", "dev*", "next*" ] - tags: [ "nightly*", "v*.*.*" ] + branches: [ main ] + tags: [ nightly*, v*.*.*] release: - types: [created] + types: [ created ] + repository_dispatch: + types: [ rust ] schedule: - - cron: "30 9 * * *" # 9:30am UTC + - cron: 30 21 * * 0 # Every Sunday at 9:30PM UTC workflow_dispatch: - inputs: - publish: - default: true - description: 'Publish' - required: true - type: boolean + +permissions: write-all jobs: build: - name: Build and Test + name: Build strategy: matrix: platform: [ ubuntu-latest ] + target: [ wasm32-unknown-unknown, wasm32-wasi ] toolchain: [ stable, nightly ] runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: setup (langspace) run: | rustup update rustup default ${{ matrix.toolchain }} + - name: Set target (${{ matrix.target }}) + if: matrix.toolchain == 'nightly' + run: rustup target add ${{ matrix.target }} - name: Build - run: cargo build --release -v --workspace + run: cargo build -r -v --workspace - name: Cache build uses: actions/cache@v3 with: + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} path: | ~/.cargo/registry ~/.cargo/git target/release - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Test - run: cargo test --all --release -v - - name: Bench - if: matrix.toolchain == 'nightly' - run: cargo bench --all -v - features: - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && github.event_name == 'release' && github.event.action == 'created' || github.event.inputs.publish == 'true' - name: Publish (features) + test: + name: Test needs: build - runs-on: ubuntu-latest strategy: matrix: - feature: [ "graph", "merkle", "mmr" ] - env: - PACKAGE_NAME: ${{ github.event.repository.name }}-${{ matrix.feature }} + platform: [ ubuntu-latest ] + target: [ wasm32-unknown-unknown, wasm32-wasi ] + toolchain: [ stable, nightly ] + runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v3 - - name: Publish (${{ env.PACKAGE_NAME }}) - run: cargo publish --all-features -v -p ${{ env.PACKAGE_NAME }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} - publish: - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && github.event_name == 'release' && github.event.action == 'created' || github.event.inputs.publish == 'true' - name: Publish (sdk) - needs: features + - uses: actions/checkout@v4 + - name: setup (langspace) + run: | + rustup update + rustup default ${{ matrix.toolchain }} + - name: Test + run: cargo test --all-features -r -v --workspace + benchmark: + name: Bench + needs: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Publish (${{ github.event.repository.name }}) - run: cargo publish --all-features -v -p ${{ github.event.repository.name }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file + - uses: actions/checkout@v4 + - name: setup (langspace) + run: | + rustup update + rustup default nightly + - name: Bench + run: cargo bench --features full -v --workspace diff --git a/.gitignore b/.gitignore index f77dcea..e21ff3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,20 @@ +# Artifacts + +### Cache +**/.artifacts/cache/ +**/.docker/cache/ + +### Data +**/.artifacts/data/ +**/.docker/data/ + +### Logs +**/.artifacts/logs/ +**/.docker/logs/ + +*.log +*.log.* + # Config **/config.* **/*.config.* @@ -5,27 +22,46 @@ **/*.env **/*.env.* -# Directories -**/__pycache__/ -**/__sapper__/ +### Exceptions +!**/default.config.* +!**/*.env.example +!**/*.config.js +!**/*.config.cjs -**/.DS_STORE/ -**/.artifacts/data/ -**/.docker/data/ +# Development + +### IDEs +**/.atom/ **/.idea/ -**/.pytest_cache/ -**/.svelte-kit/ **/.vscode/ +# Languages + +### Node **/build/ **/debug/ **/dist/ **/env/ **/node_modules/ -**/target/ + +### Python +**/__pycache__/ **/venv/ -# File Extensions +*.pyc +*.pyo + +### Rust +**/target/ + +**/Cargo.lock + +*.bk +*.bk.* + +# Misc + +### File Extensions **/*.bk **/*.bk.* @@ -49,10 +85,4 @@ **/*.whl.* **/*.zip -**/*.zip.* - -# Exceptions -!**/default.config.* -!**/*.env.example -!**/*.config.js -!**/*.config.cjs +**/*.zip.* \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index d55e01b..d0afdca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,17 +8,16 @@ keywords = ["algorithms", "data-structures"] license = "Apache-2.0" readme = "README.md" repository = "https://github.com/scattered-systems/algae" -version = "0.1.19" +version = "0.1.20" [workspace.dependencies] -decanter = { features = ["derive", "wasm"], version = "0.1.5" } + +# decanter = { features = ["derive"], git = "https://github.com/FL03/decanter", branch = "v0.1.7", version = "0.1.7" } anyhow = "1" -itertools = "0.10" -serde = { features = ["derive"], version = "1" } -serde_json = "1" -smart-default = "0.6" -strum = { features = ["derive"], version = "0.24" } +itertools = "0.12" +smart-default = "0.7" +strum = { features = ["derive"], version = "0.26" } [workspace] default-members = [ @@ -26,12 +25,16 @@ default-members = [ ] members = [ - "algae", + "algae", + "core", "graph", "merkle", - "mmr" + "mmr", + "queue", ] +resolver = "2" + [profile.dev] codegen-units = 256 debug = true @@ -50,4 +53,4 @@ incremental = false lto = false panic = "unwind" rpath = false -opt-level = "z" \ No newline at end of file +opt-level = "z" diff --git a/LICENSE b/LICENSE index f25b2b1..622e1d9 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2023 Joe McCain III + Copyright 2024 Scattered-Systems, 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/README.md b/README.md index 1d0f81a..3be349a 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,21 @@ Welcome to algae, a collection of optimized data-structures and algorithms intended for use within blockchain environments. -## Developers +## Getting Started -### Getting Started +### Build from the source +#### _Clone the repository_ + +```bash git clone https://github.com/FL03/algae +``` -#### Testing +#### _Build the project_ +```bash cargo build --release - cargo test --all-features --release +``` ## Contributors diff --git a/SECURITY.md b/SECURITY.md index 0f79aff..862d741 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,9 +6,10 @@ Checkout the current and supported packages below | Package | Current | Supported | |---------|---------|-----------| -| algae | 0.1.9 | <=0.1.9 | +| algae | 0.1.12 | <=0.1.12 | ## Reporting a Vulnerability + Email me at j3mccain@gmail.com to report any vulnerabilities. [Website](https://pzzld.eth.link/) \ No newline at end of file diff --git a/algae/Cargo.toml b/algae/Cargo.toml index a60f035..92545c5 100644 --- a/algae/Cargo.toml +++ b/algae/Cargo.toml @@ -12,32 +12,64 @@ repository.workspace = true version.workspace = true [features] -default = ["core", "graph", "merkle", "mmr"] +default = [ + "ds", +] + full = [ - "core", + "ds", + "serde", +] + +ds = [ "graph", - "merkle", - "mmr", + "merkle", + "mmr", + "queue", ] -core = [] -graph = ["algae-graph"] -merkle = ["algae-merkle"] -mmr = ["algae-mmr"] +graph = [ + "dep:algae-graph" +] +merkle = [ + "dep:algae-merkle" +] +mmr = [ + "dep:algae-mmr" +] + +queue = [ + "dep:algae-queue" +] wasm = [] +serde = [ + "algae-core/serde", + "algae-graph/serde", + "algae-merkle/serde", + "algae-queue/serde", +] + [lib] bench = false crate-type = ["cdylib", "rlib"] +doctest = true test = true +[[example]] +name = "graph" +required-features = ["graph"] + + [build-dependencies] [dependencies] -algae-graph = { features = [], optional = true, path = "../graph", version = "0.1.19" } -algae-merkle = { features = [], optional = true, path = "../merkle", version = "0.1.19" } -algae-mmr = { features = [], optional = true, path = "../mmr", version = "0.1.19" } +algae-core = { path = "../core", version = "0.1.20" } +algae-graph = { optional = true, path = "../graph", version = "0.1.20" } +algae-merkle = { optional = true, path = "../merkle", version = "0.1.20" } +algae-mmr = { optional = true, path = "../mmr", version = "0.1.20" } +algae-queue = { optional = true, path = "../queue", version = "0.1.20" } [dev-dependencies] diff --git a/algae/benches/lists.rs b/algae/benches/lists.rs new file mode 100644 index 0000000..5cf136d --- /dev/null +++ b/algae/benches/lists.rs @@ -0,0 +1,21 @@ +// lists.rs +#![feature(test)] + +extern crate test; +use algae::list::linked::singly::*; +use test::Bencher; + +pub const BENCH_SIZE: usize = 10; + +#[bench] +fn basic_bench(b: &mut Bencher) { + let mut list = SinglyLinkedList::new(); + + assert_eq!(list.pop(), None); + + b.iter(|| { + for i in 0..BENCH_SIZE { + list.push(i); + } + }); +} diff --git a/algae/examples/graph.rs b/algae/examples/graph.rs new file mode 100644 index 0000000..f7edd25 --- /dev/null +++ b/algae/examples/graph.rs @@ -0,0 +1,9 @@ +/* + Appellation: graph + Contrib: FL03 +*/ +extern crate algae; + +fn main() { + println!("Hello, world!"); +} diff --git a/algae/src/lib.rs b/algae/src/lib.rs index f48e1e2..058a771 100644 --- a/algae/src/lib.rs +++ b/algae/src/lib.rs @@ -1,23 +1,32 @@ /* Appellation: algae Contrib: FL03 - Description: - Algae is a comprehensive collection of algorithms and data-structures */ +//! # Algae +//! +//! Algae is a comprehensive collection of algorithms and data-structures written in Rust. #[cfg(feature = "graph")] pub use algae_graph as graph; #[cfg(feature = "merkle")] pub use algae_merkle as merkle; #[cfg(feature = "mmr")] pub use algae_mmr as mmr; +#[cfg(feature = "queue")] +pub use algae_queue as queue; +pub use algae_core as core; pub mod list; pub mod prelude { + pub use crate::core::prelude::*; #[cfg(feature = "graph")] - pub use super::graph::*; + pub use algae_graph::graph; #[cfg(feature = "merkle")] - pub use super::merkle::*; + pub use algae_merkle::prelude::*; #[cfg(feature = "mmr")] - pub use super::mmr::*; + pub use algae_mmr::*; + #[cfg(feature = "queue")] + pub use algae_queue::prelude::*; + + pub use crate::list::*; } diff --git a/algae/src/list/linked/mod.rs b/algae/src/list/linked/mod.rs index 5327b5b..14228fb 100644 --- a/algae/src/list/linked/mod.rs +++ b/algae/src/list/linked/mod.rs @@ -1,7 +1,13 @@ /* Appellation: linked Contrib: FL03 - Description: ... Summary ... */ +pub use self::{singly::SinglyLinkedList, utils::*}; -pub mod persistant; +pub mod singly; +pub mod stack; + +pub(crate) mod utils {} + +#[cfg(test)] +mod tests {} diff --git a/algae/src/list/linked/singly/iter.rs b/algae/src/list/linked/singly/iter.rs new file mode 100644 index 0000000..b2316c7 --- /dev/null +++ b/algae/src/list/linked/singly/iter.rs @@ -0,0 +1,95 @@ +/* + Appellation: singly + Contrib: FL03 +*/ +use super::{Node, SinglyLinkedList}; + +pub struct IntoIter(SinglyLinkedList); + +impl IntoIter { + pub fn new(list: SinglyLinkedList) -> Self { + Self(list) + } +} + +pub struct Iter<'a, T> { + pub(crate) next: Option<&'a Node>, +} + +impl<'a, T> Iter<'a, T> { + pub unsafe fn new(list: &'a SinglyLinkedList) -> Self { + Self { + next: list.head().as_ref(), + } + } +} + +pub struct IterMut<'a, T> { + pub(crate) next: Option<&'a mut Node>, +} + +impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + self.0.pop() + } +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + unsafe { + self.next.map(|node| { + self.next = node.link().as_ref(); + node.elem() + }) + } + } +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + unsafe { + self.next.take().map(|node| { + self.next = node.link().as_mut(); + node.elem_mut() + }) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_iter() { + let mut list = SinglyLinkedList::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.iter(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), None); + } + + #[test] + fn iter_mut() { + let mut list = SinglyLinkedList::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.iter_mut(); + assert_eq!(iter.next(), Some(&mut 1)); + assert_eq!(iter.next(), Some(&mut 2)); + assert_eq!(iter.next(), Some(&mut 3)); + assert_eq!(iter.next(), None); + } +} diff --git a/algae/src/list/linked/singly/mod.rs b/algae/src/list/linked/singly/mod.rs new file mode 100644 index 0000000..a3ef8f7 --- /dev/null +++ b/algae/src/list/linked/singly/mod.rs @@ -0,0 +1,13 @@ +/* + Appellation: singly + Contrib: FL03 +*/ +pub use self::{iter::*, store::*, utils::*}; + +pub(crate) mod iter; +pub(crate) mod store; + +pub(crate) mod utils {} + +#[cfg(test)] +mod tests {} diff --git a/algae/src/list/linked/singly/store.rs b/algae/src/list/linked/singly/store.rs new file mode 100644 index 0000000..449e465 --- /dev/null +++ b/algae/src/list/linked/singly/store.rs @@ -0,0 +1,245 @@ +/* + Appellation: store + Contrib: FL03 +*/ +use super::{IntoIter, Iter, IterMut}; +use std::ptr; + +type Link = *mut Node; + +// pub type LinkMut = Option<*mut Node>; + +pub struct SinglyLinkedList { + head: Link, + tail: *mut Node, +} + +impl SinglyLinkedList { + pub fn new() -> Self { + SinglyLinkedList { + head: ptr::null_mut(), + tail: ptr::null_mut(), + } + } + pub fn head(&self) -> Link { + self.head + } + pub fn push(&mut self, elem: T) { + unsafe { + let new_tail = Box::into_raw(Box::new(Node::new(elem, ptr::null_mut()))); + + if self.tail.is_null() { + self.head = new_tail; + } else { + (*self.tail).next = new_tail; + } + // Update the tail + self.tail = new_tail; + } + } + pub fn pop(&mut self) -> Option { + unsafe { + if self.head.is_null() { + None + } else { + let head = Box::from_raw(self.head); + self.head = head.next; + + if self.head.is_null() { + self.tail = ptr::null_mut(); + } + + Some(head.elem) + } + } + } + + pub fn peek(&self) -> Option<&T> { + unsafe { self.head.as_ref().map(|node| &node.elem) } + } + + pub fn peek_mut(&mut self) -> Option<&mut T> { + unsafe { self.head.as_mut().map(|node| &mut node.elem) } + } + + pub fn iter(&self) -> Iter<'_, T> { + unsafe { Iter::new(self) } + } + + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + unsafe { + IterMut { + next: self.head.as_mut(), + } + } + } +} + +impl Drop for SinglyLinkedList { + fn drop(&mut self) { + while let Some(_) = self.pop() {} + } +} + +impl IntoIterator for SinglyLinkedList { + type Item = T; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self) + } +} + +pub struct Node { + elem: T, + next: Link, +} + +impl Node { + pub fn new(elem: T, next: Link) -> Self { + Node { elem, next } + } + + pub fn elem(&self) -> &T { + &self.elem + } + + pub fn elem_mut(&mut self) -> &mut T { + &mut self.elem + } + + pub fn link(&self) -> Link { + self.next + } + + pub unsafe fn next(&self) -> Option<&Node> { + self.next.as_ref() + } + + pub unsafe fn next_mut(&mut self) -> Option<&mut Node> { + self.next.as_mut() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_basics() { + let mut list = SinglyLinkedList::new(); + + // Check empty list behaves right + assert_eq!(list.pop(), None); + + // Populate list + list.push(1); + list.push(2); + list.push(3); + + // Check normal removal + assert_eq!(list.pop(), Some(1)); + assert_eq!(list.pop(), Some(2)); + + // Push some more just to make sure nothing's corrupted + list.push(4); + list.push(5); + + // Check normal removal + assert_eq!(list.pop(), Some(3)); + assert_eq!(list.pop(), Some(4)); + + // Check exhaustion + assert_eq!(list.pop(), Some(5)); + assert_eq!(list.pop(), None); + + // Check the exhaustion case fixed the pointer right + list.push(6); + list.push(7); + + // Check normal removal + assert_eq!(list.pop(), Some(6)); + assert_eq!(list.pop(), Some(7)); + assert_eq!(list.pop(), None); + } + + #[test] + fn test_into_iter() { + let mut list = SinglyLinkedList::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.into_iter(); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), Some(2)); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_iter() { + let mut list = SinglyLinkedList::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.iter(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_iter_mut() { + let mut list = SinglyLinkedList::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.iter_mut(); + assert_eq!(iter.next(), Some(&mut 1)); + assert_eq!(iter.next(), Some(&mut 2)); + assert_eq!(iter.next(), Some(&mut 3)); + assert_eq!(iter.next(), None); + } + + #[test] + fn miri_food() { + let mut list = SinglyLinkedList::new(); + + list.push(1); + list.push(2); + list.push(3); + + assert_eq!(list.pop(), Some(1)); + list.push(4); + assert_eq!(list.pop(), Some(2)); + list.push(5); + + assert_eq!(list.peek(), Some(&3)); + list.push(6); + list.peek_mut().map(|x| *x *= 10); + assert_eq!(list.peek(), Some(&30)); + assert_eq!(list.pop(), Some(30)); + + for elem in list.iter_mut() { + *elem *= 100; + } + + let mut iter = list.iter(); + assert_eq!(iter.next(), Some(&400)); + assert_eq!(iter.next(), Some(&500)); + assert_eq!(iter.next(), Some(&600)); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + + assert_eq!(list.pop(), Some(400)); + list.peek_mut().map(|x| *x *= 10); + assert_eq!(list.peek(), Some(&5000)); + list.push(7); + + // Drop it on the ground and let the dtor exercise itself + } +} diff --git a/algae/src/list/linked/persistant.rs b/algae/src/list/linked/stack.rs similarity index 59% rename from algae/src/list/linked/persistant.rs rename to algae/src/list/linked/stack.rs index 54ef537..51adbc1 100644 --- a/algae/src/list/linked/persistant.rs +++ b/algae/src/list/linked/stack.rs @@ -1,48 +1,42 @@ /* - Appellation: persistant + Appellation: stack Contrib: FL03 - Description: ... Summary ... */ use std::rc::Rc; type Link = Option>>; +pub struct Stack { + head: Link, +} + 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 { +impl Stack { pub fn new() -> Self { - Self::from(None) + Stack { head: None } } - pub fn prepend(&self, elem: T) -> Self { - SLPList::from(Some(Rc::new(Node::new(elem, self.head.clone())))) + + pub fn prepend(&self, elem: T) -> Stack { + Stack { + head: Some(Rc::new(Node { + elem: elem, + next: self.head.clone(), + })), + } } - pub fn tail(&self) -> Self { - SLPList::from(self.head.as_ref().and_then(|node| node.link().clone())) + pub fn tail(&self) -> Stack { + Stack { + head: self.head.as_ref().and_then(|node| node.next.clone()), + } } pub fn head(&self) -> Option<&T> { - self.head.as_ref().map(|node| node.data()) + self.head.as_ref().map(|node| &node.elem) } pub fn iter(&self) -> Iter<'_, T> { @@ -52,19 +46,7 @@ impl SLPList { } } -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 { +impl Drop for Stack { fn drop(&mut self) { let mut head = self.head.take(); while let Some(node) = head { @@ -94,11 +76,11 @@ impl<'a, T> Iterator for Iter<'a, T> { #[cfg(test)] mod test { - use super::*; + use super::Stack; #[test] - fn test_linked_list() { - let list = SLPList::default(); + fn basics() { + let list = Stack::new(); assert_eq!(list.head(), None); let list = list.prepend(1).prepend(2).prepend(3); @@ -119,8 +101,8 @@ mod test { } #[test] - fn test_linked_list_iter() { - let list = SLPList::default().prepend(1).prepend(2).prepend(3); + fn iter() { + let list = Stack::new().prepend(1).prepend(2).prepend(3); let mut iter = list.iter(); assert_eq!(iter.next(), Some(&3)); diff --git a/algae/src/list/mod.rs b/algae/src/list/mod.rs index 7f6e183..97dad5b 100644 --- a/algae/src/list/mod.rs +++ b/algae/src/list/mod.rs @@ -1,7 +1,10 @@ /* Appellation: list Contrib: FL03 - Description: ... Summary ... */ +//! # Lists +//! +//! This module contains several 'list' data structures, +//! primarily highlighting singly linked lists pub mod linked; diff --git a/algae/tests/default.rs b/algae/tests/default.rs index 7dee5fa..5f26546 100644 --- a/algae/tests/default.rs +++ b/algae/tests/default.rs @@ -1,6 +1,17 @@ +/* + Appellation: default + Contrib: FL03 +*/ #[cfg(test)] + +fn addition(a: A, b: B) -> C +where + A: std::ops::Add, +{ + a + b +} + #[test] -fn lib_compiles() { - let f = |i| i + 1; - assert_eq!(f(10), 11) +fn compiles() { + assert_eq!(addition(10, 1), 11) } diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 0000000..3fd4559 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,39 @@ +[package] +authors.workspace = true +categories.workspace = true +description.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "algae-core" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[features] +default = [] + +serde = [ + "dep:serde", + "serde-ext" +] + +serde-ext = [ + "dep:serde_json" +] + +[dependencies] +serde = { optional = true, features = ["derive"], version = "1" } +serde_json = { optional = true, version = "1" } + + +[dev-dependencies] + +[package.metadata.docs.rs] +all-features = true +rustc-args = ["--cfg", "docsrs"] + +[target.wasm32-unknown-unknown] + +[target.wasm32-wasi] diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 0000000..2b603db --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,9 @@ +/* + Appellation: algae-core + Contrib: FL03 +*/ +//! # Algae Core +//! +//! Algae is a comprehensive collection of algorithms and data-structures written in Rust. + +pub mod prelude {} diff --git a/core/tests/default.rs b/core/tests/default.rs new file mode 100644 index 0000000..5f26546 --- /dev/null +++ b/core/tests/default.rs @@ -0,0 +1,17 @@ +/* + Appellation: default + Contrib: FL03 +*/ +#[cfg(test)] + +fn addition(a: A, b: B) -> C +where + A: std::ops::Add, +{ + a + b +} + +#[test] +fn compiles() { + assert_eq!(addition(10, 1), 11) +} diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 2e5efce..b7bf5b0 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -16,16 +16,27 @@ default = [] wasm = [] +serde = [ + "dep:serde", + "serde-ext" +] + +serde-ext = [ + "dep:serde_json" +] + [lib] +bench = true crate-type = ["cdylib", "rlib"] +doctest = true +test = true [build-dependencies] [dependencies] -decanter.workspace = true itertools.workspace = true -serde.workspace = true -serde_json.workspace = true +serde = { optional = true, features = ["derive"], version = "1" } +serde_json = { optional = true, version = "1" } smart-default.workspace = true strum.workspace = true diff --git a/algae/benches/graphs.rs b/graph/benches/graphs.rs similarity index 89% rename from algae/benches/graphs.rs rename to graph/benches/graphs.rs index 8e4d12c..e788538 100644 --- a/algae/benches/graphs.rs +++ b/graph/benches/graphs.rs @@ -2,7 +2,7 @@ #![feature(test)] extern crate test; -use algae::graph::{DirectedGraph, Edge, Graph,}; +use algae_graph::prelude::{DirectedGraph, Edge, Graph}; use test::Bencher; const TEST_EDGES: [(&str, &str, usize); 5] = [ diff --git a/graph/src/cmp/edge.rs b/graph/src/cmp/edge.rs index ac797f5..670d5db 100644 --- a/graph/src/cmp/edge.rs +++ b/graph/src/cmp/edge.rs @@ -3,16 +3,22 @@ Contrib: FL03 Description: an edge consists of two nodes and an optional edge value */ +//! # Edge +//! +//! use super::Pair; -use crate::Node; +use crate::{Node, Weight}; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; pub trait Related {} -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct Edge where N: Node, + V: Weight, { pair: Pair, weight: V, @@ -21,6 +27,7 @@ where impl Edge where N: Node, + V: Weight, { pub fn new(a: N, b: N, weight: V) -> Self { Self { @@ -36,9 +43,30 @@ where } } +impl AsMut> for Edge +where + N: Node, + V: Weight, +{ + fn as_mut(&mut self) -> &mut Pair { + &mut self.pair + } +} + +impl AsRef> for Edge +where + N: Node, + V: Weight, +{ + fn as_ref(&self) -> &Pair { + &self.pair + } +} + impl From<(N, N, V)> for Edge where N: Node, + V: Weight, { fn from(data: (N, N, V)) -> Self { Self::new(data.0, data.1, data.2) @@ -48,6 +76,7 @@ where impl From<(Pair, V)> for Edge where N: Node, + V: Weight, { fn from(data: (Pair, V)) -> Self { Self { diff --git a/graph/src/cmp/entry.rs b/graph/src/cmp/entry.rs new file mode 100644 index 0000000..ae3b246 --- /dev/null +++ b/graph/src/cmp/entry.rs @@ -0,0 +1,41 @@ +/* + Appellation: entry + Contrib: FL03 +*/ +//! # Entry +//! +//! +use crate::prelude::Contain; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +pub struct Entry { + key: N, + value: Vec<(N, V)>, +} + +impl Entry { + pub fn new(key: N, value: Vec<(N, V)>) -> Self { + Self { key, value } + } + pub fn key(&self) -> &N { + &self.key + } + pub fn value(&self) -> &Vec<(N, V)> { + &self.value + } + pub fn value_mut(&mut self) -> &mut Vec<(N, V)> { + &mut self.value + } +} + +impl Contain for Entry +where + N: PartialEq, +{ + fn contains(&self, node: &N) -> bool { + self.value.iter().any(|(n, _)| n == node) + } +} diff --git a/graph/src/cmp/mod.rs b/graph/src/cmp/mod.rs index d00028c..67da408 100644 --- a/graph/src/cmp/mod.rs +++ b/graph/src/cmp/mod.rs @@ -3,8 +3,33 @@ Contrib: FL03 Description: components (cmp) for building effecient graph data-structures */ -pub use self::{edge::*, neighbors::*, pair::*}; +//! # Components (cmp) +//! +//! +pub use self::{edge::*, pair::*}; -mod edge; -mod neighbors; -mod pair; +pub(crate) mod edge; +pub(crate) mod pair; + +pub mod entry; + +pub(crate) mod prelude { + pub use super::edge::*; + pub use super::entry::*; + pub use super::pair::*; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_edge() { + let a = "a"; + let b = "b"; + let weight = 1; + let edge = Edge::new(a, b, weight); + assert_eq!(edge.pair(), Pair::new(a, b)); + assert_eq!(edge.value(), &weight); + } +} diff --git a/graph/src/cmp/neighbors.rs b/graph/src/cmp/neighbors.rs deleted file mode 100644 index d6269ca..0000000 --- a/graph/src/cmp/neighbors.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* - Appellation: neighbors - Contrib: FL03 - Description: ... summary ... -*/ diff --git a/graph/src/cmp/pair.rs b/graph/src/cmp/pair.rs index a7eb6f7..4d3f013 100644 --- a/graph/src/cmp/pair.rs +++ b/graph/src/cmp/pair.rs @@ -3,25 +3,39 @@ Contrib: FL03 Description: a pair can either be scalar or vector; if vector, than direction matters */ +//! # Pair +use crate::Node; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct Pair(pub T, pub T) where - T: Default; + T: Node; impl Pair where - T: Default, + T: Node, { pub fn new(a: T, b: T) -> Self { Self(a, b) } + pub fn reverse(&mut self) { + std::mem::swap(&mut self.0, &mut self.1) + } + pub fn reverse_mut(&mut self) -> &mut Self { + self.reverse(); + self + } + pub fn pair(&self) -> (T, T) { + (self.0.clone(), self.1.clone()) + } } impl From<(T, T)> for Pair where - T: Default, + T: Node, { fn from(data: (T, T)) -> Self { Self(data.0, data.1) diff --git a/graph/src/directed.rs b/graph/src/directed.rs index 1f21105..57d2982 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -1,13 +1,14 @@ /* Appellation: directed Contrib: FL03 - Description: ... Summary ... */ use crate::{store::AdjacencyTable, Edge, Node, Weight}; use crate::{Contain, Graph, GraphExt, Subgraph}; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct DirectedGraph where N: Node, @@ -135,7 +136,6 @@ where #[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)]; @@ -179,5 +179,6 @@ mod tests { graph.add_node("c"); assert!(graph.contains_all(["a", "b", "c"])); assert!(graph.contains_some(["a", "b", "c", "d"])); + assert!(graph.remove_node(&"a").is_ok()); } } diff --git a/graph/src/errors.rs b/graph/src/errors.rs index 4519fe3..5fff8e2 100644 --- a/graph/src/errors.rs +++ b/graph/src/errors.rs @@ -1,29 +1,35 @@ /* Appellation: errors Contrib: FL03 - Description: ... Summary ... */ +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use smart_default::SmartDefault; -use strum::{Display, EnumString, EnumVariantNames}; +use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; #[derive( Clone, Copy, Debug, - Deserialize, Display, + EnumCount, + EnumIs, + EnumIter, EnumString, - EnumVariantNames, Eq, Hash, Ord, PartialEq, PartialOrd, - Serialize, SmartDefault, + VariantNames, )] #[strum(serialize_all = "snake_case")] +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(rename_all = "snake_case") +)] pub enum GraphError { NodeInGraph, #[default] diff --git a/graph/src/graph/mod.rs b/graph/src/graph/mod.rs index 05d9b74..6a7235d 100644 --- a/graph/src/graph/mod.rs +++ b/graph/src/graph/mod.rs @@ -3,9 +3,10 @@ Contrib: FL03 Description: This module implements an abstract graph data structure */ -use crate::store::Entry; +use crate::cmp::entry::Entry; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use strum::{Display, EnumString, EnumVariantNames}; +use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; pub trait GraphStore: Default { fn capacity(&self) -> usize; @@ -19,19 +20,25 @@ pub trait GraphStore: Default { Copy, Debug, Default, - Deserialize, Display, + EnumCount, + EnumIs, + EnumIter, EnumString, - EnumVariantNames, Eq, Hash, Ord, PartialEq, PartialOrd, - Serialize, + VariantNames, )] #[repr(u8)] #[strum(serialize_all = "snake_case")] +#[cfg_attr( + feature = "serde", + derive(Deserialize, Serialize), + serde(rename_all = "lowercase", untagged) +)] pub enum GraphType { Directed, #[default] diff --git a/graph/src/lib.rs b/graph/src/lib.rs index d90955f..5187815 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -1,27 +1,33 @@ /* 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. */ -pub use self::{cmp::*, directed::*, errors::*, specs::*, undirected::*}; +/// # Graphs +/// +/// This library is dedicated to graphs, explicitly implementing generic directed and undirected data-structures while providing the tools to create new ones. +pub use self::{directed::*, errors::*, specs::*, undirected::*}; -pub(crate) mod cmp; pub(crate) mod directed; mod errors; mod specs; pub(crate) mod undirected; +pub mod cmp; pub mod graph; pub mod search; pub mod store; -use errors::GraphError; +use cmp::Edge; use std::{collections::HashSet, ops::IndexMut}; use store::AdjacencyTable; /// [Graph] describes the basic operations of a graph data-structure pub trait Graph: - Clone + Contain + Contain> + IndexMut> + AsMut> + + Clone + + Contain + + Contain> + + IndexMut> where N: Node, V: Weight, @@ -129,6 +135,45 @@ where visited.len() == self.nodes().len() } + /// [Graph::remove_edge] attempts to remove an edge from the graph + fn remove_edge(&mut self, edge: &Edge) -> Result<(), GraphError> { + match self.store_mut().get_mut(&edge.pair().0) { + Some(edges) => { + edges.retain(|(n, _)| n != &edge.pair().1); + Ok(()) + } + None => Err(GraphError::NodeNotInGraph), + } + } + /// [Graph::remove_edges] attempts to remove several edges from the graph + fn remove_edges( + &mut self, + iter: impl IntoIterator>, + ) -> Result<(), GraphError> { + for i in iter { + self.remove_edge(&i)?; + } + Ok(()) + } + /// [Graph::remove_node] attempts to remove a node from the graph + fn remove_node(&mut self, node: &N) -> Result<(), GraphError> { + if self.contains_node(node) { + self.store_mut().remove(node); + for (_, edges) in self.store_mut().iter_mut() { + edges.retain(|(n, _)| n != node); + } + Ok(()) + } else { + Err(GraphError::NodeNotInGraph) + } + } + /// [Graph::remove_nodes] attempts to remove several nodes from the graph + fn remove_nodes(&mut self, iter: impl IntoIterator) -> Result<(), GraphError> { + for i in iter { + self.remove_node(&i)?; + } + Ok(()) + } /// [Graph::store_mut] returns an owned, mutable instance of the [AdjacencyTable] fn store_mut(&mut self) -> &mut AdjacencyTable; /// [Graph::store] returns an owned instance of the [AdjacencyTable] @@ -163,3 +208,10 @@ where self.nodes().is_subset(&graph.nodes()) } } + +pub mod prelude { + pub use crate::cmp::prelude::*; + pub use crate::directed::*; + pub use crate::errors::*; + pub use crate::{Contain, Graph, Node, Subgraph, Weight}; +} diff --git a/graph/src/search/bfs.rs b/graph/src/search/bfs.rs index 717ad1e..e92a748 100644 --- a/graph/src/search/bfs.rs +++ b/graph/src/search/bfs.rs @@ -1,10 +1,9 @@ /* Appellation: bfs Contrib: FL03 - Description: ... Summary ... */ use super::Searcher; -use crate::{Contain, Graph, Node, Weight}; +use crate::prelude::{Contain, Graph, Node, Weight}; use std::collections::{HashSet, VecDeque}; #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -59,7 +58,8 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{DirectedGraph, Edge}; + use crate::cmp::Edge; + use crate::DirectedGraph; const TEST_EDGES: [(&str, &str, usize); 5] = [ ("a", "b", 5), diff --git a/graph/src/store/matrix.rs b/graph/src/store/matrix.rs index 74825c5..9b5a4f0 100644 --- a/graph/src/store/matrix.rs +++ b/graph/src/store/matrix.rs @@ -1,13 +1,15 @@ /* Appellation: atable Contrib: FL03 - Description: an adjacency table */ +//! # Adjacency Table use crate::Node; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use std::iter::Extend; +use std::ops::{Index, IndexMut}; -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct AdjacencyMatrix where N: Node, @@ -76,7 +78,7 @@ impl Extend<(N, Vec<(N, V)>)> for AdjacencyMatrix } } -impl std::ops::Index for AdjacencyMatrix +impl Index for AdjacencyMatrix where N: Node, V: Clone + PartialEq, @@ -88,7 +90,19 @@ where } } -impl std::ops::IndexMut for AdjacencyMatrix +impl Index<&N> for AdjacencyMatrix +where + N: Node, + V: Clone + PartialEq, +{ + type Output = Vec<(N, V)>; + + fn index(&self, index: &N) -> &Self::Output { + self.get(index).unwrap() + } +} + +impl IndexMut for AdjacencyMatrix where N: Node, V: Clone + PartialEq, diff --git a/graph/src/store/mod.rs b/graph/src/store/mod.rs index 4c9173f..7021c07 100644 --- a/graph/src/store/mod.rs +++ b/graph/src/store/mod.rs @@ -1,51 +1,21 @@ /* Appellation: store Contrib: FL03 - Description: */ +//! # Store pub use self::{matrix::*, table::*}; mod matrix; mod table; -use crate::{Contain, Edge, Node}; -use serde::{Deserialize, Serialize}; -use std::ops::IndexMut; - -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Entry { - key: N, - value: Vec<(N, V)>, -} - -impl Entry { - pub fn new(key: N, value: Vec<(N, V)>) -> Self { - Self { key, value } - } - pub fn key(&self) -> &N { - &self.key - } - pub fn value(&self) -> &Vec<(N, V)> { - &self.value - } - pub fn value_mut(&mut self) -> &mut Vec<(N, V)> { - &mut self.value - } -} +use crate::{Edge, Node, Weight}; -impl Contain<(N, V)> for Entry -where - N: PartialEq, - V: PartialEq, -{ - fn contains(&self, elem: &(N, V)) -> bool { - self.value.contains(elem) - } -} +use std::ops::IndexMut; pub trait Store: Extend> + IndexMut> where N: Node, + V: Weight, { fn clear(&mut self); fn contains_key(&self, key: &N) -> bool; diff --git a/graph/src/store/table.rs b/graph/src/store/table.rs index 627ad08..da26ada 100644 --- a/graph/src/store/table.rs +++ b/graph/src/store/table.rs @@ -1,14 +1,14 @@ /* Appellation: atable Contrib: FL03 - Description: an adjacency table */ use crate::{Node, Weight}; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::collections::{hash_map, HashMap}; -use std::iter::Extend; -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct AdjacencyTable where N: Node, @@ -45,6 +45,15 @@ where pub fn insert(&mut self, key: N, val: Vec<(N, V)>) -> Option> { self.store.insert(key, val) } + pub fn is_empty(&self) -> bool { + self.store.is_empty() + } + pub fn iter(&self) -> hash_map::Iter<'_, N, Vec<(N, V)>> { + self.store.iter() + } + pub fn iter_mut(&mut self) -> hash_map::IterMut<'_, N, Vec<(N, V)>> { + self.store.iter_mut() + } pub fn get(&self, key: &N) -> Option<&Vec<(N, V)>> { self.store.get(key) } @@ -60,6 +69,12 @@ where pub fn len(&self) -> usize { self.store.len() } + pub fn remove(&mut self, key: &N) -> Option> { + self.store.remove(key) + } + pub fn reserve(&mut self, additional: usize) { + self.store.reserve(additional) + } pub fn table(self) -> HashMap> { self.store } diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index 5058d49..6e2c58e 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -5,10 +5,11 @@ */ use crate::{cmp::Edge, store::AdjacencyTable}; use crate::{Contain, Graph, GraphExt, Node, Subgraph, Weight}; - +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct UndirectedGraph where N: Node, diff --git a/graph/tests/default.rs b/graph/tests/default.rs index 7dee5fa..5f26546 100644 --- a/graph/tests/default.rs +++ b/graph/tests/default.rs @@ -1,6 +1,17 @@ +/* + Appellation: default + Contrib: FL03 +*/ #[cfg(test)] + +fn addition(a: A, b: B) -> C +where + A: std::ops::Add, +{ + a + b +} + #[test] -fn lib_compiles() { - let f = |i| i + 1; - assert_eq!(f(10), 11) +fn compiles() { + assert_eq!(addition(10, 1), 11) } diff --git a/merkle/Cargo.toml b/merkle/Cargo.toml index 9bd2259..bcf4c70 100644 --- a/merkle/Cargo.toml +++ b/merkle/Cargo.toml @@ -13,32 +13,43 @@ version.workspace = true [features] default = [ - "ring/default" + ] + wasm = [ - "decanter/wasm", - "ring/wasm32_c" + +] + +serde = [ + "dep:serde", + "serde-ext" +] + +serde-ext = [ + "dep:serde_json" ] [lib] +bench = true crate-type = ["cdylib", "rlib"] test = true [dependencies] anyhow.workspace = true -blake3 ={ features = [], version = "1.3" } -decanter.workspace = true +blake3 = { features = [], version = "1" } +decanter = { features = ["derive"], version = "0.1.6" } hex = "0.4" itertools.workspace = true -ring = { features = ["wasm32_c"], version = "0.16" } -serde.workspace = true -serde_json.workspace = true +serde = { optional = true, features = ["derive"], version = "1" } +serde_json = { optional = true, version = "1" } smart-default.workspace = true strum.workspace = true [dev-dependencies] -hex-literal = "0.3" +hex-literal = "0.4" +lazy_static = "1.4" log = "0.4" +rs_merkle = "1.4" vrf = "0.2" [package.metadata.docs.rs] diff --git a/merkle/benches/merkle.rs b/merkle/benches/merkle.rs new file mode 100644 index 0000000..e26a7f2 --- /dev/null +++ b/merkle/benches/merkle.rs @@ -0,0 +1,53 @@ +// bench.rs +#![feature(test)] + +extern crate test; +use algae_merkle::MerkleTree; +use decanter::prelude::{Hashable, H256}; +use rs_merkle::Hasher; +use test::Bencher; + +const TEST_NODES: [&str; 4] = ["a", "b", "c", "d"]; + +#[bench] +fn bench_merkle_proofs(b: &mut Bencher) { + let leafs = TEST_NODES.iter().map(|i| i.hash()).collect::>(); + let tree = MerkleTree::from(leafs.as_slice()); + b.iter(|| { + leafs + .iter() + .enumerate() + .for_each(|(i, leaf)| assert!(!tree.proof(i).contains(leaf))); + }); +} + +#[derive(Clone)] +pub struct BSha256Algorithm; + +impl Hasher for BSha256Algorithm { + type Hash = [u8; 32]; + + fn hash(data: &[u8]) -> Self::Hash { + let mut hasher = blake3::Hasher::new(); + hasher.update(data); + hasher.finalize().into() + } +} + +#[bench] +fn bench_rs_merkle(b: &mut Bencher) { + use rs_merkle::Hasher; + use rs_merkle::MerkleTree as RSMerkleTree; + + let leafs = TEST_NODES + .iter() + .map(|i| BSha256Algorithm::hash(i.as_bytes())) + .collect::>(); + let tree = RSMerkleTree::::from_leaves(&leafs); + b.iter(|| { + leafs + .iter() + .enumerate() + .for_each(|(i, leaf)| assert!(!tree.proof(&[i]).proof_hashes().contains(leaf))); + }); +} diff --git a/merkle/src/layers.rs b/merkle/src/layers.rs index 140be48..75fe1dd 100644 --- a/merkle/src/layers.rs +++ b/merkle/src/layers.rs @@ -1,25 +1,25 @@ /* Appellation: layers Contrib: FL03 - Description: - Merkle Tree def... */ use crate::Node; +use decanter::prelude::Hashable; use itertools::Itertools; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use std::string::ToString; // pub fn build_new_merkle_layer(left: MerkleNode, right: MerkleNode) -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct Layer(Vec>) where - T: Default + ToString; + T: Hashable; impl Layer where - T: Default + ToString, + T: Hashable, { - pub fn new(data: Vec>) -> Self { + pub fn new(data: impl IntoIterator>) -> Self { let layer = data.into_iter().batching(|it| match it.next() { Some(l) => match it.next() { Some(r) => Some(Node::from((l, r))), @@ -37,7 +37,7 @@ where impl From>> for Layer where - T: Default + ToString, + T: Hashable, { fn from(data: Vec>) -> Self { Self::new(data) @@ -46,7 +46,7 @@ where impl From<(Node, Node)> for Layer where - T: Default + ToString, + T: Hashable, { fn from(data: (Node, Node)) -> Self { Self::new(vec![data.0, data.1]) diff --git a/merkle/src/lib.rs b/merkle/src/lib.rs index 11a21fd..8ac9e2a 100644 --- a/merkle/src/lib.rs +++ b/merkle/src/lib.rs @@ -1,7 +1,6 @@ /* Appellation: algae-merkle Creator: FL03 - Description: */ #[cfg(test)] extern crate hex_literal; @@ -13,6 +12,15 @@ pub(crate) mod nodes; pub(crate) mod payloads; pub(crate) mod shape; pub(crate) mod tree; -mod utils; +pub(crate) mod utils; pub mod proofs; + +pub mod prelude { + pub use crate::layers::*; + pub use crate::nodes::*; + pub use crate::payloads::*; + pub use crate::shape::*; + pub use crate::tree::*; + pub use crate::utils::*; +} diff --git a/merkle/src/nodes.rs b/merkle/src/nodes.rs index dc8c23b..18568c4 100644 --- a/merkle/src/nodes.rs +++ b/merkle/src/nodes.rs @@ -1,17 +1,17 @@ /* Appellation: nodes Contrib: FL03 - Description: ... Summary ... */ use crate::{combine_hash_str, merkle_hash, Payload}; use decanter::prelude::{Hashable, H256}; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use std::string::ToString; -#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct Node where - T: Default + ToString, + T: Hashable, { pub data: Payload, pub hash: H256, @@ -19,7 +19,7 @@ where impl Node where - T: Default + ToString, + T: Hashable, { pub fn new(data: Payload, hash: H256) -> Self { Self { data, hash } @@ -28,7 +28,7 @@ where impl Hashable for Node where - T: Default + ToString, + T: Hashable, { fn hash(&self) -> H256 { merkle_hash(self.data.to_string()) @@ -37,7 +37,7 @@ where impl From<(Node, Node)> for Node where - T: Default + ToString, + T: Hashable, { fn from(data: (Node, Node)) -> Self { let hash = merkle_hash(combine_hash_str(&data.0.hash, &data.1.hash)); @@ -48,13 +48,9 @@ where impl std::fmt::Display for Node where - T: Default + ToString, + T: Hashable + ToString, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let msg = serde_json::json!({ - "data": self.data.to_string(), - "hash": self.hash, - }); - write!(f, "{}", msg) + write!(f, "data: {}, hash: {}", self.data.to_string(), self.hash) } } diff --git a/merkle/src/payloads.rs b/merkle/src/payloads.rs index ff5f254..cfd8823 100644 --- a/merkle/src/payloads.rs +++ b/merkle/src/payloads.rs @@ -1,41 +1,26 @@ /* Appellation: nodes Contrib: FL03 - Description: ... Summary ... */ use crate::Node; use decanter::prelude::Hashable; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use std::string::ToString; use strum::Display; -#[derive( - Clone, - Debug, - Deserialize, - Display, - Eq, - Hash, - Hashable, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, -)] +#[derive(Clone, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Deserialize, Hashable, Serialize))] pub enum Payload where - T: Default + ToString, + T: Hashable, { - #[default] Leaf(T), Node(Box>, Box>), } impl Payload where - T: Default + ToString, + T: Hashable, { pub fn leaf(data: T) -> Self { Self::Leaf(data) @@ -59,7 +44,7 @@ where impl From for Payload where - T: Default + ToString, + T: Hashable, { fn from(data: T) -> Self { Self::Leaf(data) @@ -68,7 +53,7 @@ where impl From<(Box>, Box>)> for Payload where - T: Default + ToString, + T: Hashable, { fn from(data: (Box>, Box>)) -> Self { Self::node(data.0, data.1) diff --git a/merkle/src/proofs/mod.rs b/merkle/src/proofs/mod.rs index 44a87d2..caf85d8 100644 --- a/merkle/src/proofs/mod.rs +++ b/merkle/src/proofs/mod.rs @@ -1,9 +1,23 @@ /* Appellation: proofs Contrib: FL03 - Description: ... Summary ... */ +//! # Merkle Proofs +//! +//! ## Overview +//! +//! Merkle proofs are a way to prove that a given piece of data is part of a Merkle tree. +//! pub use self::{path::*, proof::*}; pub(crate) mod path; pub(crate) mod proof; + +use decanter::prelude::H256; + +pub trait MerkleProof { + fn proof(&self) -> Vec; +} + +#[cfg(test)] +mod tests {} diff --git a/merkle/src/proofs/path.rs b/merkle/src/proofs/path.rs index 77eb7a8..c30b3d3 100644 --- a/merkle/src/proofs/path.rs +++ b/merkle/src/proofs/path.rs @@ -1,7 +1,6 @@ /* Appellation: path [merkle] Contrib: FL03 - Description: ... Summary ... */ pub fn proof_path(index: usize, size: usize) -> Vec { diff --git a/merkle/src/proofs/proof.rs b/merkle/src/proofs/proof.rs index d1d48f8..09fbd5f 100644 --- a/merkle/src/proofs/proof.rs +++ b/merkle/src/proofs/proof.rs @@ -1,7 +1,6 @@ /* Appellation: proof Contrib: FL03 - Description: ... Summary ... */ use super::path::proof_path; use crate::{MerkleDimension, MerkleTree}; @@ -46,11 +45,11 @@ impl Iterator for Prover { pub fn merkle_proof(dim: MerkleDimension, nodes: Vec, index: usize) -> Vec { let mut proof: Vec = Vec::new(); let mut offset: usize = 0; - let mut leaf_size = dim.leafs; + let mut leaf_size = dim.leafs(); let proof_index = proof_path(index, leaf_size); - for i in 0..dim.depth - 1 { + for i in 0..dim.depth() - 1 { proof.push(nodes[offset + proof_index[i]]); if leaf_size % 2 != 0 { leaf_size += 1; diff --git a/merkle/src/shape.rs b/merkle/src/shape.rs index e8351a9..3736a05 100644 --- a/merkle/src/shape.rs +++ b/merkle/src/shape.rs @@ -1,43 +1,63 @@ /* Appellation: shape Contrib: FL03 - Description: ... Summary ... */ +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -pub trait MerkleShape { - fn depth(&self) -> usize; - fn leafs(&self) -> usize; - fn shape(&self) -> (usize, usize, usize) { - (self.depth(), self.leafs(), self.size()) +fn get_merkle_tree_size(leafs: usize) -> usize { + let mut size = leafs + (leafs % 2); + let mut l = leafs; + while l > 1 { + l = (l as f64 / 2_f64).ceil() as usize; + size += l; } - fn size(&self) -> usize; + size } -#[derive( - Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] +fn get_merkle_depth(leafs: usize) -> usize { + let mut depth = 1; + let mut l = leafs; + while l > 1 { + l = (l as f64 / 2_f64).ceil() as usize; + depth += 1; + } + depth +} + +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct MerkleDimension { - pub depth: usize, - pub leafs: usize, - pub size: usize, + depth: usize, + leafs: usize, + size: usize, } impl MerkleDimension { pub fn new(depth: usize, leafs: usize, size: usize) -> Self { Self { depth, leafs, size } } -} -impl MerkleShape for MerkleDimension { - fn depth(&self) -> usize { + pub fn from_leafs(leafs: usize) -> Self { + let depth = get_merkle_depth(leafs); + let size = get_merkle_tree_size(leafs); + + Self::new(depth, leafs, size) + } + + pub fn depth(&self) -> usize { self.depth } - fn leafs(&self) -> usize { + pub fn leafs(&self) -> usize { self.leafs } - fn size(&self) -> usize { + + pub fn shape(&self) -> (usize, usize, usize) { + (self.depth, self.leafs, self.size) + } + + pub fn size(&self) -> usize { self.size } } @@ -48,14 +68,14 @@ impl std::fmt::Display for MerkleDimension { } } -impl From> for MerkleDimension { - fn from(data: Box) -> Self { - Self::from(data.shape()) - } -} - impl From<(usize, usize, usize)> for MerkleDimension { fn from(data: (usize, usize, usize)) -> Self { Self::new(data.0, data.1, data.2) } } + +impl From for (usize, usize, usize) { + fn from(data: MerkleDimension) -> Self { + (data.depth, data.leafs, data.size) + } +} diff --git a/merkle/src/tree/builder.rs b/merkle/src/tree/builder.rs new file mode 100644 index 0000000..9fd220c --- /dev/null +++ b/merkle/src/tree/builder.rs @@ -0,0 +1,19 @@ +/* + Appellation: builder + Contrib: FL03 +*/ + +pub struct MerkleTreeBuilder { + leafs: Vec, +} + +impl MerkleTreeBuilder { + pub fn new() -> Self { + Self { leafs: Vec::new() } + } + + pub fn with_leafs(mut self, leafs: Vec) -> Self { + self.leafs = leafs; + self + } +} diff --git a/merkle/src/tree.rs b/merkle/src/tree/merkle.rs similarity index 53% rename from merkle/src/tree.rs rename to merkle/src/tree/merkle.rs index 941a3c4..8c4cc57 100644 --- a/merkle/src/tree.rs +++ b/merkle/src/tree/merkle.rs @@ -1,17 +1,19 @@ /* - Appellation: tree + Appellation: merkle Contrib: FL03 - Description: ... Summary ... */ use crate::proofs::merkle_proof; -use crate::{create_merkle_tree, MerkleDimension, MerkleShape}; +use crate::{create_merkle_tree, MerkleDimension}; use decanter::prelude::{Hashable, H256}; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use std::ops::{Index, IndexMut, Range}; -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct MerkleTree { - pub dim: MerkleDimension, - pub nodes: Vec, + dim: MerkleDimension, + nodes: Vec, } impl MerkleTree { @@ -31,7 +33,7 @@ impl MerkleTree { merkle_proof(self.dim(), self.nodes().clone(), index) } pub fn root(&self) -> H256 { - self.nodes()[self.dim().size() - 1] + self[self.dim().size() - 1] } pub fn nodes(&self) -> &Vec { &self.nodes @@ -47,7 +49,10 @@ impl std::fmt::Display for MerkleTree { } } -impl From<&[T]> for MerkleTree { +impl From<&[T]> for MerkleTree +where + T: Hashable, +{ fn from(data: &[T]) -> Self { let (dim, nodes) = create_merkle_tree::(data); Self { @@ -57,11 +62,30 @@ impl From<&[T]> for MerkleTree { } } -impl From<(Box, Vec)> for MerkleTree { - fn from(data: (Box, Vec)) -> Self { - Self { - dim: MerkleDimension::from(data.0), - nodes: data.1, - } +impl Index for MerkleTree { + type Output = H256; + + fn index(&self, index: usize) -> &Self::Output { + &self.nodes[index] + } +} + +impl IndexMut for MerkleTree { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.nodes[index] + } +} + +impl Index> for MerkleTree { + type Output = [H256]; + + fn index(&self, index: Range) -> &Self::Output { + &self.nodes[index] + } +} + +impl IndexMut> for MerkleTree { + fn index_mut(&mut self, index: Range) -> &mut Self::Output { + &mut self.nodes[index] } } diff --git a/merkle/src/tree/mod.rs b/merkle/src/tree/mod.rs new file mode 100644 index 0000000..91342ab --- /dev/null +++ b/merkle/src/tree/mod.rs @@ -0,0 +1,9 @@ +/* + Appellation: tree + Contrib: FL03 +*/ +pub use self::{builder::*, merkle::*, partial::*}; + +mod builder; +mod merkle; +mod partial; diff --git a/merkle/src/tree/partial.rs b/merkle/src/tree/partial.rs new file mode 100644 index 0000000..c4bb788 --- /dev/null +++ b/merkle/src/tree/partial.rs @@ -0,0 +1,25 @@ +/* + Appellation: partial + Contrib: FL03 +*/ +use crate::Layer; + +pub struct MerkleLayer {} + +pub struct PartialTree { + layers: Vec, +} + +impl PartialTree { + pub fn new() -> Self { + Self { layers: Vec::new() } + } + + pub fn add_layer(&mut self, layer: Layer) { + self.layers.push(layer); + } + + pub fn layers(&self) -> &[Layer] { + &self.layers + } +} diff --git a/merkle/src/utils.rs b/merkle/src/utils.rs index b35fdbf..db0769e 100644 --- a/merkle/src/utils.rs +++ b/merkle/src/utils.rs @@ -1,24 +1,36 @@ /* - Appellation: generate + Appellation: utils Contrib: FL03 - Description: */ use crate::proofs::proof_path; -use crate::{MerkleDimension, MerkleShape}; +use crate::MerkleDimension; use decanter::prelude::{hasher, Hashable, H256}; /// Combines two hashes into a single hash -pub fn add_hash(a: &H256, b: &H256) -> H256 { - let c = [a.as_ref(), b.as_ref()].concat(); - let combined = ring::digest::digest(&ring::digest::SHA256, &c); - hasher(combined).into() +// pub fn add_hash(a: &H256, b: &H256) -> H256 { +// let c = [a.as_ref(), b.as_ref()].concat(); +// let combined = ring::digest::digest(&ring::digest::SHA256, &c); +// hasher(combined).into() +// } + +pub fn concat_and_hash(left: &H256, right: Option<&H256>) -> H256 { + let mut concatenated: Vec = (*left).0.to_vec(); + + match right { + Some(right_node) => { + let mut right_node_clone: Vec = (*right_node).0.to_vec(); + concatenated.append(&mut right_node_clone); + hasher(&concatenated).into() + } + None => *left, + } } /// Merges two hashes into a string pub fn combine_hash_str(a: &T, b: &T) -> String { format!("{}{}", a.to_string(), b.to_string()) } /// Creates a Merkle tree from a slice of data -pub fn create_merkle_tree(data: &[T]) -> (Box, Vec) +pub fn create_merkle_tree(data: &[T]) -> (MerkleDimension, Vec) where T: Hashable, { @@ -39,7 +51,7 @@ where } let mut temp = Vec::new(); for i in 0..length / 2 { - let h: H256 = add_hash(&last_level[2 * i], &last_level[2 * i + 1]); + let h: H256 = concat_and_hash(&last_level[2 * i], Some(&last_level[2 * i + 1])); temp.push(h); nodes.push(h); } @@ -48,7 +60,7 @@ where depth += 1; } let dim = MerkleDimension::new(depth, data.len(), nodes.len()); - (Box::new(dim), nodes) + (dim, nodes) } /// Takes the hash of the given information to the second degree @@ -70,9 +82,9 @@ pub fn is_merkle_valid( 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); + h = concat_and_hash(&proof[i], Some(&h)); } else { - h = add_hash(&h, &proof[i]); + h = concat_and_hash(&h, Some(&proof[i])); } } *root == h diff --git a/merkle/tests/default.rs b/merkle/tests/default.rs index 7dee5fa..5f26546 100644 --- a/merkle/tests/default.rs +++ b/merkle/tests/default.rs @@ -1,6 +1,17 @@ +/* + Appellation: default + Contrib: FL03 +*/ #[cfg(test)] + +fn addition(a: A, b: B) -> C +where + A: std::ops::Add, +{ + a + b +} + #[test] -fn lib_compiles() { - let f = |i| i + 1; - assert_eq!(f(10), 11) +fn compiles() { + assert_eq!(addition(10, 1), 11) } diff --git a/merkle/tests/merkle.rs b/merkle/tests/merkle.rs index f60daf0..551d2b1 100644 --- a/merkle/tests/merkle.rs +++ b/merkle/tests/merkle.rs @@ -7,15 +7,60 @@ 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}; +use algae_merkle::{concat_and_hash, is_merkle_valid, MerkleTree}; use decanter::prelude::{Hashable, H256}; use hex_literal::hex; +use rs_merkle::Hasher; + +// lazy_static::lazy_static!( +// static ref SAMPLE_DATA: Vec = hash_leaves(); +// ); + +#[derive(Clone)] +pub struct BSha256; + +impl Hasher for BSha256 { + type Hash = [u8; 32]; + + fn hash(data: &[u8]) -> Self::Hash { + blake3::hash(data).into() + } +} + +fn get_merkle_tree_size(leafs: usize) -> usize { + let mut size = leafs + (leafs % 2); + let mut l = leafs; + while l > 1 { + l = (l as f64 / 2_f64).ceil() as usize; + size += l; + } + size +} + +fn get_merkle_depth(leafs: usize) -> usize { + let mut depth = 1; + let mut l = leafs; + while l > 1 { + l = (l as f64 / 2_f64).ceil() as usize; + depth += 1; + } + depth +} + +macro_rules! hash_leaves { + ($leaves:expr) => { + $leaves + .iter() + .map(|leaf| leaf.hash()) + .collect::>() + }; +} macro_rules! gen_merkle_tree_data { () => {{ vec![ - (hex!("0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), + (hex!("17762fddd969a453925d65717ac3eea21320b66b54342fde15128d6caf21215f")).into(), + (hex!("10e5cf3d3c8a4f9f3468c8cc58eea84892a22fdadbc1acb22410190044c1d553")).into(), ] }}; } @@ -23,11 +68,11 @@ macro_rules! gen_merkle_tree_data { macro_rules! gen_merkle_tree_data2 { () => {{ vec![ - (hex!("0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), + (hex!("17762fddd969a453925d65717ac3eea21320b66b54342fde15128d6caf21215f")).into(), + (hex!("10e5cf3d3c8a4f9f3468c8cc58eea84892a22fdadbc1acb22410190044c1d553")).into(), + (hex!("ea7aa1fc9efdbe106dbb70369a75e9671fa29d52bd55536711bf197477b8f021")).into(), + (hex!("d5ede538f628f687e5e0422c7755b503653de2dcd7053ca8791afa5d4787d843")).into(), + (hex!("27bb492e108bf5e9c724176d7ae75d4cedc422fe4065020bd6140c3fcad3a9e7")).into(), ] }}; } @@ -39,11 +84,47 @@ macro_rules! gen_merkle_tree_data2 { */ #[test] fn test_merkle_root() { - let data: Vec = gen_merkle_tree_data!(); - let expected = - (hex!("6b787718210e0b3b608814e04e61fde06d0df794319a12162f287412df3ec920")).into(); - let a = MerkleTree::from(data.as_slice()); - assert_ne!(&a.root(), &expected); + let sample = ["a", "b", "c", "d"]; + let leaves = hash_leaves!(sample); + let nleafs = leaves.len(); + let exp = { + let a = concat_and_hash(&leaves[0], Some(&leaves[1])); + let b = concat_and_hash(&leaves[2], Some(&leaves[3])); + concat_and_hash(&a, Some(&b)) + // add_hash(&a, &leaves[2]) + }; + + let a = MerkleTree::from(sample.as_slice()); + let b = rs_merkle::MerkleTree::::from_leaves( + sample + .iter() + .map(|i| BSha256::hash(i.as_bytes())) + .collect::>() + .as_slice(), + ); + + assert_eq!(a.root(), exp); + assert_eq!(a.root().0, b.root().expect("No Root")); + assert_eq!( + a.dim().shape(), + ( + get_merkle_depth(nleafs), + nleafs, + get_merkle_tree_size(nleafs) + ) + ); +} + +#[test] +fn test_merkle_shape() { + let sample = ["a", "b", "c", "d", "e", "f", "g"]; + let leafs = sample.len(); + let a = MerkleTree::from(sample.as_slice()); + + assert_eq!( + a.dim().shape(), + (get_merkle_depth(leafs), leafs, get_merkle_tree_size(leafs)) + ); } /* @@ -51,12 +132,14 @@ fn test_merkle_root() { */ #[test] fn test_merkle_proof() { - let expected = - vec![hex!("965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f").into()]; - let data: Vec = gen_merkle_tree_data!(); - let a = MerkleTree::from(data.as_slice()); + let sample = ["a", "b", "c", "d"]; + let leaves = hash_leaves!(sample); + + let exp = vec![leaves[1], concat_and_hash(&leaves[2], Some(&leaves[3]))]; + + let a = MerkleTree::from(leaves.as_slice()); - assert_ne!(a.proof(0), expected) + assert_eq!(a.proof(0), exp); } /* diff --git a/mmr/Cargo.toml b/mmr/Cargo.toml index 5796c33..96963ab 100644 --- a/mmr/Cargo.toml +++ b/mmr/Cargo.toml @@ -13,10 +13,11 @@ version.workspace = true [features] default = [ - "decanter/derive" + ] + wasm = [ - "decanter/wasm" + ] [lib] @@ -25,18 +26,18 @@ test = true [dependencies] anyhow.workspace = true -decanter.workspace = true +decanter = { features = ["derive"], version = "0.1.6" } digest = "0.10" hex = "0.4" itertools.workspace = true -serde.workspace = true -serde_json.workspace = true +serde = { features = ["derive"], version = "1" } +serde_json = "1" smart-default.workspace = true strum.workspace = true [dev-dependencies] -hex-literal = "0.3.4" -vrf = "0.2.4" +hex-literal = "0.4" +vrf = "0.2" [package.metadata.docs.rs] all-features = true diff --git a/mmr/src/cmp/nodes.rs b/mmr/src/cmp/nodes.rs index ce531fc..e345141 100644 --- a/mmr/src/cmp/nodes.rs +++ b/mmr/src/cmp/nodes.rs @@ -1,12 +1,9 @@ /* Appellation: cmps Contrib: FL03 - Description: ... summary ... */ - use decanter::prelude::{Hashable, H256}; use serde::{Deserialize, Serialize}; -use std::convert::From; #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct Node { diff --git a/mmr/src/mmr.rs b/mmr/src/mmr.rs index b39fa6d..87cce42 100644 --- a/mmr/src/mmr.rs +++ b/mmr/src/mmr.rs @@ -1,14 +1,12 @@ /* Appellation: mmr Contrib: FL03 - Description: ... summary ... */ use crate::cmp::{Node, Position}; use crate::{is_node_right, sibling_index, RangeMap}; use decanter::prelude::{hasher, Hashable, H256}; use digest::Digest; use serde::{Deserialize, Serialize}; -use std::convert::From; #[derive(Clone, Debug, Default, Deserialize, Eq, Hashable, PartialEq, Serialize)] pub struct MerkleMountainRange diff --git a/mmr/tests/default.rs b/mmr/tests/default.rs index 7dee5fa..5f26546 100644 --- a/mmr/tests/default.rs +++ b/mmr/tests/default.rs @@ -1,6 +1,17 @@ +/* + Appellation: default + Contrib: FL03 +*/ #[cfg(test)] + +fn addition(a: A, b: B) -> C +where + A: std::ops::Add, +{ + a + b +} + #[test] -fn lib_compiles() { - let f = |i| i + 1; - assert_eq!(f(10), 11) +fn compiles() { + assert_eq!(addition(10, 1), 11) } diff --git a/queue/Cargo.toml b/queue/Cargo.toml new file mode 100644 index 0000000..7d4d605 --- /dev/null +++ b/queue/Cargo.toml @@ -0,0 +1,39 @@ +[package] +authors.workspace = true +categories.workspace = true +description.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "algae-queue" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[features] +default = [] + +serde = [ + "dep:serde", + "serde-ext" +] + +serde-ext = [ + "dep:serde_json" +] + +[dependencies] +serde = { optional = true, features = ["derive"], version = "1" } +serde_json = { optional = true, version = "1" } + + +[dev-dependencies] + +[package.metadata.docs.rs] +all-features = true +rustc-args = ["--cfg", "docsrs"] + +[target.wasm32-unknown-unknown] + +[target.wasm32-wasi] diff --git a/queue/src/lib.rs b/queue/src/lib.rs new file mode 100644 index 0000000..86c732e --- /dev/null +++ b/queue/src/lib.rs @@ -0,0 +1,9 @@ +/* + Appellation: algae-queue + Contrib: FL03 +*/ +//! # Algae Queue +//! +//! + +pub mod prelude {} diff --git a/queue/tests/default.rs b/queue/tests/default.rs new file mode 100644 index 0000000..5f26546 --- /dev/null +++ b/queue/tests/default.rs @@ -0,0 +1,17 @@ +/* + Appellation: default + Contrib: FL03 +*/ +#[cfg(test)] + +fn addition(a: A, b: B) -> C +where + A: std::ops::Add, +{ + a + b +} + +#[test] +fn compiles() { + assert_eq!(addition(10, 1), 11) +}