use serde::{Deserialize, Serialize};
use std::fmt::{self, Debug};
mod utils;
mod node_content;
pub use node_content::{FullNodeContent, HiddenNodeContent, Mergeable};
mod tree_builder;
pub use tree_builder::multi_threaded;
pub use tree_builder::{
single_threaded, BinaryTreeBuilder, InputLeafNode, TreeBuildError, MIN_STORE_DEPTH,
};
mod path_siblings;
pub use path_siblings::{
PathSiblings, PathSiblingsBuildError, PathSiblingsError, PathSiblingsWriteError,
};
mod height;
pub use height::{Height, HeightError, MAX_HEIGHT, MIN_HEIGHT};
use crate::utils::ErrOnSome;
pub const MIN_RECOMMENDED_SPARSITY: u8 = 2;
#[derive(Serialize, Deserialize)]
pub struct BinaryTree<C: fmt::Display> {
root: Node<C>,
store: Store<C>,
height: Height,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Node<C: fmt::Display> {
pub coord: Coordinate,
pub content: C,
}
#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
pub struct Coordinate {
pub y: u8,
pub x: height::XCoord,
}
#[derive(Serialize, Deserialize)]
pub enum Store<C: fmt::Display> {
MultiThreadedStore(multi_threaded::DashMapStore<C>),
SingleThreadedStore(single_threaded::HashMapStore<C>),
}
impl<C: Clone + fmt::Display> BinaryTree<C> {
pub fn height(&self) -> &Height {
&self.height
}
pub fn root(&self) -> &Node<C> {
&self.root
}
pub fn get_node(&self, coord: &Coordinate) -> Option<Node<C>> {
self.store.get_node(coord)
}
pub fn get_leaf_node(&self, x_coord: u64) -> Option<Node<C>> {
let coord = Coordinate { x: x_coord, y: 0 };
self.get_node(&coord)
}
}
impl Coordinate {
pub fn to_bytes(&self) -> [u8; 32] {
let mut c = [0u8; 32];
let (left, mid) = c.split_at_mut(1);
left.copy_from_slice(&self.y.to_le_bytes());
let (mid, _right) = mid.split_at_mut(8);
mid.copy_from_slice(&self.x.to_le_bytes());
c
}
fn orientation(&self) -> NodeOrientation {
if self.x % 2 == 0 {
NodeOrientation::Left
} else {
NodeOrientation::Right
}
}
fn sibling_coord(&self) -> Coordinate {
let x = match self.orientation() {
NodeOrientation::Left => self.x + 1,
NodeOrientation::Right => self.x - 1,
};
Coordinate { y: self.y, x }
}
fn parent_coord(&self) -> Coordinate {
Coordinate {
y: self.y + 1,
x: self.x / 2,
}
}
fn subtree_x_coord_bounds(&self) -> (u64, u64) {
let first_leaf_x_coord = |x: u64, y: u8| 2u64.pow(y as u32) * x;
let x_coord_min = first_leaf_x_coord(self.x, self.y);
let x_coord_max = first_leaf_x_coord(self.x + 1, self.y) - 1;
(x_coord_min, x_coord_max)
}
fn to_height(&self) -> Height {
Height::expect_from(self.y + 1)
}
fn bottom_layer_leaf_from(x_coord: u64) -> Self {
Coordinate { x: x_coord, y: 0 }
}
}
impl<C: fmt::Display> Node<C> {
fn orientation(&self) -> NodeOrientation {
self.coord.orientation()
}
fn is_left_sibling_of(&self, other: &Node<C>) -> bool {
match self.orientation() {
NodeOrientation::Left => {
self.coord.y == other.coord.y && self.coord.x + 1 == other.coord.x
}
NodeOrientation::Right => false,
}
}
fn is_right_sibling_of(&self, other: &Node<C>) -> bool {
match self.orientation() {
NodeOrientation::Left => false,
NodeOrientation::Right => {
self.coord.x > 0
&& self.coord.y == other.coord.y
&& self.coord.x - 1 == other.coord.x
}
}
}
pub fn coord(&self) -> &Coordinate {
&self.coord
}
fn sibling_coord(&self) -> Coordinate {
self.coord.sibling_coord()
}
fn parent_coord(&self) -> Coordinate {
self.coord.parent_coord()
}
pub fn convert<B: From<C> + fmt::Display>(self) -> Node<B> {
Node {
content: self.content.into(),
coord: self.coord,
}
}
}
impl<C: Clone + fmt::Display> Store<C> {
fn get_node(&self, coord: &Coordinate) -> Option<Node<C>> {
match self {
Store::MultiThreadedStore(store) => store.get_node(coord),
Store::SingleThreadedStore(store) => store.get_node(coord),
}
}
fn len(&self) -> usize {
match self {
Store::MultiThreadedStore(store) => store.len(),
Store::SingleThreadedStore(store) => store.len(),
}
}
}
impl<C: fmt::Display + Clone> fmt::Debug for BinaryTree<C> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "root: {}, height: {:?}", self.root, self.height)
}
}
impl<C: fmt::Display> fmt::Display for Node<C> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(content: {}, coord: {:?})", self.content, self.coord)
}
}
impl fmt::Display for Coordinate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(x: {:?}, y: {:?})", self.x, self.y)
}
}
#[derive(Debug, PartialEq)]
enum NodeOrientation {
Left,
Right,
}
enum Sibling<C: fmt::Display> {
Left(Node<C>),
Right(Node<C>),
}
struct MatchedPair<C: fmt::Display> {
left: Node<C>,
right: Node<C>,
}
impl<C: fmt::Display> From<Node<C>> for Sibling<C> {
fn from(node: Node<C>) -> Self {
match node.orientation() {
NodeOrientation::Left => Sibling::Left(node),
NodeOrientation::Right => Sibling::Right(node),
}
}
}
impl<C: Mergeable + fmt::Display> MatchedPair<C> {
fn merge(&self) -> Node<C> {
Node {
coord: self.left.parent_coord(),
content: C::merge(&self.left.content, &self.right.content),
}
}
}
impl<C: fmt::Display> From<(Node<C>, Node<C>)> for MatchedPair<C> {
fn from(siblings: (Node<C>, Node<C>)) -> Self {
if siblings.1.is_right_sibling_of(&siblings.0) {
MatchedPair {
left: siblings.0,
right: siblings.1,
}
} else if siblings.1.is_left_sibling_of(&siblings.0) {
MatchedPair {
left: siblings.1,
right: siblings.0,
}
} else {
panic!(
"A pair cannot be made from 2 nodes that are not siblings {:?} {:?}",
siblings.0.coord.clone(),
siblings.1.coord.clone(),
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::binary_tree::utils::test_utils::single_leaf;
#[test]
fn coord_byte_conversion_correct() {
let x = 258;
let y = 12;
let coord = Coordinate { x, y };
let bytes = coord.to_bytes();
assert_eq!(bytes.len(), 32, "Byte array should be 256 bits");
assert_eq!(
bytes[0], y,
"1st element of byte array should be equal to y-coord"
);
assert_eq!(
bytes[1], 2,
"2nd element of byte array should be equal to least significant byte of x-coord"
);
assert_eq!(
bytes[2], 1,
"3rd element of byte array should be equal to most significant byte of x-coord"
);
for item in bytes.iter().skip(3) {
assert_eq!(
*item, 0,
"4th-last elements of byte array should be equal to 0"
);
}
}
#[test]
fn node_orientation_correctly_determined() {
let x_coord = 0;
let left_node = single_leaf(x_coord).into_node();
assert_eq!(left_node.orientation(), NodeOrientation::Left);
let x_coord = 1;
let right_node = single_leaf(x_coord).into_node();
assert_eq!(right_node.orientation(), NodeOrientation::Right);
}
#[test]
fn is_sibling_of_works() {
let height = Height::expect_from(5);
let x_coord = 16;
let left_node = single_leaf(x_coord).into_node();
let x_coord = 17;
let right_node = single_leaf(x_coord).into_node();
assert!(right_node.is_right_sibling_of(&left_node));
assert!(!right_node.is_left_sibling_of(&left_node));
assert!(left_node.is_left_sibling_of(&right_node));
assert!(!left_node.is_right_sibling_of(&right_node));
for i in 0..height.max_bottom_layer_nodes() {
let node = single_leaf(i).into_node();
if left_node.coord.x != i && right_node.coord.x != i {
assert!(!right_node.is_right_sibling_of(&node));
assert!(!right_node.is_left_sibling_of(&node));
assert!(!left_node.is_left_sibling_of(&node));
assert!(!left_node.is_right_sibling_of(&node));
}
}
}
#[test]
fn sibling_coord_calculated_correctly() {
let x_coord = 5;
let right_node = single_leaf(x_coord).into_node();
let sibling_coord = right_node.sibling_coord();
assert_eq!(
sibling_coord.y, 0,
"Sibling should be on the bottom layer (y-coord == 0)"
);
assert_eq!(sibling_coord.x, 4, "Sibling's x-coord should be 1 less than the node's x-coord because the node is a right sibling");
let x_coord = 0;
let left_node = single_leaf(x_coord).into_node();
let sibling_coord = left_node.sibling_coord();
assert_eq!(
sibling_coord.y, 0,
"Sibling should be on the bottom layer (y-coord == 0)"
);
assert_eq!(sibling_coord.x, 1, "Sibling's x-coord should be 1 more than the node's x-coord because the node is a left sibling");
}
#[test]
fn parent_coord_calculated_correctly() {
let x_coord = 5;
let right_node = single_leaf(x_coord).into_node();
let right_parent_coord = right_node.parent_coord();
let x_coord = 4;
let left_node = single_leaf(x_coord).into_node();
let left_parent_coord = left_node.parent_coord();
assert_eq!(
right_parent_coord, left_parent_coord,
"Left and right siblings should have same parent coord"
);
assert_eq!(
right_parent_coord.y, 1,
"Parent's y-coord should be 1 more than child's"
);
assert_eq!(
right_parent_coord.x, 2,
"Parent's x-coord should be half the child's"
);
}
#[test]
fn input_node_correctly_converted_into_node() {
let x_coord = 5;
let input_node = single_leaf(x_coord);
let content = input_node.content.clone();
let node = input_node.into_node();
assert_eq!(
node.coord.x, 5,
"Node's x-coord should match input leaf node's"
);
assert_eq!(
node.coord.y, 0,
"Node's y-coord should be 0 because all input nodes are assumed to be on bottom layer"
);
assert_eq!(content, node.content);
}
#[test]
fn sibling_from_node_works() {
let x_coord = 11;
let right_node = single_leaf(x_coord).into_node();
let sibling = Sibling::from(right_node);
match sibling {
Sibling::Left(_) => panic!("Node should be a right sibling"),
Sibling::Right(_) => {}
}
let x_coord = 16;
let left_node = single_leaf(x_coord).into_node();
let sibling = Sibling::from(left_node);
match sibling {
Sibling::Right(_) => panic!("Node should be a left sibling"),
Sibling::Left(_) => {}
}
}
#[test]
fn matched_pair_merge_works() {
let x_coord = 17;
let right = single_leaf(x_coord).into_node();
let x_coord = 16;
let left = single_leaf(x_coord).into_node();
let pair = MatchedPair::from((left, right));
let parent = pair.merge();
assert_eq!(
parent.coord.y, 1,
"Parent's y-coord should be 1 more than child's"
);
assert_eq!(
parent.coord.x, 8,
"Parent's x-coord should be half the child's"
);
}
#[test]
fn subtree_bounds_works() {
let coord = Coordinate { x: 2, y: 2 };
let (lower, upper) = coord.subtree_x_coord_bounds();
assert_eq!(lower, 8, "Incorrect lower x-coord bound for subtree");
assert_eq!(upper, 11, "Incorrect upper x-coord bound for subtree");
}
}