Skip to content

Commit

Permalink
adding docs; split new file; optimizing on t.loop_get(i)
Browse files Browse the repository at this point in the history
  • Loading branch information
tiye committed May 30, 2022
1 parent 6d58702 commit 3602848
Show file tree
Hide file tree
Showing 6 changed files with 359 additions and 360 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## Structrual sharing tree for Calcit
## Persistetnt structrual sharing tree for Calcit

> borrowed ideas from 2-3 tree and finger-tree.
> a variant of 2-3 tree, with enhancements on ternary branching, optimized with tricks like finger-tree.
`t.pop_left()` and `t.push_right(..)` is optimized to be amortized `O(1)` at best cases and `O(log n)` when restructuring involed.

Tree layout from 0 to 159 watch [video](https://www.bilibili.com/video/BV1F34y147V7) or try [live demo](https://github.com/calcit-lang/explain-ternary-tree).

Expand Down
75 changes: 55 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! ternary tree with structural sharing.
//! a bit like 2-3 finger tree, however this library does not handle balancing well.
//! meanwhile, it is also an interesting library displaying with triples:
//! A variant of 2-3 tree, with enhancements on ternary branching, optimized with tricks like finger-tree.
//! `t.push_right(..)` is optimized to be amortized `O(1)` at best cases and `O(log n)` when restructuring involed.
//!
//! ![](https://pbs.twimg.com/media/FRc3gB7aQAA1pBb?format=jpg&name=4096x4096)
//!
//! it is also interesting to display it with triples:
//!
//! ```text
//! (((0 1 2) (3 4 5) (6 7 8)) ((9 10 11) (12 13 14) (15 16 17)) (18 19 _))
Expand All @@ -22,10 +25,9 @@ use std::hash::{Hash, Hasher};
use std::ops::Index;
use std::sync::Arc;

pub use tree::TernaryTree;

use crate::tree::TernaryTree::*;
use tree::TernaryTree::{self, *};

/// wraps TerarnaryTreeList with support for empty
#[derive(Clone, Debug)]
pub enum TernaryTreeList<T> {
Empty,
Expand All @@ -52,29 +54,32 @@ where
}
}

/// turn into a compare representation, with `_` for holes
/// turn into a representation in triples, `_` for holes
pub fn format_inline(&self) -> String {
match self {
Empty => String::from("_"),
Tree(t) => t.format_inline(),
}
}

/// get element in list by reference
pub fn get(&self, idx: usize) -> Option<&T> {
if self.is_empty() || idx >= self.len() {
None
} else {
self.ref_get(idx)
self.loop_get(idx)
}
}

/// find position of matched element in list(if exists)
pub fn find_index(&self, f: Arc<dyn Fn(&T) -> bool>) -> Option<i64> {
match self {
Empty => None,
Tree(t) => t.find_index(f),
}
}

/// find position of element
pub fn index_of(&self, item: &T) -> Option<usize> {
match self {
Empty => None,
Expand All @@ -83,27 +88,29 @@ where
}

/// recursively check structure
pub fn is_shape_same(&self, ys: &Self) -> bool {
pub fn eq_shape(&self, ys: &Self) -> bool {
match (self, ys) {
(Empty, Empty) => true,
(Empty, _) => false,
(_, Empty) => false,
(Tree(x), Tree(y)) => x.is_shape_same(y),
(Tree(x), Tree(y)) => x.eq_shape(y),
}
}

/// unchecked get reference of element
pub fn ref_get(&self, idx: usize) -> Option<&T> {
match self {
Empty => None,
Tree(t) => t.ref_get(idx),
Tree(t) => Some(t.ref_get(idx)),
}
}

/// get via go down the branch with a mutable loop
pub fn loop_get(&self, original_idx: usize) -> Option<T> {
/// unchecked get via go down the branch with a mutable loop
/// this function is SLOWER compared to `ref_get`, not used by default
pub fn loop_get(&self, original_idx: usize) -> Option<&T> {
match self {
Empty => None,
Tree(t) => t.loop_get(original_idx),
Tree(t) => Some(t.loop_get(original_idx)),
}
}

Expand All @@ -123,21 +130,34 @@ where
pub fn assoc(&self, idx: usize, item: T) -> Result<Self, String> {
match self {
Empty => Err(String::from("empty")),
Tree(t) => Ok(TernaryTreeList::Tree(t.assoc(idx, item)?)),
Tree(t) => {
if idx > self.len() - 1 {
return Err(format!("Index too large {} for {}", idx, self.format_inline()));
} else {
Ok(TernaryTreeList::Tree(t.assoc(idx, item)?))
}
}
}
}
pub fn dissoc(&self, idx: usize) -> Result<Self, String> {
match self {
Empty => Err(String::from("calling dissoc from empty")),
Tree(t) => {
if idx == 0 && t.len() == 1 {
Ok(TernaryTreeList::Empty)
} else {
if t.len() == 1 {
if idx == 0 {
Ok(Empty)
} else {
Err(format!("Index too large {} for {}", idx, self.format_inline()))
}
} else if idx < t.len() {
Ok(TernaryTreeList::Tree(t.dissoc(idx)?))
} else {
Err(format!("Index too large {} for {}", idx, self.format_inline()))
}
}
}
}
/// ternary tree operation of rest
pub fn rest(&self) -> Result<Self, String> {
if self.is_empty() {
Err(String::from("calling rest on empty"))
Expand Down Expand Up @@ -196,19 +216,22 @@ where
pub fn push(&self, item: T) -> Self {
self.append(item)
}
/// insert_after last element, this not optimzed for performance
pub fn append(&self, item: T) -> Self {
match self {
Empty => TernaryTreeList::Tree(TernaryTree::Leaf(Arc::new(item))),
Tree(t) => TernaryTreeList::Tree(t.append(item)),
}
}
/// optimized for armotized `O(1)` performance at best cases
pub fn push_right(&self, item: T) -> Self {
match self {
Empty => TernaryTreeList::Tree(TernaryTree::Leaf(Arc::new(item))),
Tree(t) => TernaryTreeList::Tree(t.push_right(item)),
}
}

/// optimized for armotized `O(1)` at best cases
pub fn drop_left(&self) -> Self {
match self {
Empty => TernaryTreeList::Empty,
Expand Down Expand Up @@ -249,7 +272,19 @@ where
}
match self {
Empty => Err(String::from("empty")),
Tree(t) => Ok(TernaryTreeList::Tree(t.slice(start_idx, end_idx)?)),
Tree(t) => {
// echo "slice {tree.formatListInline}: {start_idx}..{end_idx}"
if end_idx > self.len() {
return Err(format!("Slice range too large {} for {}", end_idx, self.format_inline()));
}
if start_idx > end_idx {
return Err(format!("Invalid slice range {}..{} for {}", start_idx, end_idx, self));
}
if start_idx == end_idx {
return Ok(TernaryTreeList::Empty);
}
Ok(TernaryTreeList::Tree(t.slice(start_idx, end_idx)?))
}
}
}

Expand Down Expand Up @@ -376,7 +411,7 @@ where
fn index<'b>(&self, idx: usize) -> &Self::Output {
match self {
Empty => panic!("index out of bounds"),
Tree(t) => t.ref_get(idx).expect("failed to index"),
Tree(t) => t.ref_get(idx),
}
}
}
Expand Down
Loading

0 comments on commit 3602848

Please sign in to comment.