diff --git a/cl-structures/src/lib.rs b/cl-structures/src/lib.rs index 69deef0..836cefc 100644 --- a/cl-structures/src/lib.rs +++ b/cl-structures/src/lib.rs @@ -4,3 +4,4 @@ #![warn(clippy::all)] pub mod span; +pub mod tree; diff --git a/cl-structures/src/tree.rs b/cl-structures/src/tree.rs new file mode 100644 index 0000000..897dc31 --- /dev/null +++ b/cl-structures/src/tree.rs @@ -0,0 +1,218 @@ +//! An insert-only unordered tree, backed by a [Vec] +//! +//! # Examples +//! ``` +//! use cl_structures::tree::{Tree, Node}; +//! // A tree can be created +//! let mut tree = Tree::new(); +//! // Provided with a root node +//! let root = tree.root("This is the root node").unwrap(); +//! +//! // Nodes can be accessed by indexing +//! assert_eq!(*tree[root].as_ref(), "This is the root node"); +//! // Nodes' data can be accessed directly by calling `get`/`get_mut` +//! assert_eq!(tree.get(root).unwrap(), &"This is the root node") +//! ``` + +// #![allow(unused)] + +pub use self::tree_ref::Ref; +use std::ops::{Index, IndexMut}; + +pub mod tree_ref; + +/// An insert-only unordered tree, backed by a [Vec] +#[derive(Debug)] +pub struct Tree { + nodes: Vec>, +} + +impl Default for Tree { + fn default() -> Self { + Self { nodes: Default::default() } + } +} + +/// Getters +impl Tree { + pub fn get(&self, index: Ref) -> Option<&T> { + self.get_node(index).map(|node| &node.value) + } + pub fn get_mut(&mut self, index: Ref) -> Option<&mut T> { + self.get_node_mut(index).map(|node| &mut node.value) + } + pub fn get_node(&self, index: Ref) -> Option<&Node> { + self.nodes.get(usize::from(index)) + } + pub fn get_node_mut(&mut self, index: Ref) -> Option<&mut Node> { + self.nodes.get_mut(usize::from(index)) + } +} + +/// Tree operations +impl Tree { + pub fn new() -> Self { + Self { nodes: Default::default() } + } + + /// Creates a new root for the tree. + /// + /// If the tree already has a root, the value will be returned. + pub fn root(&mut self, value: T) -> Result, T> { + if self.is_empty() { + // Create an index for the new node + let node = Ref::new_unchecked(self.nodes.len()); + // add child to tree + self.nodes.push(Node::from(value)); + Ok(node) + } else { + Err(value) + } + } + + pub fn get_root(&mut self) -> Option> { + match self.nodes.is_empty() { + true => None, + false => Some(Ref::new_unchecked(0)), + } + } + + /// Insert a value into the tree as a child of the parent node + /// + /// # Panics + /// May panic if the node [Ref] is from a different tree + pub fn insert(&mut self, value: T, parent: Ref) -> Ref { + let child = Ref::new_unchecked(self.nodes.len()); + // add child to tree before parent + self.nodes.push(Node::with_parent(value, parent)); + // add child to parent + self[parent].children.push(child); + + child + } + + /// Gets the depth of a node + /// + /// # Panics + /// May panic if the node [Ref] is from a different tree + pub fn depth(&self, node: Ref) -> usize { + match self[node].parent { + Some(node) => self.depth(node) + 1, + None => 0, + } + } + + /// Gets the number of branches in the tree + pub fn branches(&self) -> usize { + self.nodes.iter().fold(0, |edges, node| edges + node.len()) + } +} + +/// Standard data structure functions +impl Tree { + pub fn len(&self) -> usize { + self.nodes.len() + } + pub fn is_empty(&self) -> bool { + self.nodes.is_empty() + } +} + +impl Index> for Tree { + type Output = Node; + fn index(&self, index: Ref) -> &Self::Output { + self.get_node(index).expect("Ref should be inside Tree") + } +} +impl IndexMut> for Tree { + fn index_mut(&mut self, index: Ref) -> &mut Self::Output { + self.get_node_mut(index).expect("Ref should be inside Tree") + } +} + +/// A node in a [Tree] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Node { + value: T, + /// The parent + parent: Option>, + /// The children + children: Vec>, +} + +impl Node { + pub const fn new(value: T) -> Self { + Self { value, parent: None, children: vec![] } + } + pub const fn with_parent(value: T, parent: Ref) -> Self { + Self { value, parent: Some(parent), children: vec![] } + } + + pub fn get(&self) -> &T { + self.as_ref() + } + pub fn get_mut(&mut self) -> &mut T { + self.as_mut() + } + pub fn swap(&mut self, value: T) -> T { + std::mem::replace(&mut self.value, value) + } + + pub fn children(&self) -> &[Ref] { + &self.children + } + + pub fn len(&self) -> usize { + self.children.len() + } + + pub fn is_empty(&self) -> bool { + self.children.is_empty() + } +} + +impl AsRef for Node { + fn as_ref(&self) -> &T { + &self.value + } +} +impl AsMut for Node { + fn as_mut(&mut self) -> &mut T { + &mut self.value + } +} + +impl From for Node { + #[inline] + fn from(value: T) -> Self { + Self::new(value) + } +} + +#[cfg(test)] +mod test { + #[allow(unused)] + use super::*; + + #[test] + fn add_children() { + let mut tree = Tree::new(); + let root = tree.root(0).unwrap(); + let one = tree.insert(1, root); + let two = tree.insert(2, root); + assert_eq!([one, two].as_slice(), tree[root].children()); + } + #[test] + fn nest_children() { + let mut tree = Tree::new(); + let root = tree.root(0).unwrap(); + let one = tree.insert(1, root); + let two = tree.insert(2, one); + assert_eq!(&[one], tree[root].children()); + assert_eq!(&[two], tree[one].children()); + assert_eq!(tree[two].children(), &[]); + } + + #[test] + fn compares_equal() {} +} diff --git a/cl-structures/src/tree/tree_ref.rs b/cl-structures/src/tree/tree_ref.rs new file mode 100644 index 0000000..5cf0997 --- /dev/null +++ b/cl-structures/src/tree/tree_ref.rs @@ -0,0 +1,70 @@ +//! An element in a [Tree](super::Tree) +/// +/// Contains a niche, and as such, [`Option>`] is free :D +use std::{marker::PhantomData, num::NonZeroUsize}; + +/// An element of in a [Tree](super::Tree). +//? The index of the node is stored as a [NonZeroUsize] for space savings +//? Making Refs T-specific helps the user keep track of which Refs belong to which trees. +//? This isn't bulletproof, of course, but it'll keep Ref from being used on Tree +pub struct Ref(NonZeroUsize, PhantomData); + +impl Ref { + /// Constructs a new [TreeRef] with the given index + pub fn new_unchecked(index: usize) -> Self { + // Safety: index cannot be zero because we use saturating addition on unsigned type. + Self( + unsafe { NonZeroUsize::new_unchecked(index.saturating_add(1)) }, + PhantomData, + ) + } +} + +impl From> for usize { + fn from(value: Ref) -> Self { + usize::from(value.0) - 1 + } +} + +/* --- implementations of derivable traits, because we don't need bounds here --- */ + +impl std::fmt::Debug for Ref { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("TreeRef").field(&self.0).finish() + } +} + +impl std::hash::Hash for Ref { + fn hash(&self, state: &mut H) { + self.0.hash(state); + self.1.hash(state); + } +} + +impl PartialEq for Ref { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} + +impl Eq for Ref {} + +impl PartialOrd for Ref { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Ref { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.cmp(&other.0) + } +} + +impl Clone for Ref { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Ref {}