Skip to content

Commit

Permalink
Merge pull request #2 from clementwanjau/feat-tree-opers
Browse files Browse the repository at this point in the history
Feat tree opers
  • Loading branch information
clementwanjau authored Jun 6, 2024
2 parents 348a0f3 + 660dc91 commit df10030
Show file tree
Hide file tree
Showing 8 changed files with 2,081 additions and 1,140 deletions.
36 changes: 0 additions & 36 deletions CONTRIBUTING

This file was deleted.

47 changes: 47 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
Tree-ds
=======

We appreciate your interest in contributing to `tree-ds`! This document outlines the process for submitting pull
requests and other contributions.

### Getting Started

- Fork the repository: Create a fork of the `tree-ds` repository on GitHub. This will allow you to make changes and
submit pull requests.
- Set up your development environment: Ensure you have Rust and Cargo installed. Refer to the official Rust
documentation for installation instructions: https://www.rust-lang.org/tools/install
- Clone your fork: Clone your forked repository to your local machine.

### Making Changes

- Identify an issue: Check the issue tracker for existing issues or feature requests. If you can't find one that matches
your contribution, feel free to create a new issue.
- Create a branch: Create a new branch for your specific contribution. Use a descriptive branch name that reflects the
changes you're making.
- Make your changes: Implement your changes in the codebase. Follow the existing code style and formatting conventions.
- Write unit tests: Ensure your changes are covered by unit tests. We recommend using the cargo test command to run
tests.
- Document your changes: Update the documentation (README.md) to reflect any new features or functionalities introduced
by your contribution.

### Submitting Pull Requests

- Push your changes: Push your branch to your forked repository on GitHub.
- Open a pull request: Create a pull request from your branch to the main branch of the upstream repository.
- Describe your changes: Provide a clear and concise description of your changes in the pull request description. This
should include what problem your contribution solves, any relevant code examples, and potential breaking changes (if
applicable).
- Respond to feedback: We will review your pull request and provide feedback. Be prepared to address any comments or
questions raised during the review process.

### Additional Guidelines

- Follow the code style: Adhere to the existing code style and formatting conventions used in the project. This helps
maintain consistency and readability of the codebase.
- Write clear and concise code: Strive to write clean, readable, and well-commented code.
- Test your changes: Ensure your changes are thoroughly tested with unit tests and regression tests.
- Respectful communication: Maintain a respectful and professional tone in all interactions.

Thank you!

We appreciate your contributions to `tree-ds`. Your help makes this project better for everyone!
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "tree-ds"
description = "A library to manipulate tree data structures."
version = "0.1.1"
version = "0.1.2"
edition = "2021"
authors = ["Clement Wanjau <clementwanjau@gmail.com>"]
repository = "https://github.com/clementwanjau/tree-ds"
Expand Down
62 changes: 53 additions & 9 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ A crude example of how to use the library is shown below:
use tree_ds::prelude::{Node, NodeRemovalStrategy, Result, Tree};

