Skip to main content

ftui_widgets/
adaptive_radix.rs

1//! Adaptive Radix Tree (ART) for prefix search (bd-1cgh9).
2//!
3//! A radix tree with adaptive node sizes (Node4/Node16/Node48/Node256) that
4//! provides O(k) lookup and O(k + m) prefix scan, where k = key length and
5//! m = number of matches.
6//!
7//! # Node Types
8//!
9//! | Type    | Keys | Children | Size     | Use Case            |
10//! |---------|------|----------|----------|---------------------|
11//! | Node4   | 4    | 4        | ~48 B    | Very sparse         |
12//! | Node16  | 16   | 16       | ~192 B   | Moderate density    |
13//! | Node48  | 48   | 48+256   | ~656 B   | Dense               |
14//! | Node256 | 256  | 256      | ~2056 B  | Very dense (full)   |
15//!
16//! # Example
17//!
18//! ```
19//! use ftui_widgets::adaptive_radix::AdaptiveRadixTree;
20//!
21//! let mut art = AdaptiveRadixTree::new();
22//! art.insert("file:open", 1);
23//! art.insert("file:save", 2);
24//! art.insert("file:close", 3);
25//! art.insert("edit:undo", 4);
26//!
27//! // Prefix scan
28//! let matches = art.prefix_scan("file:");
29//! assert_eq!(matches.len(), 3);
30//!
31//! // Exact lookup
32//! assert_eq!(art.get("edit:undo"), Some(&4));
33//! ```
34
35/// Maximum number of children in a Node4.
36const NODE4_MAX: usize = 4;
37/// Maximum number of children in a Node16.
38const NODE16_MAX: usize = 16;
39/// Maximum number of children in a Node48.
40const NODE48_MAX: usize = 48;
41
42/// An adaptive radix tree for string-keyed data.
43#[derive(Debug, Clone)]
44pub struct AdaptiveRadixTree<V> {
45    root: Option<Box<ArtNode<V>>>,
46    len: usize,
47}
48
49/// Internal node types with adaptive sizing.
50#[derive(Debug, Clone)]
51#[allow(clippy::large_enum_variant)]
52enum ArtNode<V> {
53    /// Leaf node storing a key-value pair.
54    Leaf { key: String, value: V },
55    /// Inner node with adaptive children.
56    Inner {
57        /// Compressed path prefix (path compression optimization).
58        prefix: Vec<u8>,
59        /// Children stored in one of the adaptive formats.
60        children: Children<V>,
61        /// Value stored at this node (if key terminates here).
62        value: Option<V>,
63        /// Full key for terminal values stored directly on compressed inner nodes.
64        terminal_key: Option<String>,
65    },
66}
67
68/// Adaptive child storage.
69#[derive(Debug, Clone)]
70#[allow(clippy::large_enum_variant)]
71enum Children<V> {
72    /// Up to 4 children: sorted key bytes + child pointers.
73    Node4 {
74        keys: Vec<u8>,
75        children: Vec<Box<ArtNode<V>>>,
76    },
77    /// Up to 16 children: sorted key bytes + child pointers.
78    Node16 {
79        keys: Vec<u8>,
80        children: Vec<Box<ArtNode<V>>>,
81    },
82    /// Up to 48 children: 256-entry index + child array.
83    Node48 {
84        index: [u8; 256],
85        children: Vec<Option<Box<ArtNode<V>>>>,
86        count: usize,
87    },
88    /// Up to 256 children: direct indexing.
89    Node256 {
90        children: Vec<Option<Box<ArtNode<V>>>>,
91    },
92}
93
94impl<V: Clone> AdaptiveRadixTree<V> {
95    /// Create a new empty tree.
96    pub fn new() -> Self {
97        Self { root: None, len: 0 }
98    }
99
100    /// Number of entries in the tree.
101    pub fn len(&self) -> usize {
102        self.len
103    }
104
105    /// Whether the tree is empty.
106    pub fn is_empty(&self) -> bool {
107        self.len == 0
108    }
109
110    /// Insert a key-value pair. Returns the previous value if the key existed.
111    pub fn insert(&mut self, key: &str, value: V) -> Option<V> {
112        if self.root.is_none() {
113            self.root = Some(Box::new(ArtNode::Leaf {
114                key: key.to_string(),
115                value,
116            }));
117            self.len += 1;
118            return None;
119        }
120
121        let result = insert_recursive(self.root.as_mut().unwrap(), key.as_bytes(), key, value, 0);
122        if result.is_none() {
123            self.len += 1;
124        }
125        result
126    }
127
128    /// Look up a value by exact key.
129    pub fn get(&self, key: &str) -> Option<&V> {
130        let node = self.root.as_ref()?;
131        get_recursive(node, key.as_bytes(), 0)
132    }
133
134    /// Return all key-value pairs whose keys start with the given prefix,
135    /// sorted by key.
136    pub fn prefix_scan(&self, prefix: &str) -> Vec<(&str, &V)> {
137        let mut results = Vec::new();
138        if let Some(ref root) = self.root {
139            prefix_scan_recursive(root, prefix.as_bytes(), 0, &mut results);
140        }
141        results.sort_by_key(|(k, _)| *k);
142        results
143    }
144
145    /// Delete a key. Returns the value if it existed.
146    pub fn delete(&mut self, key: &str) -> Option<V> {
147        let result = {
148            let root = self.root.as_mut()?;
149            delete_recursive(root, key.as_bytes(), 0)
150        };
151
152        if result.removed.is_some() {
153            self.len -= 1;
154            if result.prune || self.root.as_ref().is_some_and(|root| is_empty_node(root)) {
155                self.root = None;
156            }
157        }
158        result.removed
159    }
160
161    /// Iterate all entries in sorted key order.
162    pub fn iter(&self) -> Vec<(&str, &V)> {
163        let mut results = Vec::new();
164        if let Some(ref root) = self.root {
165            collect_all(root, &mut results);
166        }
167        results.sort_by_key(|(k, _)| *k);
168        results
169    }
170
171    /// Get node type distribution for diagnostics.
172    pub fn node_distribution(&self) -> NodeDistribution {
173        let mut dist = NodeDistribution::default();
174        if let Some(ref root) = self.root {
175            count_nodes(root, &mut dist);
176        }
177        dist
178    }
179}
180
181impl<V: Clone> Default for AdaptiveRadixTree<V> {
182    fn default() -> Self {
183        Self::new()
184    }
185}
186
187/// Distribution of node types in the tree.
188#[derive(Debug, Clone, Default)]
189pub struct NodeDistribution {
190    pub leaves: usize,
191    pub node4: usize,
192    pub node16: usize,
193    pub node48: usize,
194    pub node256: usize,
195}
196
197#[derive(Debug)]
198struct DeleteResult<V> {
199    removed: Option<V>,
200    prune: bool,
201}
202
203impl<V> DeleteResult<V> {
204    const fn not_found() -> Self {
205        Self {
206            removed: None,
207            prune: false,
208        }
209    }
210}
211
212// ============================================================================
213// Internal recursive operations
214// ============================================================================
215
216fn insert_recursive<V: Clone>(
217    node: &mut ArtNode<V>,
218    key_bytes: &[u8],
219    full_key: &str,
220    value: V,
221    depth: usize,
222) -> Option<V> {
223    match node {
224        ArtNode::Leaf {
225            key: existing_key,
226            value: existing_value,
227        } => {
228            if existing_key == full_key {
229                // Key exists — update value.
230                let old = existing_value.clone();
231                *existing_value = value;
232                return Some(old);
233            }
234
235            // Split leaf into inner node.
236            let existing_bytes = existing_key.as_bytes();
237            let common_prefix_len =
238                common_prefix_length(&existing_bytes[depth..], &key_bytes[depth..]);
239
240            let prefix = existing_bytes[depth..depth + common_prefix_len].to_vec();
241
242            let old_key = existing_key.clone();
243            let old_val = existing_value.clone();
244
245            let split_depth = depth + common_prefix_len;
246
247            if split_depth >= existing_bytes.len() && split_depth >= key_bytes.len() {
248                // Keys are identical up to this point — shouldn't happen (checked above).
249                *existing_value = value;
250                return Some(old_val);
251            }
252
253            let mut children = Children::Node4 {
254                keys: Vec::new(),
255                children: Vec::new(),
256            };
257
258            if split_depth < existing_bytes.len() {
259                let old_child = Box::new(ArtNode::Leaf {
260                    key: old_key.clone(),
261                    value: old_val.clone(),
262                });
263                children_insert(&mut children, existing_bytes[split_depth], old_child);
264            }
265
266            let mut inner_value = None;
267            let mut terminal_key = None;
268            if split_depth < key_bytes.len() {
269                let new_child = Box::new(ArtNode::Leaf {
270                    key: full_key.to_string(),
271                    value,
272                });
273                children_insert(&mut children, key_bytes[split_depth], new_child);
274            } else {
275                inner_value = Some(value);
276                terminal_key = Some(full_key.to_string());
277            }
278
279            if split_depth >= existing_bytes.len() {
280                inner_value = Some(old_val);
281                terminal_key = Some(old_key);
282            }
283
284            *node = ArtNode::Inner {
285                prefix,
286                children,
287                value: inner_value,
288                terminal_key,
289            };
290            None
291        }
292        ArtNode::Inner {
293            prefix,
294            children,
295            value: node_value,
296            terminal_key,
297        } => {
298            let remaining = &key_bytes[depth..];
299            let prefix_match = common_prefix_length(remaining, prefix);
300
301            if prefix_match < prefix.len() {
302                // Prefix mismatch — split this inner node.
303                let common = prefix[..prefix_match].to_vec();
304                let old_suffix = prefix[prefix_match..].to_vec();
305                let old_first_byte = old_suffix[0];
306
307                // Create new inner node with shared prefix.
308                let old_inner = ArtNode::Inner {
309                    prefix: old_suffix[1..].to_vec(),
310                    children: std::mem::replace(
311                        children,
312                        Children::Node4 {
313                            keys: Vec::new(),
314                            children: Vec::new(),
315                        },
316                    ),
317                    value: node_value.take(),
318                    terminal_key: terminal_key.take(),
319                };
320
321                let mut new_children = Children::Node4 {
322                    keys: Vec::new(),
323                    children: Vec::new(),
324                };
325                children_insert(&mut new_children, old_first_byte, Box::new(old_inner));
326
327                let new_depth = depth + prefix_match;
328                let mut inner_value = None;
329                let mut new_terminal_key = None;
330                if new_depth < key_bytes.len() {
331                    let new_child = Box::new(ArtNode::Leaf {
332                        key: full_key.to_string(),
333                        value,
334                    });
335                    children_insert(&mut new_children, key_bytes[new_depth], new_child);
336                } else {
337                    inner_value = Some(value);
338                    new_terminal_key = Some(full_key.to_string());
339                }
340
341                *prefix = common;
342                *children = new_children;
343                *node_value = inner_value;
344                *terminal_key = new_terminal_key;
345                return None;
346            }
347
348            let next_depth = depth + prefix.len();
349            if next_depth >= key_bytes.len() {
350                // Key terminates at this node.
351                let old = node_value.take();
352                *node_value = Some(value);
353                *terminal_key = Some(full_key.to_string());
354                return old;
355            }
356
357            let byte = key_bytes[next_depth];
358            if let Some(child) = children_get_mut(children, byte) {
359                insert_recursive(child, key_bytes, full_key, value, next_depth + 1)
360            } else {
361                let new_child = Box::new(ArtNode::Leaf {
362                    key: full_key.to_string(),
363                    value,
364                });
365                children_insert(children, byte, new_child);
366                None
367            }
368        }
369    }
370}
371
372fn get_recursive<'a, V>(node: &'a ArtNode<V>, key_bytes: &[u8], depth: usize) -> Option<&'a V> {
373    match node {
374        ArtNode::Leaf { key, value } => {
375            if key.as_bytes() == key_bytes {
376                Some(value)
377            } else {
378                None
379            }
380        }
381        ArtNode::Inner {
382            prefix,
383            children,
384            value,
385            terminal_key: _,
386        } => {
387            let remaining = &key_bytes[depth..];
388            if remaining.len() < prefix.len() || &remaining[..prefix.len()] != prefix.as_slice() {
389                return None;
390            }
391            let next_depth = depth + prefix.len();
392            if next_depth >= key_bytes.len() {
393                return value.as_ref();
394            }
395            let byte = key_bytes[next_depth];
396            children_get(children, byte)
397                .and_then(|child| get_recursive(child, key_bytes, next_depth + 1))
398        }
399    }
400}
401
402fn prefix_scan_recursive<'a, V>(
403    node: &'a ArtNode<V>,
404    prefix_bytes: &[u8],
405    depth: usize,
406    results: &mut Vec<(&'a str, &'a V)>,
407) {
408    match node {
409        ArtNode::Leaf { key, value } => {
410            if key.as_bytes().starts_with(prefix_bytes) {
411                results.push((key.as_str(), value));
412            }
413        }
414        ArtNode::Inner {
415            prefix,
416            children,
417            value: _,
418            terminal_key: _,
419        } => {
420            let remaining_prefix = if depth < prefix_bytes.len() {
421                &prefix_bytes[depth..]
422            } else {
423                &[]
424            };
425
426            // Check if the node's prefix is compatible with the search prefix.
427            let match_len = common_prefix_length(remaining_prefix, prefix);
428
429            if match_len < remaining_prefix.len() && match_len < prefix.len() {
430                // No overlap.
431                return;
432            }
433
434            let next_depth = depth + prefix.len();
435
436            if remaining_prefix.len() <= prefix.len() {
437                // The search prefix is fully consumed — collect all descendants.
438                if depth + match_len >= prefix_bytes.len() {
439                    // Prefix fully matched — collect all.
440                    collect_all_inner(node, results);
441                    return;
442                }
443            }
444
445            if next_depth > prefix_bytes.len() {
446                // Already past the prefix — collect all.
447                collect_all_inner(node, results);
448                return;
449            }
450
451            if next_depth == prefix_bytes.len() {
452                // Prefix exactly consumed at this level — collect all.
453                collect_all_inner(node, results);
454                return;
455            }
456
457            // Continue searching in the appropriate child.
458            let byte = prefix_bytes[next_depth];
459            if let Some(child) = children_get(children, byte) {
460                prefix_scan_recursive(child, prefix_bytes, next_depth + 1, results);
461            }
462        }
463    }
464}
465
466fn collect_all_inner<'a, V>(node: &'a ArtNode<V>, results: &mut Vec<(&'a str, &'a V)>) {
467    match node {
468        ArtNode::Leaf { key, value } => {
469            results.push((key.as_str(), value));
470        }
471        ArtNode::Inner {
472            children,
473            value,
474            terminal_key,
475            ..
476        } => {
477            if let (Some(key), Some(value)) = (terminal_key.as_deref(), value.as_ref()) {
478                results.push((key, value));
479            }
480            for child in children_iter(children) {
481                collect_all_inner(child, results);
482            }
483        }
484    }
485}
486
487fn collect_all<'a, V>(node: &'a ArtNode<V>, results: &mut Vec<(&'a str, &'a V)>) {
488    collect_all_inner(node, results);
489}
490
491fn delete_recursive<V: Clone>(
492    node: &mut ArtNode<V>,
493    key_bytes: &[u8],
494    depth: usize,
495) -> DeleteResult<V> {
496    match node {
497        ArtNode::Leaf { key, value } => {
498            if key.as_bytes() == key_bytes {
499                DeleteResult {
500                    removed: Some(value.clone()),
501                    prune: true,
502                }
503            } else {
504                DeleteResult::not_found()
505            }
506        }
507        ArtNode::Inner {
508            prefix,
509            children,
510            value: node_value,
511            terminal_key,
512        } => {
513            let remaining = &key_bytes[depth..];
514            if remaining.len() < prefix.len() || &remaining[..prefix.len()] != prefix.as_slice() {
515                return DeleteResult::not_found();
516            }
517            let next_depth = depth + prefix.len();
518            if next_depth >= key_bytes.len() {
519                let removed = node_value.take();
520                if removed.is_some() {
521                    *terminal_key = None;
522                }
523                let prune = removed.is_some() && children_count(children) == 0;
524                return DeleteResult { removed, prune };
525            }
526            let byte = key_bytes[next_depth];
527            let child_result = if let Some(child) = children_get_mut(children, byte) {
528                delete_recursive(child, key_bytes, next_depth + 1)
529            } else {
530                DeleteResult::not_found()
531            };
532
533            if child_result.prune {
534                children_remove(children, byte);
535            }
536            let removed = child_result.removed;
537            DeleteResult {
538                prune: removed.is_some() && node_value.is_none() && children_count(children) == 0,
539                removed,
540            }
541        }
542    }
543}
544
545fn is_empty_node<V>(node: &ArtNode<V>) -> bool {
546    match node {
547        ArtNode::Leaf { .. } => false, // Leaves are never "empty"
548        ArtNode::Inner {
549            children, value, ..
550        } => value.is_none() && children_count(children) == 0,
551    }
552}
553
554fn count_nodes<V>(node: &ArtNode<V>, dist: &mut NodeDistribution) {
555    match node {
556        ArtNode::Leaf { .. } => dist.leaves += 1,
557        ArtNode::Inner { children, .. } => {
558            match children {
559                Children::Node4 { .. } => dist.node4 += 1,
560                Children::Node16 { .. } => dist.node16 += 1,
561                Children::Node48 { .. } => dist.node48 += 1,
562                Children::Node256 { .. } => dist.node256 += 1,
563            }
564            for child in children_iter(children) {
565                count_nodes(child, dist);
566            }
567        }
568    }
569}
570
571// ============================================================================
572// Children operations with automatic promotion/demotion
573// ============================================================================
574
575fn children_insert<V>(children: &mut Children<V>, byte: u8, child: Box<ArtNode<V>>) {
576    match children {
577        Children::Node4 { keys, children: ch } => {
578            if ch.len() < NODE4_MAX {
579                let pos = keys.iter().position(|&k| k > byte).unwrap_or(keys.len());
580                keys.insert(pos, byte);
581                ch.insert(pos, child);
582            } else {
583                // Promote to Node16.
584                let mut new_keys = keys.clone();
585                let mut new_ch: Vec<Box<ArtNode<V>>> = std::mem::take(ch);
586                let pos = new_keys
587                    .iter()
588                    .position(|&k| k > byte)
589                    .unwrap_or(new_keys.len());
590                new_keys.insert(pos, byte);
591                new_ch.insert(pos, child);
592                *children = Children::Node16 {
593                    keys: new_keys,
594                    children: new_ch,
595                };
596            }
597        }
598        Children::Node16 { keys, children: ch } => {
599            if ch.len() < NODE16_MAX {
600                let pos = keys.iter().position(|&k| k > byte).unwrap_or(keys.len());
601                keys.insert(pos, byte);
602                ch.insert(pos, child);
603            } else {
604                // Promote to Node48.
605                let mut index = [u8::MAX; 256];
606                let mut new_ch: Vec<Option<Box<ArtNode<V>>>> = Vec::with_capacity(NODE48_MAX);
607                for (i, (&k, c)) in keys.iter().zip(ch.drain(..)).enumerate() {
608                    index[k as usize] = i as u8;
609                    new_ch.push(Some(c));
610                }
611                let idx = new_ch.len();
612                index[byte as usize] = idx as u8;
613                new_ch.push(Some(child));
614                *children = Children::Node48 {
615                    index,
616                    children: new_ch,
617                    count: idx + 1,
618                };
619            }
620        }
621        Children::Node48 {
622            index,
623            children: ch,
624            count,
625        } => {
626            if *count < NODE48_MAX {
627                let idx = *count;
628                index[byte as usize] = idx as u8;
629                if idx < ch.len() {
630                    ch[idx] = Some(child);
631                } else {
632                    ch.push(Some(child));
633                }
634                *count += 1;
635            } else {
636                // Promote to Node256.
637                let mut new_ch: Vec<Option<Box<ArtNode<V>>>> = (0..256).map(|_| None).collect();
638                for (b, &idx) in index.iter().enumerate() {
639                    if idx != u8::MAX && (idx as usize) < ch.len() {
640                        new_ch[b] = ch[idx as usize].take();
641                    }
642                }
643                new_ch[byte as usize] = Some(child);
644                *children = Children::Node256 { children: new_ch };
645            }
646        }
647        Children::Node256 { children: ch } => {
648            ch[byte as usize] = Some(child);
649        }
650    }
651}
652
653fn children_get<V>(children: &Children<V>, byte: u8) -> Option<&ArtNode<V>> {
654    match children {
655        Children::Node4 { keys, children: ch } => {
656            keys.iter().position(|&k| k == byte).map(|i| ch[i].as_ref())
657        }
658        Children::Node16 { keys, children: ch } => {
659            keys.iter().position(|&k| k == byte).map(|i| ch[i].as_ref())
660        }
661        Children::Node48 {
662            index,
663            children: ch,
664            ..
665        } => {
666            let idx = index[byte as usize];
667            if idx != u8::MAX && (idx as usize) < ch.len() {
668                ch[idx as usize].as_ref().map(|c| c.as_ref())
669            } else {
670                None
671            }
672        }
673        Children::Node256 { children: ch } => ch[byte as usize].as_ref().map(|c| c.as_ref()),
674    }
675}
676
677fn children_get_mut<V>(children: &mut Children<V>, byte: u8) -> Option<&mut ArtNode<V>> {
678    match children {
679        Children::Node4 { keys, children: ch } => keys
680            .iter()
681            .position(|&k| k == byte)
682            .map(move |i| ch[i].as_mut()),
683        Children::Node16 { keys, children: ch } => keys
684            .iter()
685            .position(|&k| k == byte)
686            .map(move |i| ch[i].as_mut()),
687        Children::Node48 {
688            index,
689            children: ch,
690            ..
691        } => {
692            let idx = index[byte as usize];
693            if idx != u8::MAX && (idx as usize) < ch.len() {
694                ch[idx as usize].as_mut().map(|c| c.as_mut())
695            } else {
696                None
697            }
698        }
699        Children::Node256 { children: ch } => ch[byte as usize].as_mut().map(|c| c.as_mut()),
700    }
701}
702
703fn children_remove<V>(children: &mut Children<V>, byte: u8) {
704    match children {
705        Children::Node4 { keys, children: ch } => {
706            if let Some(pos) = keys.iter().position(|&k| k == byte) {
707                keys.remove(pos);
708                ch.remove(pos);
709            }
710        }
711        Children::Node16 { keys, children: ch } => {
712            if let Some(pos) = keys.iter().position(|&k| k == byte) {
713                keys.remove(pos);
714                ch.remove(pos);
715            }
716            // Demote to Node4 if small enough.
717            if ch.len() <= NODE4_MAX {
718                *children = Children::Node4 {
719                    keys: keys.clone(),
720                    children: std::mem::take(ch),
721                };
722            }
723        }
724        Children::Node48 {
725            index,
726            children: ch,
727            count,
728        } => {
729            let idx = index[byte as usize];
730            if idx != u8::MAX && (idx as usize) < ch.len() {
731                ch[idx as usize] = None;
732                index[byte as usize] = u8::MAX;
733                *count = count.saturating_sub(1);
734            }
735        }
736        Children::Node256 { children: ch } => {
737            ch[byte as usize] = None;
738        }
739    }
740}
741
742fn children_count<V>(children: &Children<V>) -> usize {
743    match children {
744        Children::Node4 { children: ch, .. } => ch.len(),
745        Children::Node16 { children: ch, .. } => ch.len(),
746        Children::Node48 { count, .. } => *count,
747        Children::Node256 { children: ch } => ch.iter().filter(|c| c.is_some()).count(),
748    }
749}
750
751fn children_iter<'a, V>(
752    children: &'a Children<V>,
753) -> Box<dyn Iterator<Item = &'a ArtNode<V>> + 'a> {
754    match children {
755        Children::Node4 { children: ch, .. } => Box::new(ch.iter().map(|c| c.as_ref())),
756        Children::Node16 { children: ch, .. } => Box::new(ch.iter().map(|c| c.as_ref())),
757        Children::Node48 { children: ch, .. } => {
758            Box::new(ch.iter().filter_map(|c| c.as_ref().map(|c| c.as_ref())))
759        }
760        Children::Node256 { children: ch } => {
761            Box::new(ch.iter().filter_map(|c| c.as_ref().map(|c| c.as_ref())))
762        }
763    }
764}
765
766fn common_prefix_length(a: &[u8], b: &[u8]) -> usize {
767    a.iter().zip(b.iter()).take_while(|(x, y)| x == y).count()
768}
769
770#[cfg(test)]
771mod tests {
772    use super::*;
773
774    #[test]
775    fn empty_tree() {
776        let art: AdaptiveRadixTree<u32> = AdaptiveRadixTree::new();
777        assert!(art.is_empty());
778        assert_eq!(art.len(), 0);
779        assert_eq!(art.get("anything"), None);
780    }
781
782    #[test]
783    fn single_insert_and_get() {
784        let mut art = AdaptiveRadixTree::new();
785        art.insert("hello", 42);
786        assert_eq!(art.get("hello"), Some(&42));
787        assert_eq!(art.get("hell"), None);
788        assert_eq!(art.get("helloo"), None);
789        assert_eq!(art.len(), 1);
790    }
791
792    #[test]
793    fn multiple_inserts() {
794        let mut art = AdaptiveRadixTree::new();
795        art.insert("file:open", 1);
796        art.insert("file:save", 2);
797        art.insert("file:close", 3);
798        art.insert("edit:undo", 4);
799        art.insert("edit:redo", 5);
800
801        assert_eq!(art.len(), 5);
802        assert_eq!(art.get("file:open"), Some(&1));
803        assert_eq!(art.get("file:save"), Some(&2));
804        assert_eq!(art.get("file:close"), Some(&3));
805        assert_eq!(art.get("edit:undo"), Some(&4));
806        assert_eq!(art.get("edit:redo"), Some(&5));
807    }
808
809    #[test]
810    fn prefix_scan_basic() {
811        let mut art = AdaptiveRadixTree::new();
812        art.insert("file:open", 1);
813        art.insert("file:save", 2);
814        art.insert("file:close", 3);
815        art.insert("edit:undo", 4);
816
817        let results = art.prefix_scan("file:");
818        assert_eq!(results.len(), 3);
819
820        let edit_results = art.prefix_scan("edit:");
821        assert_eq!(edit_results.len(), 1);
822    }
823
824    #[test]
825    fn prefix_scan_sorted() {
826        let mut art = AdaptiveRadixTree::new();
827        art.insert("c", 3);
828        art.insert("b", 2);
829        art.insert("a", 1);
830
831        let results = art.prefix_scan("");
832        let keys: Vec<&str> = results.iter().map(|(k, _)| *k).collect();
833        assert_eq!(keys, vec!["a", "b", "c"]);
834    }
835
836    #[test]
837    fn update_existing_key() {
838        let mut art = AdaptiveRadixTree::new();
839        assert_eq!(art.insert("key", 1), None);
840        assert_eq!(art.insert("key", 2), Some(1));
841        assert_eq!(art.get("key"), Some(&2));
842        assert_eq!(art.len(), 1);
843    }
844
845    #[test]
846    fn delete_existing() {
847        let mut art = AdaptiveRadixTree::new();
848        art.insert("hello", 42);
849        assert_eq!(art.delete("hello"), Some(42));
850        assert_eq!(art.get("hello"), None);
851        assert_eq!(art.len(), 0);
852    }
853
854    #[test]
855    fn delete_nonexistent() {
856        let mut art = AdaptiveRadixTree::new();
857        art.insert("hello", 42);
858        assert_eq!(art.delete("world"), None);
859        assert_eq!(art.len(), 1);
860    }
861
862    #[test]
863    fn many_inserts_promote_node_types() {
864        let mut art = AdaptiveRadixTree::new();
865        // Insert enough to trigger Node4 → Node16 → Node48 promotion.
866        for i in 0..50u32 {
867            art.insert(&format!("key_{i:03}"), i);
868        }
869        assert_eq!(art.len(), 50);
870
871        // Verify all entries retrievable.
872        for i in 0..50u32 {
873            assert_eq!(art.get(&format!("key_{i:03}")), Some(&i));
874        }
875
876        let dist = art.node_distribution();
877        assert!(dist.leaves >= 50);
878    }
879
880    #[test]
881    fn iter_returns_all_sorted() {
882        let mut art = AdaptiveRadixTree::new();
883        art.insert("z", 26);
884        art.insert("a", 1);
885        art.insert("m", 13);
886
887        let entries = art.iter();
888        let keys: Vec<&str> = entries.iter().map(|(k, _)| *k).collect();
889        assert_eq!(keys, vec!["a", "m", "z"]);
890    }
891
892    #[test]
893    fn shared_prefix_keys() {
894        let mut art = AdaptiveRadixTree::new();
895        art.insert("test", 1);
896        art.insert("testing", 2);
897        art.insert("tested", 3);
898        art.insert("tester", 4);
899
900        assert_eq!(art.len(), 4);
901        assert_eq!(art.get("test"), Some(&1));
902        assert_eq!(art.get("testing"), Some(&2));
903        assert_eq!(art.get("tested"), Some(&3));
904        assert_eq!(art.get("tester"), Some(&4));
905
906        let scan = art.prefix_scan("test");
907        assert_eq!(scan.len(), 4);
908    }
909
910    #[test]
911    fn iter_includes_keys_stored_on_compressed_inner_nodes() {
912        let mut art = AdaptiveRadixTree::new();
913        art.insert("file:save", 1);
914        art.insert("file:save-as", 2);
915
916        let entries = art.iter();
917        let keys: Vec<&str> = entries.iter().map(|(key, _)| *key).collect();
918        assert_eq!(keys, vec!["file:save", "file:save-as"]);
919    }
920
921    #[test]
922    fn deleting_prefix_key_preserves_longer_descendants() {
923        let mut art = AdaptiveRadixTree::new();
924        art.insert("test", 1);
925        art.insert("testing", 2);
926        art.insert("tester", 3);
927
928        assert_eq!(art.delete("test"), Some(1));
929        assert_eq!(art.get("test"), None);
930        assert_eq!(art.get("testing"), Some(&2));
931        assert_eq!(art.get("tester"), Some(&3));
932
933        let keys: Vec<&str> = art
934            .prefix_scan("test")
935            .into_iter()
936            .map(|(key, _)| key)
937            .collect();
938        assert_eq!(keys, vec!["tester", "testing"]);
939    }
940
941    #[test]
942    fn empty_prefix_scan_returns_all() {
943        let mut art = AdaptiveRadixTree::new();
944        art.insert("a", 1);
945        art.insert("b", 2);
946
947        let results = art.prefix_scan("");
948        assert_eq!(results.len(), 2);
949    }
950
951    #[test]
952    fn node_distribution() {
953        let mut art = AdaptiveRadixTree::new();
954        art.insert("a", 1);
955        art.insert("b", 2);
956        art.insert("c", 3);
957
958        let dist = art.node_distribution();
959        assert!(dist.leaves >= 3);
960    }
961
962    #[test]
963    fn command_palette_scenario() {
964        let mut art = AdaptiveRadixTree::new();
965        let commands = [
966            "file:open",
967            "file:save",
968            "file:save-as",
969            "file:close",
970            "file:new",
971            "edit:undo",
972            "edit:redo",
973            "edit:cut",
974            "edit:copy",
975            "edit:paste",
976            "view:sidebar",
977            "view:terminal",
978            "view:explorer",
979            "view:minimap",
980            "go:line",
981            "go:file",
982            "go:symbol",
983            "go:definition",
984        ];
985        for (i, cmd) in commands.iter().enumerate() {
986            art.insert(cmd, i);
987        }
988
989        // User types "file:" → 5 results.
990        assert_eq!(art.prefix_scan("file:").len(), 5);
991        // User types "edit:" → 5 results.
992        assert_eq!(art.prefix_scan("edit:").len(), 5);
993        // User types "view:" → 4 results.
994        assert_eq!(art.prefix_scan("view:").len(), 4);
995        // User types "go:" → 4 results.
996        assert_eq!(art.prefix_scan("go:").len(), 4);
997        // User types "f" → 5 results (all file: commands).
998        assert_eq!(art.prefix_scan("f").len(), 5);
999    }
1000}