scirs2_core/data_structures/rrb_tree.rs
1//! Persistent vector backed by a Relaxed Radix-Balanced (RRB) tree.
2//!
3//! All "mutating" operations return a *new version* of the vector while leaving
4//! the original intact. Unchanged subtrees are shared via `Arc`, so the
5//! amortised cost of each operation is O(log₃₂ N) **node allocations** rather
6//! than O(N).
7//!
8//! # Branching factor
9//!
10//! Each internal node holds up to [`BRANCHING`] (32) children. The tree height
11//! grows by one when the number of elements exceeds 32^(height+1). Index
12//! navigation uses 5-bit radix steps.
13//!
14//! # Operations
15//!
16//! | Operation | Allocation cost | Notes |
17//! |---------------|-----------------|------------------------------------|
18//! | `get` | O(1) | no allocation |
19//! | `push_back` | O(log N) | amortised, O(1) common case |
20//! | `pop_back` | O(log N) | |
21//! | `update` | O(log N) | path-copy from root to leaf |
22//! | `iter` | O(1) | lazy traversal via stack |
23//!
24//! # Example
25//!
26//! ```rust
27//! use scirs2_core::data_structures::PersistentVec;
28//!
29//! let v0 = PersistentVec::new();
30//! let v1 = v0.push_back(10u32);
31//! let v2 = v1.push_back(20u32);
32//! let v3 = v2.update(0, 99u32).expect("index 0 is valid");
33//!
34//! assert_eq!(v3.get(0), Some(&99u32));
35//! assert_eq!(v3.get(1), Some(&20u32));
36//! // Original v2 is unchanged:
37//! assert_eq!(v2.get(0), Some(&10u32));
38//! ```
39
40use std::sync::Arc;
41
42// ─────────────────────────────────────────────────────────────────────────────
43// Constants
44// ─────────────────────────────────────────────────────────────────────────────
45
46/// Branching factor: each internal node has up to 32 children.
47pub const BRANCHING: usize = 32;
48
49/// log₂(BRANCHING): bits consumed per tree level during radix navigation.
50pub const LOG_BRANCHING: usize = 5;
51
52/// Bitmask for extracting a single radix level index.
53const MASK: usize = BRANCHING - 1;
54
55// ─────────────────────────────────────────────────────────────────────────────
56// Internal node types
57// ─────────────────────────────────────────────────────────────────────────────
58
59/// A node in the RRB trie.
60///
61/// - `Internal`: holds up to `BRANCHING` child nodes.
62/// - `Leaf`: holds up to `BRANCHING` data elements.
63#[derive(Clone)]
64pub(crate) enum Node<T: Clone> {
65 Internal(Vec<Arc<Node<T>>>),
66 Leaf(Vec<T>),
67}
68
69impl<T: Clone> Node<T> {
70 // ------------------------------------------------------------------
71 // Navigation helpers
72 // ------------------------------------------------------------------
73
74 /// Access the child nodes of an internal node.
75 fn children(&self) -> &[Arc<Node<T>>] {
76 match self {
77 Node::Internal(ch) => ch,
78 Node::Leaf(_) => &[],
79 }
80 }
81
82 /// Access the data elements of a leaf node.
83 fn elements(&self) -> &[T] {
84 match self {
85 Node::Leaf(elems) => elems,
86 Node::Internal(_) => &[],
87 }
88 }
89
90 // ------------------------------------------------------------------
91 // Pure reads
92 // ------------------------------------------------------------------
93
94 /// Retrieve the element at `index` within the subtree rooted here.
95 ///
96 /// `shift` is `height * LOG_BRANCHING` — the number of bits by which the
97 /// radix index for this level begins. When `shift == 0` this is a leaf.
98 fn get_at(&self, index: usize, shift: usize) -> Option<&T> {
99 match self {
100 Node::Leaf(elems) => elems.get(index & MASK),
101 Node::Internal(children) => {
102 let child_idx = (index >> shift) & MASK;
103 let child = children.get(child_idx)?;
104 if shift == LOG_BRANCHING {
105 // Next level is a leaf.
106 child.get_at(index, 0)
107 } else {
108 child.get_at(index, shift - LOG_BRANCHING)
109 }
110 }
111 }
112 }
113
114 // ------------------------------------------------------------------
115 // Path-copy update
116 // ------------------------------------------------------------------
117
118 /// Return a new node that has `value` at `index`, sharing all unchanged
119 /// subtrees with `self`. Returns `None` if `index` is out of bounds.
120 fn update_at(&self, index: usize, value: T, shift: usize) -> Option<Arc<Node<T>>> {
121 match self {
122 Node::Leaf(elems) => {
123 let i = index & MASK;
124 if i >= elems.len() {
125 return None;
126 }
127 let mut new_elems = elems.clone();
128 new_elems[i] = value;
129 Some(Arc::new(Node::Leaf(new_elems)))
130 }
131 Node::Internal(children) => {
132 let child_idx = (index >> shift) & MASK;
133 if child_idx >= children.len() {
134 return None;
135 }
136 let next_shift = shift.saturating_sub(LOG_BRANCHING);
137 let new_child = children[child_idx].update_at(index, value, next_shift)?;
138 let mut new_children = children.clone();
139 new_children[child_idx] = new_child;
140 Some(Arc::new(Node::Internal(new_children)))
141 }
142 }
143 }
144
145 // ------------------------------------------------------------------
146 // Insertion helpers
147 // ------------------------------------------------------------------
148
149 /// Attempt to insert `leaf_node` as the rightmost leaf of the subtree
150 /// rooted here, given that this subtree has height `height` (leaf == 0).
151 ///
152 /// Returns `None` when the subtree is full (cannot accept more leaves at
153 /// this height), indicating the caller must grow the tree upward.
154 fn push_tail(&self, leaf_node: Arc<Node<T>>, height: usize) -> Option<Arc<Node<T>>> {
155 match self {
156 Node::Leaf(_) => {
157 // Leaf nodes cannot hold another leaf — the tree needs to grow.
158 None
159 }
160 Node::Internal(children) => {
161 if height == 1 {
162 // Direct parent of leaves.
163 if children.len() < BRANCHING {
164 let mut new_children = children.clone();
165 new_children.push(leaf_node);
166 Some(Arc::new(Node::Internal(new_children)))
167 } else {
168 None // this internal node is full
169 }
170 } else {
171 // Try to insert into the rightmost child first.
172 if let Some(last) = children.last() {
173 if let Some(new_last) = last.push_tail(Arc::clone(&leaf_node), height - 1) {
174 let mut new_children = children.clone();
175 let last_idx = new_children.len() - 1;
176 new_children[last_idx] = new_last;
177 return Some(Arc::new(Node::Internal(new_children)));
178 }
179 }
180 // Rightmost subtree was full; create a new child path.
181 if children.len() < BRANCHING {
182 let new_subtree = build_path_to_leaf(leaf_node, height - 1);
183 let mut new_children = children.clone();
184 new_children.push(new_subtree);
185 Some(Arc::new(Node::Internal(new_children)))
186 } else {
187 None // this internal node is also full
188 }
189 }
190 }
191 }
192 }
193
194 // ------------------------------------------------------------------
195 // Deletion helpers
196 // ------------------------------------------------------------------
197
198 /// Remove the rightmost leaf from the subtree, returning the updated
199 /// subtree root and the removed leaf.
200 ///
201 /// Returns `None` for the new root if the subtree becomes empty.
202 fn pop_tail(&self, height: usize) -> Option<(Option<Arc<Node<T>>>, Arc<Node<T>>)> {
203 match self {
204 Node::Leaf(_) => {
205 // A leaf is itself the rightmost "leaf subtree".
206 None
207 }
208 Node::Internal(children) => {
209 if children.is_empty() {
210 return None;
211 }
212 let last_idx = children.len() - 1;
213
214 if height == 1 {
215 // Children are leaves.
216 let removed_leaf = Arc::clone(&children[last_idx]);
217 let new_root = if children.len() == 1 {
218 None
219 } else {
220 let new_children = children[..last_idx].to_vec();
221 Some(Arc::new(Node::Internal(new_children)))
222 };
223 Some((new_root, removed_leaf))
224 } else {
225 let (new_last_opt, leaf) = children[last_idx].pop_tail(height - 1)?;
226 let new_children = match new_last_opt {
227 Some(new_last) => {
228 let mut ch = children.clone();
229 ch[last_idx] = new_last;
230 ch
231 }
232 None => children[..last_idx].to_vec(),
233 };
234 let new_root = if new_children.is_empty() {
235 None
236 } else {
237 Some(Arc::new(Node::Internal(new_children)))
238 };
239 Some((new_root, leaf))
240 }
241 }
242 }
243 }
244}
245
246// ─────────────────────────────────────────────────────────────────────────────
247// Private helpers
248// ─────────────────────────────────────────────────────────────────────────────
249
250/// Build a spine of internal nodes of `height` levels leading down to `leaf`.
251///
252/// Height 0 → return `leaf` unchanged.
253/// Height 1 → one internal node containing `leaf`.
254/// Height 2 → one internal node containing one internal node containing `leaf`.
255fn build_path_to_leaf<T: Clone>(leaf: Arc<Node<T>>, height: usize) -> Arc<Node<T>> {
256 if height == 0 {
257 return leaf;
258 }
259 let child = build_path_to_leaf(leaf, height - 1);
260 Arc::new(Node::Internal(vec![child]))
261}
262
263// ─────────────────────────────────────────────────────────────────────────────
264// PersistentVec<T>
265// ─────────────────────────────────────────────────────────────────────────────
266
267/// Persistent vector with structural sharing, backed by an RRB-tree.
268///
269/// All "mutating" operations return a new version while the original is left
270/// unchanged. Structural sharing (via `Arc`) ensures that only O(log N)
271/// nodes are newly allocated per operation.
272///
273/// ```rust
274/// use scirs2_core::data_structures::PersistentVec;
275///
276/// let v0: PersistentVec<u32> = PersistentVec::new();
277/// let v1 = v0.push_back(1);
278/// let v2 = v1.push_back(2);
279///
280/// assert_eq!(v2.len(), 2);
281/// assert_eq!(v2.get(0), Some(&1));
282/// // v1 is still valid and unchanged:
283/// assert_eq!(v1.len(), 1);
284/// ```
285#[derive(Clone)]
286pub struct PersistentVec<T: Clone> {
287 /// Root of the trie, or `None` when empty.
288 root: Option<Arc<Node<T>>>,
289 /// Total number of elements.
290 size: usize,
291 /// `shift = height * LOG_BRANCHING` for the current tree.
292 /// A brand-new (empty) vector starts with `shift = LOG_BRANCHING` (height 1).
293 shift: usize,
294 /// Tail buffer: up to BRANCHING elements not yet pushed into the trie.
295 tail: Vec<T>,
296}
297
298impl<T: Clone> PersistentVec<T> {
299 // ------------------------------------------------------------------
300 // Constructors
301 // ------------------------------------------------------------------
302
303 /// Creates an empty persistent vector.
304 pub fn new() -> Self {
305 PersistentVec {
306 root: None,
307 size: 0,
308 shift: LOG_BRANCHING,
309 tail: Vec::new(),
310 }
311 }
312
313 // ------------------------------------------------------------------
314 // Queries
315 // ------------------------------------------------------------------
316
317 /// Returns the number of elements.
318 pub fn len(&self) -> usize {
319 self.size
320 }
321
322 /// Returns `true` when the vector contains no elements.
323 pub fn is_empty(&self) -> bool {
324 self.size == 0
325 }
326
327 /// Returns a reference to the element at `index`, or `None` if out of
328 /// bounds.
329 ///
330 /// O(log N) time, no allocation.
331 pub fn get(&self, index: usize) -> Option<&T> {
332 if index >= self.size {
333 return None;
334 }
335
336 // Is the element in the tail?
337 let tail_offset = self.size - self.tail.len();
338 if index >= tail_offset {
339 return self.tail.get(index - tail_offset);
340 }
341
342 // Navigate the trie.
343 let root = self.root.as_ref()?;
344 root.get_at(index, self.shift)
345 }
346
347 // ------------------------------------------------------------------
348 // Persistent updates
349 // ------------------------------------------------------------------
350
351 /// Returns a new vector with `value` appended to the end.
352 ///
353 /// Amortised O(log N) — O(1) when the tail has room.
354 pub fn push_back(&self, value: T) -> Self {
355 if self.tail.len() < BRANCHING {
356 // Fast path: just extend the tail.
357 let mut new_tail = self.tail.clone();
358 new_tail.push(value);
359 return PersistentVec {
360 root: self.root.clone(),
361 size: self.size + 1,
362 shift: self.shift,
363 tail: new_tail,
364 };
365 }
366
367 // Tail is full; push it into the trie as a leaf node.
368 let full_tail = std::mem::take(&mut { self.tail.clone() });
369 let leaf = Arc::new(Node::Leaf(full_tail));
370
371 let (new_root, new_shift) = match &self.root {
372 None => {
373 // Empty trie — the leaf becomes the sole node.
374 (Some(Arc::new(Node::Internal(vec![leaf]))), self.shift)
375 }
376 Some(root) => {
377 let height = self.shift / LOG_BRANCHING;
378 match root.push_tail(leaf.clone(), height) {
379 Some(new_root) => (Some(new_root), self.shift),
380 None => {
381 // Tree is full at current height; grow it.
382 let new_height = height + 1;
383 let new_shift = new_height * LOG_BRANCHING;
384 let new_spine = build_path_to_leaf(leaf, height);
385 let new_root = Arc::new(Node::Internal(vec![Arc::clone(root), new_spine]));
386 (Some(new_root), new_shift)
387 }
388 }
389 }
390 };
391
392 PersistentVec {
393 root: new_root,
394 size: self.size + 1,
395 shift: new_shift,
396 tail: vec![value],
397 }
398 }
399
400 /// Returns a new vector with the element at `index` replaced by `value`.
401 ///
402 /// Returns `None` if `index >= self.len()`.
403 /// O(log N) node allocations.
404 pub fn update(&self, index: usize, value: T) -> Option<Self> {
405 if index >= self.size {
406 return None;
407 }
408
409 let tail_offset = self.size - self.tail.len();
410
411 if index >= tail_offset {
412 // Element is in the tail.
413 let mut new_tail = self.tail.clone();
414 new_tail[index - tail_offset] = value;
415 return Some(PersistentVec {
416 root: self.root.clone(),
417 size: self.size,
418 shift: self.shift,
419 tail: new_tail,
420 });
421 }
422
423 // Element is in the trie — path-copy from root to the leaf.
424 let root = self.root.as_ref()?;
425 let new_root = root.update_at(index, value, self.shift)?;
426 Some(PersistentVec {
427 root: Some(new_root),
428 size: self.size,
429 shift: self.shift,
430 tail: self.tail.clone(),
431 })
432 }
433
434 /// Removes and returns the last element, returning `(new_vec, value)`.
435 ///
436 /// Returns `None` if the vector is empty.
437 /// O(log N) in the worst case, O(1) common case.
438 pub fn pop_back(&self) -> Option<(Self, T)> {
439 if self.size == 0 {
440 return None;
441 }
442
443 if !self.tail.is_empty() {
444 // Fast path: pop from tail.
445 let mut new_tail = self.tail.clone();
446 let popped = new_tail.pop()?;
447
448 if new_tail.is_empty() && self.root.is_some() {
449 // Tail is now empty; pull the rightmost leaf from the trie
450 // to become the new tail.
451 let root = self.root.as_ref()?;
452 let height = self.shift / LOG_BRANCHING;
453 let (new_root_opt, leaf_arc) = root.pop_tail(height)?;
454
455 let new_tail_vec = leaf_arc.elements().to_vec();
456
457 // Shrink tree height if the new root has only one child.
458 let (final_root, final_shift) = match new_root_opt {
459 None => (None, LOG_BRANCHING),
460 Some(nr) => {
461 let (collapsed, new_shift) = collapse_root(nr, self.shift);
462 (Some(collapsed), new_shift)
463 }
464 };
465
466 return Some((
467 PersistentVec {
468 root: final_root,
469 size: self.size - 1,
470 shift: final_shift,
471 tail: new_tail_vec,
472 },
473 popped,
474 ));
475 }
476
477 return Some((
478 PersistentVec {
479 root: self.root.clone(),
480 size: self.size - 1,
481 shift: self.shift,
482 tail: new_tail,
483 },
484 popped,
485 ));
486 }
487
488 // Tail is empty; pull from trie (should not normally happen but handle
489 // the degenerate case).
490 let root = self.root.as_ref()?;
491 let height = self.shift / LOG_BRANCHING;
492 let (new_root_opt, leaf_arc) = root.pop_tail(height)?;
493
494 let leaf_elems = leaf_arc.elements();
495 let popped = leaf_elems.last()?.clone();
496 let new_tail_vec: Vec<T> = leaf_elems[..leaf_elems.len() - 1].to_vec();
497
498 let (final_root, final_shift) = match new_root_opt {
499 None => (None, LOG_BRANCHING),
500 Some(nr) => {
501 let (collapsed, ns) = collapse_root(nr, self.shift);
502 (Some(collapsed), ns)
503 }
504 };
505
506 Some((
507 PersistentVec {
508 root: final_root,
509 size: self.size - 1,
510 shift: final_shift,
511 tail: new_tail_vec,
512 },
513 popped,
514 ))
515 }
516
517 // ------------------------------------------------------------------
518 // Iteration
519 // ------------------------------------------------------------------
520
521 /// Returns an iterator over references to elements in order.
522 pub fn iter(&self) -> PersistentVecIter<T> {
523 // Collect all elements from the trie via depth-first traversal.
524 let mut trie_elements: Vec<T> =
525 Vec::with_capacity(self.size.saturating_sub(self.tail.len()));
526 if let Some(root) = &self.root {
527 collect_elements(root, &mut trie_elements);
528 }
529 // Append tail.
530 trie_elements.extend_from_slice(&self.tail);
531
532 PersistentVecIter {
533 data: trie_elements,
534 pos: 0,
535 }
536 }
537}
538
539/// Collapse the root if it has exactly one internal child (shrink height).
540fn collapse_root<T: Clone>(root: Arc<Node<T>>, shift: usize) -> (Arc<Node<T>>, usize) {
541 if shift <= LOG_BRANCHING {
542 return (root, shift);
543 }
544 match root.as_ref() {
545 Node::Internal(children) if children.len() == 1 => {
546 collapse_root(Arc::clone(&children[0]), shift - LOG_BRANCHING)
547 }
548 _ => (root, shift),
549 }
550}
551
552/// Depth-first traversal to collect all elements from a subtree.
553fn collect_elements<T: Clone>(node: &Arc<Node<T>>, out: &mut Vec<T>) {
554 match node.as_ref() {
555 Node::Leaf(elems) => out.extend_from_slice(elems),
556 Node::Internal(children) => {
557 for child in children {
558 collect_elements(child, out);
559 }
560 }
561 }
562}
563
564// ─────────────────────────────────────────────────────────────────────────────
565// Default / FromIterator
566// ─────────────────────────────────────────────────────────────────────────────
567
568impl<T: Clone> Default for PersistentVec<T> {
569 fn default() -> Self {
570 Self::new()
571 }
572}
573
574impl<T: Clone> FromIterator<T> for PersistentVec<T> {
575 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
576 let mut vec = PersistentVec::new();
577 for item in iter {
578 vec = vec.push_back(item);
579 }
580 vec
581 }
582}
583
584// ─────────────────────────────────────────────────────────────────────────────
585// Iterator
586// ─────────────────────────────────────────────────────────────────────────────
587
588/// Iterator over references to elements of a `PersistentVec`.
589///
590/// Produced by [`PersistentVec::iter`].
591pub struct PersistentVecIter<T: Clone> {
592 /// All elements in order (built eagerly during construction for simplicity).
593 data: Vec<T>,
594 pos: usize,
595}
596
597impl<T: Clone> Iterator for PersistentVecIter<T> {
598 type Item = T;
599
600 fn next(&mut self) -> Option<Self::Item> {
601 if self.pos < self.data.len() {
602 let item = self.data[self.pos].clone();
603 self.pos += 1;
604 Some(item)
605 } else {
606 None
607 }
608 }
609
610 fn size_hint(&self) -> (usize, Option<usize>) {
611 let remaining = self.data.len() - self.pos;
612 (remaining, Some(remaining))
613 }
614}
615
616impl<T: Clone> ExactSizeIterator for PersistentVecIter<T> {}
617
618// ─────────────────────────────────────────────────────────────────────────────
619// Tests
620// ─────────────────────────────────────────────────────────────────────────────
621
622#[cfg(test)]
623mod tests {
624 use super::*;
625
626 #[test]
627 fn test_rrb_basic_ops() {
628 let mut v = PersistentVec::new();
629 for i in 0u32..100 {
630 v = v.push_back(i);
631 }
632 assert_eq!(v.len(), 100);
633 for i in 0u32..100 {
634 assert_eq!(v.get(i as usize), Some(&i), "mismatch at index {i}");
635 }
636 assert_eq!(v.get(100), None);
637 }
638
639 #[test]
640 fn test_rrb_persistence() {
641 let v0: PersistentVec<u32> = PersistentVec::new();
642 let v1 = v0.push_back(1);
643 let v2 = v1.push_back(2);
644 let v3 = v2.push_back(3);
645
646 // Newer versions see all elements.
647 assert_eq!(v3.len(), 3);
648 assert_eq!(v3.get(0), Some(&1));
649 assert_eq!(v3.get(1), Some(&2));
650 assert_eq!(v3.get(2), Some(&3));
651
652 // Older versions are unmodified.
653 assert_eq!(v0.len(), 0);
654 assert_eq!(v1.len(), 1);
655 assert_eq!(v1.get(0), Some(&1));
656 assert_eq!(v2.len(), 2);
657 assert_eq!(v2.get(1), Some(&2));
658 }
659
660 #[test]
661 fn test_rrb_update() {
662 let base: PersistentVec<i32> = (0..50).collect();
663
664 let updated = base.update(25, 999).expect("index 25 is valid");
665
666 // Updated version has the new value.
667 assert_eq!(updated.get(25), Some(&999));
668 // All other indices are unchanged.
669 for i in 0..50 {
670 if i != 25 {
671 assert_eq!(updated.get(i), Some(&(i as i32)), "mismatch at {i}");
672 }
673 }
674 // Original is unchanged.
675 assert_eq!(base.get(25), Some(&25));
676
677 // Out-of-bounds update returns None.
678 assert!(base.update(100, 42).is_none());
679 }
680
681 #[test]
682 fn test_rrb_iter() {
683 let v: PersistentVec<u32> = (0..64).collect();
684 let collected: Vec<u32> = v.iter().collect();
685 let expected: Vec<u32> = (0..64).collect();
686 assert_eq!(collected, expected);
687 }
688
689 #[test]
690 fn test_rrb_large() {
691 const N: usize = 1000;
692 let v: PersistentVec<usize> = (0..N).collect();
693 assert_eq!(v.len(), N);
694 for i in 0..N {
695 assert_eq!(v.get(i), Some(&i), "mismatch at index {i}");
696 }
697 }
698
699 #[test]
700 fn test_rrb_pop_back() {
701 let v: PersistentVec<u32> = (0..10).collect();
702
703 let (v2, last) = v.pop_back().expect("non-empty");
704 assert_eq!(last, 9);
705 assert_eq!(v2.len(), 9);
706
707 // Original unchanged.
708 assert_eq!(v.len(), 10);
709 assert_eq!(v.get(9), Some(&9));
710
711 // Pop until empty.
712 let mut cur = v.clone();
713 for expected_last in (0..10).rev() {
714 let (next, val) = cur.pop_back().expect("should not be empty");
715 assert_eq!(val, expected_last);
716 cur = next;
717 }
718 assert!(cur.is_empty());
719 assert!(cur.pop_back().is_none());
720 }
721
722 #[test]
723 fn test_rrb_from_iterator() {
724 let v: PersistentVec<u8> = vec![10u8, 20, 30, 40, 50].into_iter().collect();
725 assert_eq!(v.len(), 5);
726 assert_eq!(v.get(2), Some(&30u8));
727 }
728
729 #[test]
730 fn test_rrb_default_is_empty() {
731 let v: PersistentVec<f64> = PersistentVec::default();
732 assert!(v.is_empty());
733 assert_eq!(v.len(), 0);
734 }
735
736 #[test]
737 fn test_rrb_cross_leaf_boundary() {
738 // Push exactly BRANCHING+1 elements to force a leaf spill.
739 let n = BRANCHING + 1;
740 let v: PersistentVec<usize> = (0..n).collect();
741 assert_eq!(v.len(), n);
742 for i in 0..n {
743 assert_eq!(v.get(i), Some(&i), "mismatch at {i}");
744 }
745 }
746
747 #[test]
748 fn test_rrb_update_across_leaf_boundary() {
749 // Cover the code path where an update targets an element in the trie
750 // (not the tail) near a leaf boundary.
751 let v: PersistentVec<u32> = (0..100).collect();
752 // BRANCHING = 32; index 31 is the last element of the first trie leaf.
753 let updated = v.update(31, 777).expect("valid index");
754 assert_eq!(updated.get(31), Some(&777));
755 assert_eq!(v.get(31), Some(&31)); // original unchanged
756 }
757}