Crate compt

source ·
Expand description

Summary

A library that provides a complete binary tree visitor trait with default implemenations for visiting strategies such as dfs_inorder or bfs, etc. Some adaptors are also provided that let you map, zip, or optionally also produce the depth on every call to next(). It also provides two flavors of a complete binary tree data structure with mutable and immutable visitors that implement the visitor trait. One laid out in bfs, and one laid out in dfs in order in memory. Both of these flavors assume that every node in the tree is the same type. The visitor trait is more flexible than this, however. With the NonLeafItem associated type, users can implement a visitor for tree data structures that have different types for the nonleafs and leafs.

This is the trait that this crate revoles around:

pub trait Visitor:Sized{
    type Item;
    type NonLeafItem;
    fn next(self)->(Self::Item,Option<(Self::NonLeafItem,Self,Self)>);
}

If you have a visitor, you can call next() on it to consume it, and produce the node it is visiting, plus the children nodes. Sometimes, non leaf nodes contain additional data that does not apply to leaf nodes. This is the purpose of the NonLeafItem associated type. Users can choose to define it to be some data that only non leaf nodes provide. For the two provided implementations, both leafs and nonleafs have the same time, so in those cases we just use the empty type.

The fact that the iterator is consumed when calling next(), allows us to return mutable references without fear of the users being able to create the same mutable reference some other way. So this property provides a way to get mutable references to children nodes simultaneously safely. Useful for parallelizing divide and conquer style problems.

Goals

To provide a useful complete binary tree visitor trait that has some similar features to the Iterator trait, such as zip(), and map(), and that can be used in parallel divide and conquer style problems.

Unsafety in the provided two tree implementations

With a regular Vec, getting one mutable reference to an element will borrow the entire Vec. However the two provided trees have invariants that let us make guarentees about which elements can be mutably borrowed at the same time. With the bfs tree, the children for an element at index k can be found at 2k+1 and 2k+2. This means that we are guarenteed that the parent, and the two children are all distinct elements and so mutable references two all of them can exist at the same time. With the dfs implementation, on every call to next() we use split_at_mut() to split the current slice we have into three parts: the current node, the elements ot the left, and the elements to the right.

Memory Locality

Ordering the elements in dfs in order is likely better for divide and conquer style problems. The main memory access pattern that we want to be fast is the following: If I have a parent, I hope to be able to access the children fast. So we want the children to be close to the parent. While in bfs order, the root’s children are literally right next to it, the children of nodes in the the second to last level of the tree could be extremly far apart (possibly n/2 elements away!). With dfs order, as you go down the tree, you gain better and better locality.

Modules

A complete binary tree stored in a Vec laid out in bfs order.
A complete binary tree stored in a Vec laid out in dfs in order.

Structs

Bfs Iterator. Each call to next() returns the next element in bfs order. Internally uses a VecDeque for the queue.
A level descriptor.
Dfs in order iterator. Each call to next() will return the next element in dfs in order. Internally uses a Vec for the stack.
Dfs preorder iterator. Each call to next() will return the next element in dfs order. Internally uses a Vec for the stack.
A wrapper iterator that will additionally return the depth of each element.
Map iterator adapter
Tree visitor that zips up two seperate visitors. If one of the iterators returns None for its children, this iterator will return None.

Traits

If implemented, then the level_remaining_hint must return the exact height of the tree. If this is implemented, then the exact number of nodes that will be returned by a dfs or bfs traversal is known so those iterators can implement TrustedLen in this case.
The trait this crate revoles around. A complete binary tree visitor.

Functions

Compute the number of nodes in a complete binary tree based on a height.