fn main() -> Result<()> {
let mut tree = Tree::new();
let mut tree = Tree::new(Some("Finances Tree"));
let root = tree.add_node(Node::new("Risk".to_string(), Some(5000)), None)?;
let fixed_income_node = tree.add_node(Node::new("Fixed Income".to_string(), Some(2000)), Some(&root))?;
let equity_node = tree.add_node(Node::new("Equity".to_string(), Some(3000)), Some(&root))?;
Expand All @@ -57,8 +57,6 @@ fn main() -> Result<()> {
tree.add_node(Node::new("Mid Cap Stocks".to_string(), Some(1000)), Some(&stocks_node))?;
tree.add_node(Node::new("Small Cap Stocks".to_string(), Some(1000)), Some(&stocks_node))?;

println!("Original Tree");
println!("*********************");
println!("{}", tree);


Expand All @@ -69,8 +67,6 @@ fn main() -> Result<()> {


let equity_sub_tree = tree.get_subtree(&equity_node, None);
println!("Equity Sub Tree");
println!("*********************");
println!("{}", equity_sub_tree);
Ok(())
}
Expand All @@ -80,8 +76,8 @@ fn main() -> Result<()> {
This will output:

```
Original Tree
*********************
Finances Tree
*************
Risk: 5000
├── Fixed Income: 2000
│ └── Debt: 1000
Expand All @@ -96,6 +92,8 @@ Risk: 5000
After Removing The Stocks Node
*******************
Finances Tree
*************
Risk: 5000
├── Fixed Income: 2000
│ └── Debt: 1000
Expand All @@ -104,16 +102,62 @@ Risk: 5000
└── Mutual Funds: 1000
└── Equity Mutual Funds: 500
Equity Sub Tree
*********************
Equity
******
Equity: 3000
└── Mutual Funds: 1000
└── Equity Mutual Funds: 500
```

## Traversal

You can traverse the tree using the `traverse` method. The `traverse` method
returns an iterator that allows you to traverse the tree in any order you want.
The following example shows how to traverse the tree in a pre-order fashion:

```rust
use tree_ds::prelude::{Node, Result, Tree, TraversalOrder};

fn main() -> Result<()> {
let mut tree = Tree::new();
let node_1 = tree.add_node(Node::new(1, Some(2)), None).unwrap();
let node_2 = tree.add_node(Node::new(2, Some(3)), Some(&node_1)).unwrap();
let node_3 = tree.add_node(Node::new(3, Some(6)), Some(&node_1)).unwrap();
let node_4 = tree.add_node(Node::new(4, Some(5)), Some(&node_2)).unwrap();
let node_5 = tree.add_node(Node::new(5, Some(6)), Some(&node_2)).unwrap();
let node_6 = tree.add_node(Node::new(6, Some(7)), Some(&node_3)).unwrap();
let preorder_nodes = tree.traverse(TraversalStrategy::PreOrder, &node_1);
let expected_preorder = vec![node_1, node_2, node_4, node_5, node_3, node_6];
assert_eq!(preorder_nodes, expected_preorder);

let in_order_nodes = tree.traverse(TraversalStrategy::InOrder, &node_1);
let expected_in_order = vec![node_4, node_2, node_5, node_1, node_3, node_6];
assert_eq!(in_order_nodes, expected_in_order);

let post_order_nodes = tree.traverse(TraversalStrategy::PostOrder, &node_1);
let expected_post_order = vec![node_4, node_5, node_2, node_6, node_3, node_1];
assert_eq!(post_order_nodes, expected_post_order);
Ok(())
}
```

You can also perform an action on the nodes while traversing the tree on the iterator returned by the `traverse` method.
The following example shows how to traverse the tree in a pre-order fashion and perform an action on the nodes:

```rust,ignore
let nodes = tree.traverse(TraversalOrder::PreOrder)
.map(|node| {
println!("{}", node);
node
})
.collect::<Vec<_>>();
```

## Roadmap

- Add support for more tree operations.
- Add node rotation.
- Add a macro to create trees from a DSL.
- Add support for weighted nodes.

Expand Down
55 changes: 51 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,72 @@
//! use tree_ds::prelude::*;
//!
//!
//! let mut tree: Tree<i32, i32> = Tree::new();
//! let mut tree: Tree<i32, i32> = Tree::new(Some("Sample Tree"));
//! let root = tree.add_node(Node::new(1, Some(2)), None).unwrap();
//! let child_1 = tree.add_node(Node::new(2, Some(3)), Some(&root)).unwrap();
//! let child_2 = tree.add_node(Node::new(3, Some(4)), Some(&child_1)).unwrap();
//! let child_3 = tree.add_node(Node::new(4, Some(5)), Some(&child_2)).unwrap();
//! let sub_tree = tree.get_subtree(&child_2, None);
//!
//! ```
//!
//! ## Traversal
//! The tree supports three traversal strategies:
//! - Pre-order
//! - Post-order
//! - In-order
//!
//! Consider the following tree:
//! ```text
//! Node 1: 2
//! └── Node 2: 3
//! └── Node 3: 4
//! └── Node 4: 5
//! ```
//!
//! You can modify nodes during traversal by using the iterator from the returned traversal data.
//!
//! ```rust
//! use tree_ds::prelude::*;
//!
//! let mut tree = Tree::new(Some("Sample Tree"));
//! let root = tree.add_node(Node::new("Node 1", Some(2)), None).unwrap();
//! let child_1 = tree.add_node(Node::new("Node 2", Some(3)), Some(&root)).unwrap();
//! let child_2 = tree.add_node(Node::new("Node 3", Some(4)), Some(&child_1)).unwrap();
//! let child_3 = tree.add_node(Node::new("Node 4", Some(5)), Some(&child_2)).unwrap();
//!
//! tree.traverse(TraversalStrategy::PreOrder, &root)
//! .iter()
//! .for_each(|node_id| {
//! let node = tree.get_node(node_id).unwrap();
//! let cur_value = node.get_value().unwrap();
//! node.set_value(Some(cur_value + 1));
//! });
//!
//! # assert_eq!("Sample Tree\n***********\nNode 1: 3\n└── Node 2: 4\n └── Node 3: 5\n └── Node 4: 6\n", tree.to_string());
//! # assert_eq!(tree.get_node(&root).unwrap().get_value().unwrap(), 3);
//! ```
//!
//! The newly modified tree will be:
//! ```text
//! Sample Tree
//! ***********
//! Node 1: 3
//! └── Node 2: 4
//! └── Node 3: 5
//! └── Node 4: 6
//! ```

mod error;
mod node;
mod tree;

pub mod prelude {
//! A module to re-export the necessary types for the tree data structure.
//! A module to re-export the necessary types for the tree data structure.

pub use crate::{
node::Node,
tree::{NodeRemovalStrategy, SubTree, Tree},
node::{Node, Nodes},
tree::{NodeRemovalStrategy, SubTree, TraversalStrategy, Tree},
};

pub type Result<T> = std::result::Result<T, crate::error::Error>;
Expand Down
Loading

0 comments on commit df10030

Please sign in to comment.