diff --git a/README.md b/README.md index a5a1b71..d5a2baf 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/src/lib.rs b/src/lib.rs index a8953f1..30e98f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 _)) @@ -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 { Empty, @@ -52,7 +54,7 @@ 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("_"), @@ -60,14 +62,16 @@ where } } + /// 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 bool>) -> Option { match self { Empty => None, @@ -75,6 +79,7 @@ where } } + /// find position of element pub fn index_of(&self, item: &T) -> Option { match self { Empty => None, @@ -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 { + /// 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)), } } @@ -123,21 +130,34 @@ where pub fn assoc(&self, idx: usize, item: T) -> Result { 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 { 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 { if self.is_empty() { Err(String::from("calling rest on empty")) @@ -196,12 +216,14 @@ 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))), @@ -209,6 +231,7 @@ where } } + /// optimized for armotized `O(1)` at best cases pub fn drop_left(&self) -> Self { match self { Empty => TernaryTreeList::Empty, @@ -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)?)) + } } } @@ -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), } } } diff --git a/src/tree.rs b/src/tree.rs index 71d2ab7..4e9a0ca 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -1,3 +1,9 @@ +//! internal implementation of this tree. +//! this file should be optimized for speed, even to skip some checks repeatedly down the tree, +//! checks can be added at the struct that wraps this tree + +mod finger; + use std::cell::RefCell; use std::cmp::Ordering; use std::fmt; @@ -6,9 +12,9 @@ use std::hash::{Hash, Hasher}; use std::ops::Index; use std::sync::Arc; -use crate::util::divide_ternary_sizes; +use crate::util::{divide_ternary_sizes, triple_size}; -/// internal tree structure without empty nodes +/// internal tree structure, it can't be empty #[derive(Clone, Debug)] pub enum TernaryTree { Leaf(Arc), @@ -117,7 +123,7 @@ where } } - /// turn into a compare representation, with `_` for holes + /// turn into a representation in triples, with `_` for holes pub fn format_inline(&self) -> String { match self { Leaf(value) => value.to_string(), @@ -132,14 +138,6 @@ where } } - pub fn get(&self, idx: usize) -> Option<&T> { - if idx >= self.len() { - None - } else { - self.ref_get(idx) - } - } - pub fn find_index(&self, f: Arc bool>) -> Option { match self { Leaf(value) => { @@ -183,7 +181,7 @@ where } /// recursively check structure - pub fn is_shape_same(&self, ys: &Self) -> bool { + pub fn eq_shape(&self, ys: &Self) -> bool { if self.len() != ys.len() { return false; } @@ -197,7 +195,7 @@ where middle: middle2, .. }, - ) => left.is_shape_same(left2) && middle.is_shape_same(middle2), + ) => left.eq_shape(left2) && middle.eq_shape(middle2), ( Branch3 { left, middle, right, .. }, Branch3 { @@ -206,7 +204,7 @@ where right: right2, .. }, - ) => left.is_shape_same(left2) && middle.is_shape_same(middle2) && right.is_shape_same(right2), + ) => left.eq_shape(left2) && middle.eq_shape(middle2) && right.eq_shape(right2), (_, _) => false, } @@ -221,7 +219,8 @@ where acc } - pub fn ref_get(&self, idx: usize) -> Option<&T> { + /// get with reference, but index is not checked, returns last element if too large + pub fn ref_get(&self, idx: usize) -> &T { // println!("get: {} {}", self.format_inline(), idx); // if idx >= self.len() { // println!("get from out of bound: {} {}", idx, self.len()); @@ -231,11 +230,13 @@ where Branch3 { left, middle, right, .. } => { let base = left.len(); if idx < base { - left.ref_get(idx) - } else if idx < base + middle.len() { - middle.ref_get(idx - base) + return left.ref_get(idx); + } + let idx_in_middle = idx - base; + if idx_in_middle < middle.len() { + middle.ref_get(idx_in_middle) } else { - right.ref_get(idx - base - middle.len()) + right.ref_get(idx_in_middle - middle.len()) } } Branch2 { left, middle, .. } => { @@ -246,62 +247,43 @@ where middle.ref_get(idx - base) } } - Leaf(value) => Some(value), + Leaf(value) => value, } } - /// get via go down the branch with a mutable loop - pub fn loop_get(&self, original_idx: usize) -> Option { - let mut tree_parent = self.to_owned(); + /// get am element via drilling down the branch with a mutable loop, + /// supposed to be faster than `ref_get` since it's more like VM instructions + pub fn loop_get(&'a self, original_idx: usize) -> &'a T { + let r = RefCell::new(self); + let mut tree_parent = r.borrow_mut(); let mut idx = original_idx; loop { - match tree_parent { + match *tree_parent { Leaf(value) => { - if idx == 0 { - return Some((*value).to_owned()); - } else { - println!("[warning] Cannot get from leaf with index {}", idx); - return None; - } + return value; } - Branch2 { left, middle, size, .. } => { - if idx > size - 1 { - println!("[warning] Index too large at {} from {}", idx, size); - return None; - } - - if left.len() + middle.len() != size { - unreachable!("tree.size does not match sum case branch sizes"); - } - + Branch2 { left, middle, .. } => { if idx < left.len() { - tree_parent = (*left).to_owned(); + *tree_parent = left; } else { - tree_parent = (*middle).to_owned(); + *tree_parent = middle; idx -= left.len(); } } - Branch3 { - left, middle, right, size, .. - } => { - if idx > size - 1 { - println!("[warning] Index too large at {} from {}", idx, size); - return None; + Branch3 { left, middle, right, .. } => { + if idx < left.len() { + *tree_parent = left; + continue; } + idx -= left.len(); - if left.len() + middle.len() + right.len() != size { - unreachable!("tree.size does not match sum case branch sizes"); + if idx < middle.len() { + *tree_parent = middle; + continue; } - if idx < left.len() { - tree_parent = (*left).to_owned(); - } else if idx < left.len() + middle.len() { - tree_parent = (*middle).to_owned(); - idx -= left.len(); - } else { - tree_parent = (*right).to_owned(); - idx -= left.len() + middle.len(); - } + *tree_parent = right; + idx -= middle.len(); } } } @@ -322,11 +304,9 @@ where Branch3 { right, .. } => right.last(), } } - pub fn assoc(&self, idx: usize, item: T) -> Result { - if idx > self.len() - 1 { - return Err(format!("Index too large {} for {}", idx, self.format_inline())); - } + // insert new element at position, does not check whether the index is out of bound + pub fn assoc(&self, idx: usize, item: T) -> Result { match self { Leaf { .. } => { if idx == 0 { @@ -336,14 +316,6 @@ where } } Branch2 { left, middle, size, .. } => { - if left.len() + middle.len() != *size { - return Err(format!( - "tree size {} does not match sum case branch sizes, {}", - size, - self.format_inline() - )); - } - if idx < left.len() { let changed_branch = left.assoc(idx, item)?; Ok(Branch2 { @@ -363,14 +335,6 @@ where Branch3 { left, middle, right, size, .. } => { - if left.len() + middle.len() + right.len() != *size { - return Err(format!( - "tree size {} does not match sum case branch sizes, {}", - size, - self.format_inline() - )); - } - if idx < left.len() { let changed_branch = left.assoc(idx, item)?; Ok(Branch3 { @@ -399,25 +363,12 @@ where } } } - pub fn dissoc(&self, idx: usize) -> Result { - if idx > self.len() - 1 { - return Err(format!("Index too large {} for {}", idx, self.len())); - } else if self.len() == 1 { - // idx already == 0 - return Err(String::from("Cannot remove from singleton list to empty")); - } + // remove element from give position, does not check whether the index is out of bound + pub fn dissoc(&self, idx: usize) -> Result { match self { Leaf { .. } => unreachable!("dissoc should be handled at branches"), Branch2 { left, middle, size, .. } => { - if left.len() + middle.len() != *size { - return Err(format!( - "tree {} does not match sum from branch sizes {}", - self.format_inline(), - self.len() - )); - } - if idx < left.len() { if left.len() == 1 { Ok((**middle).to_owned()) @@ -794,215 +745,6 @@ where } } - // for main branches detect keep a finger-tree like shallow-deep-shallow shape - fn push_right_main(&self, item: Self, n: u8) -> Self { - // println!(" iter: {} {:?}", self.format_inline(), mark); - - if self.len() + item.len() <= triple_size(n) { - self.push_right_side(item) - } else { - match self { - Leaf(_) => self.push_right_side(item), - Branch2 { size, left, middle, .. } => { - if middle.len() + item.len() > triple_size(n) { - Branch3 { - size: size + item.len(), - left: left.to_owned(), - middle: middle.to_owned(), - right: Arc::new(item), - } - } else { - // pile items in the compact way like in sides - // println!(" try n: {}", n); - let item_size = item.len(); - let changed_branch = middle.push_right_side(item); - - Branch2 { - size: size + item_size, - left: left.to_owned(), - middle: Arc::new(changed_branch), - } - } - } - Branch3 { - size, left, middle, right, .. - } => { - // println!(" b3 n: {}", n); - if right.len() + item.len() > triple_size(n - 1) { - let changed_branch = middle.push_right_main((**right).to_owned(), n + 1); - Branch3 { - size: left.len() + changed_branch.len() + item.len(), - left: left.to_owned(), - middle: Arc::new(changed_branch), - right: Arc::new(item), - } - } else { - let item_size = item.len(); - let changed_branch = right.push_right_side(item); - Branch3 { - size: size + item_size, - left: left.to_owned(), - middle: middle.to_owned(), - right: Arc::new(changed_branch), - } - } - } - } - } - } - - // just pile items in the compact way - fn push_right_side(&self, item: Self) -> Self { - // println!(" iter: {} {:?}", self.format_inline(), mark); - match self { - Leaf(a) => Branch2 { - size: 1 + item.len(), - left: Arc::new(Leaf(a.to_owned())), - middle: Arc::new(item), - }, - Branch2 { size, left, middle, .. } => { - if middle.len() + item.len() > left.len() { - Branch3 { - size: size + item.len(), - left: left.to_owned(), - middle: middle.to_owned(), - right: Arc::new(item), - } - } else { - let changed_branch = middle.push_right_side(item.to_owned()); - Branch2 { - size: size + item.len(), - left: left.to_owned(), - middle: Arc::new(changed_branch), - } - } - } - Branch3 { - size, left, middle, right, .. - } => { - if right.len() + item.len() > middle.len() { - Branch2 { - size: size + item.len(), - left: Arc::new(self.to_owned()), - middle: Arc::new(item), - } - } else { - let changed_branch = right.push_right_side(item.to_owned()); - Branch3 { - size: size + item.len(), - left: left.to_owned(), - middle: middle.to_owned(), - right: Arc::new(changed_branch), - } - } - } - } - } - - pub fn push_right(&self, item: T) -> Self { - // start with 2 so its left child branch has capability of only 3^1 - self.push_right_main(Leaf(Arc::new(item)), 2) - } - - pub fn drop_left(&self) -> Self { - match self { - Leaf(_) => { - unreachable!("not expected empty node inside tree") - } - Branch2 { size, left, middle, .. } => { - if left.len() == 1 { - (**middle).to_owned() - } else { - let changed_branch = left.drop_left(); - match changed_branch { - Branch2 { - left: b_left, - middle: b_middle, - .. - } => Branch3 { - size: size - 1, - left: b_left, - middle: b_middle, - right: middle.to_owned(), - }, - Branch3 { - left: b_left, - middle: b_middle, - right: b_right, - .. - } => { - let internal_branch = Branch2 { - size: b_middle.len() + b_right.len(), - left: b_middle, - middle: b_right, - }; - Branch3 { - size: size - 1, - left: b_left, - middle: Arc::new(internal_branch), - right: middle.to_owned(), - } - } - _ => Branch2 { - size: size - 1, - left: Arc::new(changed_branch), - middle: middle.to_owned(), - }, - } - } - } - Branch3 { - size, left, middle, right, .. - } => { - if left.len() == 1 { - match &**middle { - Branch2 { - left: b_left, - middle: b_middle, - .. - } => Branch3 { - size: size - 1, - left: b_left.to_owned(), - middle: b_middle.to_owned(), - right: right.to_owned(), - }, - Branch3 { - left: b_left, - middle: b_middle, - right: b_right, - .. - } => { - let internal_branch = Branch2 { - size: b_middle.len() + b_right.len(), - left: b_middle.to_owned(), - middle: b_right.to_owned(), - }; - Branch3 { - size: size - 1, - left: b_left.to_owned(), - middle: Arc::new(internal_branch), - right: right.to_owned(), - } - } - _ => Branch2 { - size: size - 1, - left: middle.to_owned(), - middle: right.to_owned(), - }, - } - } else { - let changed_branch = left.drop_left(); - Branch3 { - size: size - 1, - left: Arc::new(changed_branch), - middle: middle.to_owned(), - right: right.to_owned(), - } - } - } - } - } - pub fn concat(raw: &[TernaryTree]) -> Self { let mut xs_groups: Vec> = Vec::with_capacity(raw.len()); for x in raw { @@ -1066,18 +808,11 @@ where } } } - // excludes value at end_idx, kept aligned with JS & Clojure + + /// excludes value at end_idx, kept aligned with JS & Clojure + /// does not check at inside pub fn slice(&self, start_idx: usize, end_idx: usize) -> Result { // 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 Err(format!("Slice range empty {}..{} for {}", start_idx, end_idx, self)); - } match self { Leaf { .. } => { @@ -1194,13 +929,10 @@ where } pub fn to_vec(&self) -> Vec { - let mut xs = vec![]; - - // TODO + let mut xs = Vec::with_capacity(self.len()); for item in self { xs.push(item.to_owned()); } - xs } @@ -1244,9 +976,9 @@ where fn next(&mut self) -> Option { if self.index < self.value.len() { // println!("get: {} {}", self.value.format_inline(), self.index); - let ret = self.value.ref_get(self.index); + let ret = self.value.loop_get(self.index); self.index += 1; - ret + Some(ret) } else { None } @@ -1260,7 +992,7 @@ impl Part } for idx in 0..ys.len() { - if self.ref_get(idx) != ys.ref_get(idx) { + if self.loop_get(idx) != ys.loop_get(idx) { return false; } } @@ -1287,7 +1019,7 @@ where fn cmp(&self, other: &Self) -> Ordering { if self.len() == other.len() { for idx in 0..self.len() { - match self.ref_get(idx).cmp(&other.ref_get(idx)) { + match self.loop_get(idx).cmp(other.loop_get(idx)) { Ordering::Equal => {} a => return a, } @@ -1308,7 +1040,7 @@ where fn index<'b>(&self, idx: usize) -> &Self::Output { // println!("get: {} {}", self.format_inline(), idx); - self.ref_get(idx).expect("get from list") + self.loop_get(idx) } } @@ -1317,19 +1049,15 @@ where T: Clone + Display + Eq + PartialEq + Debug + Ord + PartialOrd + Hash, { fn hash(&self, state: &mut H) { - "ternary".hash(state); match self { Leaf(value) => { - "leaf".hash(state); value.hash(state); } Branch2 { left, middle, .. } => { - "branch".hash(state); left.hash(state); middle.hash(state); } Branch3 { left, middle, right, .. } => { - "branch".hash(state); left.hash(state); middle.hash(state); right.hash(state); @@ -1361,8 +1089,3 @@ where } } } - -fn triple_size(t: u8) -> usize { - let n: usize = 3; - n.pow(t as u32) -} diff --git a/src/tree/finger.rs b/src/tree/finger.rs new file mode 100644 index 0000000..edad385 --- /dev/null +++ b/src/tree/finger.rs @@ -0,0 +1,234 @@ +//! contains tricks for faster operating on both ends, +//! the trick is learnt from finger-tree trying to maintain shallow branches near both ends, +//! thus adding/removing there can be cheap and somehow reaching `O(1)` at best cases. +//! branches in the middle of this "pyramid" is the deepest. +//! +//! Finger-tree encodes tree structure in its ADT, however this file uses a dynamic solution, +//! i.e. detects sizes of branches, and to decide where to put new elements. +//! it's not a perfect structure for best speed, but trying to be reaching. +//! +//! ![](https://pbs.twimg.com/media/FRc3gB7aQAA1pBb?format=jpg&name=4096x4096) +//! +//! 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). + +use super::TernaryTree::{self, *}; + +use std::fmt::{Debug, Display}; +use std::hash::Hash; +use std::sync::Arc; + +use crate::util::triple_size; + +impl<'a, T> TernaryTree +where + T: Clone + Display + Eq + PartialEq + Debug + Ord + PartialOrd + Hash, +{ + // for main branches detect keep a finger-tree like shallow-deep-shallow shape + fn push_right_main(&self, item: Self, n: u8) -> Self { + // println!(" iter: {} {:?}", self.format_inline(), mark); + + if self.len() + item.len() <= triple_size(n) { + self.push_right_side(item) + } else { + match self { + Leaf(_) => self.push_right_side(item), + Branch2 { size, left, middle, .. } => { + if middle.len() + item.len() > triple_size(n) { + Branch3 { + size: size + item.len(), + left: left.to_owned(), + middle: middle.to_owned(), + right: Arc::new(item), + } + } else { + // pile items in the compact way like in sides + // println!(" try n: {}", n); + let item_size = item.len(); + let changed_branch = middle.push_right_side(item); + + Branch2 { + size: size + item_size, + left: left.to_owned(), + middle: Arc::new(changed_branch), + } + } + } + Branch3 { + size, left, middle, right, .. + } => { + // println!(" b3 n: {}", n); + if right.len() + item.len() > triple_size(n - 1) { + let changed_branch = middle.push_right_main((**right).to_owned(), n + 1); + Branch3 { + size: left.len() + changed_branch.len() + item.len(), + left: left.to_owned(), + middle: Arc::new(changed_branch), + right: Arc::new(item), + } + } else { + let item_size = item.len(); + let changed_branch = right.push_right_side(item); + Branch3 { + size: size + item_size, + left: left.to_owned(), + middle: middle.to_owned(), + right: Arc::new(changed_branch), + } + } + } + } + } + } + + // just pile items in the compact way + fn push_right_side(&self, item: Self) -> Self { + // println!(" iter: {} {:?}", self.format_inline(), mark); + match self { + Leaf(a) => Branch2 { + size: 1 + item.len(), + left: Arc::new(Leaf(a.to_owned())), + middle: Arc::new(item), + }, + Branch2 { size, left, middle, .. } => { + if middle.len() + item.len() > left.len() { + Branch3 { + size: size + item.len(), + left: left.to_owned(), + middle: middle.to_owned(), + right: Arc::new(item), + } + } else { + let changed_branch = middle.push_right_side(item.to_owned()); + Branch2 { + size: size + item.len(), + left: left.to_owned(), + middle: Arc::new(changed_branch), + } + } + } + Branch3 { + size, left, middle, right, .. + } => { + if right.len() + item.len() > middle.len() { + Branch2 { + size: size + item.len(), + left: Arc::new(self.to_owned()), + middle: Arc::new(item), + } + } else { + let changed_branch = right.push_right_side(item.to_owned()); + Branch3 { + size: size + item.len(), + left: left.to_owned(), + middle: middle.to_owned(), + right: Arc::new(changed_branch), + } + } + } + } + } + + pub fn push_right(&self, item: T) -> Self { + // start with 2 so its left child branch has capability of only 3^1 + self.push_right_main(Leaf(Arc::new(item)), 2) + } + + pub fn drop_left(&self) -> Self { + match self { + Leaf(_) => { + unreachable!("not expected empty node inside tree") + } + Branch2 { size, left, middle, .. } => { + if left.len() == 1 { + (**middle).to_owned() + } else { + let changed_branch = left.drop_left(); + match changed_branch { + Branch2 { + left: b_left, + middle: b_middle, + .. + } => Branch3 { + size: size - 1, + left: b_left, + middle: b_middle, + right: middle.to_owned(), + }, + Branch3 { + left: b_left, + middle: b_middle, + right: b_right, + .. + } => { + let internal_branch = Branch2 { + size: b_middle.len() + b_right.len(), + left: b_middle, + middle: b_right, + }; + Branch3 { + size: size - 1, + left: b_left, + middle: Arc::new(internal_branch), + right: middle.to_owned(), + } + } + _ => Branch2 { + size: size - 1, + left: Arc::new(changed_branch), + middle: middle.to_owned(), + }, + } + } + } + Branch3 { + size, left, middle, right, .. + } => { + if left.len() == 1 { + match &**middle { + Branch2 { + left: b_left, + middle: b_middle, + .. + } => Branch3 { + size: size - 1, + left: b_left.to_owned(), + middle: b_middle.to_owned(), + right: right.to_owned(), + }, + Branch3 { + left: b_left, + middle: b_middle, + right: b_right, + .. + } => { + let internal_branch = Branch2 { + size: b_middle.len() + b_right.len(), + left: b_middle.to_owned(), + middle: b_right.to_owned(), + }; + Branch3 { + size: size - 1, + left: b_left.to_owned(), + middle: Arc::new(internal_branch), + right: right.to_owned(), + } + } + _ => Branch2 { + size: size - 1, + left: middle.to_owned(), + middle: right.to_owned(), + }, + } + } else { + let changed_branch = left.drop_left(); + Branch3 { + size: size - 1, + left: Arc::new(changed_branch), + middle: middle.to_owned(), + right: right.to_owned(), + } + } + } + } + } +} diff --git a/src/util.rs b/src/util.rs index fa80eff..3ebb20f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,11 @@ -pub fn divide_ternary_sizes(size: usize) -> (usize, usize, usize) { +pub(crate) fn divide_ternary_sizes(size: usize) -> (usize, usize, usize) { let group_size = (size / 3) as usize; let extra = size - group_size * 3; (group_size, group_size + extra, group_size) } + +pub(crate) fn triple_size(t: u8) -> usize { + let n: usize = 3; + n.pow(t as u32) +} diff --git a/tests/list_tests.rs b/tests/list_tests.rs index 02ebe87..bc5367f 100644 --- a/tests/list_tests.rs +++ b/tests/list_tests.rs @@ -49,7 +49,7 @@ fn list_operations() -> Result<(), String> { // get for (idx, v) in origin11.iter().enumerate() { - assert_eq!(*v, data11.loop_get(idx).unwrap()); + assert_eq!(v, data11.loop_get(idx).unwrap()); } assert_eq!(data11.first(), Some(&1)); @@ -59,8 +59,8 @@ fn list_operations() -> Result<(), String> { let origin5 = &[1, 2, 3, 4, 5]; let data5 = TernaryTreeList::from(origin5); let updated = data5.assoc(3, 10)?; - assert_eq!(updated.loop_get(3).unwrap(), 10); - assert_eq!(data5.loop_get(3).unwrap(), 4); + assert_eq!(*updated.loop_get(3).unwrap(), 10); + assert_eq!(*data5.loop_get(3).unwrap(), 4); assert_eq!(updated.len(), data5.len()); for idx in 0..data5.len() { @@ -188,9 +188,9 @@ fn check_equality() -> Result<(), String> { let data4n = TernaryTreeList::from(&origin4); let data4_made = TernaryTreeList::from(&[2, 3, 4]).prepend(1); - assert!(data4.is_shape_same(&data4)); - assert!(data4.is_shape_same(&data4n)); - assert!(!data4.is_shape_same(&data4_made)); + assert!(data4.eq_shape(&data4)); + assert!(data4.eq_shape(&data4n)); + assert!(!data4.eq_shape(&data4_made)); assert!(data4 == data4n); assert!(data4 == data4_made);