Skip to main content

oxilean_kernel/expr/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::{Level, Name};
6
7use std::collections::HashMap;
8
9use super::functions::{
10    collect_consts, collect_fvars, count_bvar_occ, has_loose_bvar, max_loose_bvar_impl,
11};
12
13/// A pair of `StatSummary` values tracking before/after a transformation.
14#[allow(dead_code)]
15pub struct TransformStat {
16    before: StatSummary,
17    after: StatSummary,
18}
19#[allow(dead_code)]
20impl TransformStat {
21    /// Creates a new transform stat recorder.
22    pub fn new() -> Self {
23        Self {
24            before: StatSummary::new(),
25            after: StatSummary::new(),
26        }
27    }
28    /// Records a before value.
29    pub fn record_before(&mut self, v: f64) {
30        self.before.record(v);
31    }
32    /// Records an after value.
33    pub fn record_after(&mut self, v: f64) {
34        self.after.record(v);
35    }
36    /// Returns the mean reduction ratio (after/before).
37    pub fn mean_ratio(&self) -> Option<f64> {
38        let b = self.before.mean()?;
39        let a = self.after.mean()?;
40        if b.abs() < f64::EPSILON {
41            return None;
42        }
43        Some(a / b)
44    }
45}
46/// A dependency closure builder (transitive closure via BFS).
47#[allow(dead_code)]
48pub struct TransitiveClosure {
49    adj: Vec<Vec<usize>>,
50    n: usize,
51}
52#[allow(dead_code)]
53impl TransitiveClosure {
54    /// Creates a transitive closure builder for `n` nodes.
55    pub fn new(n: usize) -> Self {
56        Self {
57            adj: vec![Vec::new(); n],
58            n,
59        }
60    }
61    /// Adds a direct edge.
62    pub fn add_edge(&mut self, from: usize, to: usize) {
63        if from < self.n {
64            self.adj[from].push(to);
65        }
66    }
67    /// Computes all nodes reachable from `start` (including `start`).
68    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
69        let mut visited = vec![false; self.n];
70        let mut queue = std::collections::VecDeque::new();
71        queue.push_back(start);
72        while let Some(node) = queue.pop_front() {
73            if node >= self.n || visited[node] {
74                continue;
75            }
76            visited[node] = true;
77            for &next in &self.adj[node] {
78                queue.push_back(next);
79            }
80        }
81        (0..self.n).filter(|&i| visited[i]).collect()
82    }
83    /// Returns `true` if `from` can transitively reach `to`.
84    pub fn can_reach(&self, from: usize, to: usize) -> bool {
85        self.reachable_from(from).contains(&to)
86    }
87}
88/// A tagged union for representing a simple two-case discriminated union.
89#[allow(dead_code)]
90pub enum Either2<A, B> {
91    /// The first alternative.
92    First(A),
93    /// The second alternative.
94    Second(B),
95}
96#[allow(dead_code)]
97impl<A, B> Either2<A, B> {
98    /// Returns `true` if this is the first alternative.
99    pub fn is_first(&self) -> bool {
100        matches!(self, Either2::First(_))
101    }
102    /// Returns `true` if this is the second alternative.
103    pub fn is_second(&self) -> bool {
104        matches!(self, Either2::Second(_))
105    }
106    /// Returns the first value if present.
107    pub fn first(self) -> Option<A> {
108        match self {
109            Either2::First(a) => Some(a),
110            _ => None,
111        }
112    }
113    /// Returns the second value if present.
114    pub fn second(self) -> Option<B> {
115        match self {
116            Either2::Second(b) => Some(b),
117            _ => None,
118        }
119    }
120    /// Maps over the first alternative.
121    pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
122        match self {
123            Either2::First(a) => Either2::First(f(a)),
124            Either2::Second(b) => Either2::Second(b),
125        }
126    }
127}
128/// A min-heap implemented as a binary heap.
129#[allow(dead_code)]
130pub struct MinHeap<T: Ord> {
131    data: Vec<T>,
132}
133#[allow(dead_code)]
134impl<T: Ord> MinHeap<T> {
135    /// Creates a new empty min-heap.
136    pub fn new() -> Self {
137        Self { data: Vec::new() }
138    }
139    /// Inserts an element.
140    pub fn push(&mut self, val: T) {
141        self.data.push(val);
142        self.sift_up(self.data.len() - 1);
143    }
144    /// Removes and returns the minimum element.
145    pub fn pop(&mut self) -> Option<T> {
146        if self.data.is_empty() {
147            return None;
148        }
149        let n = self.data.len();
150        self.data.swap(0, n - 1);
151        let min = self.data.pop();
152        if !self.data.is_empty() {
153            self.sift_down(0);
154        }
155        min
156    }
157    /// Returns a reference to the minimum element.
158    pub fn peek(&self) -> Option<&T> {
159        self.data.first()
160    }
161    /// Returns the number of elements.
162    pub fn len(&self) -> usize {
163        self.data.len()
164    }
165    /// Returns `true` if empty.
166    pub fn is_empty(&self) -> bool {
167        self.data.is_empty()
168    }
169    fn sift_up(&mut self, mut i: usize) {
170        while i > 0 {
171            let parent = (i - 1) / 2;
172            if self.data[i] < self.data[parent] {
173                self.data.swap(i, parent);
174                i = parent;
175            } else {
176                break;
177            }
178        }
179    }
180    fn sift_down(&mut self, mut i: usize) {
181        let n = self.data.len();
182        loop {
183            let left = 2 * i + 1;
184            let right = 2 * i + 2;
185            let mut smallest = i;
186            if left < n && self.data[left] < self.data[smallest] {
187                smallest = left;
188            }
189            if right < n && self.data[right] < self.data[smallest] {
190                smallest = right;
191            }
192            if smallest == i {
193                break;
194            }
195            self.data.swap(i, smallest);
196            i = smallest;
197        }
198    }
199}
200/// A reusable scratch buffer for path computations.
201#[allow(dead_code)]
202pub struct PathBuf {
203    components: Vec<String>,
204}
205#[allow(dead_code)]
206impl PathBuf {
207    /// Creates a new empty path buffer.
208    pub fn new() -> Self {
209        Self {
210            components: Vec::new(),
211        }
212    }
213    /// Pushes a component.
214    pub fn push(&mut self, comp: impl Into<String>) {
215        self.components.push(comp.into());
216    }
217    /// Pops the last component.
218    pub fn pop(&mut self) {
219        self.components.pop();
220    }
221    /// Returns the current path as a `/`-separated string.
222    pub fn as_str(&self) -> String {
223        self.components.join("/")
224    }
225    /// Returns the depth of the path.
226    pub fn depth(&self) -> usize {
227        self.components.len()
228    }
229    /// Clears the path.
230    pub fn clear(&mut self) {
231        self.components.clear();
232    }
233}
234/// A flat list of substitution pairs `(from, to)`.
235#[allow(dead_code)]
236pub struct FlatSubstitution {
237    pairs: Vec<(String, String)>,
238}
239#[allow(dead_code)]
240impl FlatSubstitution {
241    /// Creates an empty substitution.
242    pub fn new() -> Self {
243        Self { pairs: Vec::new() }
244    }
245    /// Adds a pair.
246    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
247        self.pairs.push((from.into(), to.into()));
248    }
249    /// Applies all substitutions to `s` (leftmost-first order).
250    pub fn apply(&self, s: &str) -> String {
251        let mut result = s.to_string();
252        for (from, to) in &self.pairs {
253            result = result.replace(from.as_str(), to.as_str());
254        }
255        result
256    }
257    /// Returns the number of pairs.
258    pub fn len(&self) -> usize {
259        self.pairs.len()
260    }
261    /// Returns `true` if empty.
262    pub fn is_empty(&self) -> bool {
263        self.pairs.is_empty()
264    }
265}
266/// A simple directed acyclic graph.
267#[allow(dead_code)]
268pub struct SimpleDag {
269    /// `edges[i]` is the list of direct successors of node `i`.
270    edges: Vec<Vec<usize>>,
271}
272#[allow(dead_code)]
273impl SimpleDag {
274    /// Creates a DAG with `n` nodes and no edges.
275    pub fn new(n: usize) -> Self {
276        Self {
277            edges: vec![Vec::new(); n],
278        }
279    }
280    /// Adds an edge from `from` to `to`.
281    pub fn add_edge(&mut self, from: usize, to: usize) {
282        if from < self.edges.len() {
283            self.edges[from].push(to);
284        }
285    }
286    /// Returns the successors of `node`.
287    pub fn successors(&self, node: usize) -> &[usize] {
288        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
289    }
290    /// Returns `true` if `from` can reach `to` via DFS.
291    pub fn can_reach(&self, from: usize, to: usize) -> bool {
292        let mut visited = vec![false; self.edges.len()];
293        self.dfs(from, to, &mut visited)
294    }
295    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
296        if cur == target {
297            return true;
298        }
299        if cur >= visited.len() || visited[cur] {
300            return false;
301        }
302        visited[cur] = true;
303        for &next in self.successors(cur) {
304            if self.dfs(next, target, visited) {
305                return true;
306            }
307        }
308        false
309    }
310    /// Returns the topological order of nodes, or `None` if a cycle is detected.
311    pub fn topological_sort(&self) -> Option<Vec<usize>> {
312        let n = self.edges.len();
313        let mut in_degree = vec![0usize; n];
314        for succs in &self.edges {
315            for &s in succs {
316                if s < n {
317                    in_degree[s] += 1;
318                }
319            }
320        }
321        let mut queue: std::collections::VecDeque<usize> =
322            (0..n).filter(|&i| in_degree[i] == 0).collect();
323        let mut order = Vec::new();
324        while let Some(node) = queue.pop_front() {
325            order.push(node);
326            for &s in self.successors(node) {
327                if s < n {
328                    in_degree[s] -= 1;
329                    if in_degree[s] == 0 {
330                        queue.push_back(s);
331                    }
332                }
333            }
334        }
335        if order.len() == n {
336            Some(order)
337        } else {
338            None
339        }
340    }
341    /// Returns the number of nodes.
342    pub fn num_nodes(&self) -> usize {
343        self.edges.len()
344    }
345}
346/// A type-erased function pointer with arity tracking.
347#[allow(dead_code)]
348pub struct RawFnPtr {
349    /// The raw function pointer (stored as usize for type erasure).
350    ptr: usize,
351    arity: usize,
352    name: String,
353}
354#[allow(dead_code)]
355impl RawFnPtr {
356    /// Creates a new raw function pointer descriptor.
357    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
358        Self {
359            ptr,
360            arity,
361            name: name.into(),
362        }
363    }
364    /// Returns the arity.
365    pub fn arity(&self) -> usize {
366        self.arity
367    }
368    /// Returns the name.
369    pub fn name(&self) -> &str {
370        &self.name
371    }
372    /// Returns the raw pointer value.
373    pub fn raw(&self) -> usize {
374        self.ptr
375    }
376}
377/// A sparse vector: stores only non-default elements.
378#[allow(dead_code)]
379pub struct SparseVec<T: Default + Clone + PartialEq> {
380    entries: std::collections::HashMap<usize, T>,
381    default_: T,
382    logical_len: usize,
383}
384#[allow(dead_code)]
385impl<T: Default + Clone + PartialEq> SparseVec<T> {
386    /// Creates a new sparse vector with logical length `len`.
387    pub fn new(len: usize) -> Self {
388        Self {
389            entries: std::collections::HashMap::new(),
390            default_: T::default(),
391            logical_len: len,
392        }
393    }
394    /// Sets element at `idx`.
395    pub fn set(&mut self, idx: usize, val: T) {
396        if val == self.default_ {
397            self.entries.remove(&idx);
398        } else {
399            self.entries.insert(idx, val);
400        }
401    }
402    /// Gets element at `idx`.
403    pub fn get(&self, idx: usize) -> &T {
404        self.entries.get(&idx).unwrap_or(&self.default_)
405    }
406    /// Returns the logical length.
407    pub fn len(&self) -> usize {
408        self.logical_len
409    }
410    /// Returns whether the collection is empty.
411    pub fn is_empty(&self) -> bool {
412        self.len() == 0
413    }
414    /// Returns the number of non-default elements.
415    pub fn nnz(&self) -> usize {
416        self.entries.len()
417    }
418}
419/// Binder display annotation.
420///
421/// Determines how arguments are displayed and inferred.
422#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
423pub enum BinderInfo {
424    /// Default explicit argument: (x : α)
425    Default,
426    /// Implicit argument: {x : α}
427    Implicit,
428    /// Strict implicit: ⦃x : α⦄
429    StrictImplicit,
430    /// Instance implicit (type class): [x : α]
431    InstImplicit,
432}
433impl BinderInfo {
434    /// Check if this is an explicit (default) binder.
435    pub fn is_explicit(self) -> bool {
436        matches!(self, BinderInfo::Default)
437    }
438    /// Check if this is an implicit binder.
439    pub fn is_implicit(self) -> bool {
440        matches!(self, BinderInfo::Implicit | BinderInfo::StrictImplicit)
441    }
442    /// Check if this is an instance-implicit binder.
443    pub fn is_inst_implicit(self) -> bool {
444        matches!(self, BinderInfo::InstImplicit)
445    }
446    /// Return the opening delimiter for this binder.
447    pub fn open_delim(self) -> &'static str {
448        match self {
449            BinderInfo::Default => "(",
450            BinderInfo::Implicit => "{",
451            BinderInfo::StrictImplicit => "⦃",
452            BinderInfo::InstImplicit => "[",
453        }
454    }
455    /// Return the closing delimiter for this binder.
456    pub fn close_delim(self) -> &'static str {
457        match self {
458            BinderInfo::Default => ")",
459            BinderInfo::Implicit => "}",
460            BinderInfo::StrictImplicit => "⦄",
461            BinderInfo::InstImplicit => "]",
462        }
463    }
464}
465/// Unique identifier for free variables.
466///
467/// Free variables are used during elaboration and type checking to represent
468/// local hypotheses.
469#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
470pub struct FVarId(pub u64);
471impl FVarId {
472    /// Create a new free variable ID.
473    pub fn new(id: u64) -> Self {
474        FVarId(id)
475    }
476}
477impl FVarId {
478    /// Return the underlying integer ID.
479    pub fn raw(self) -> u64 {
480        self.0
481    }
482}
483/// A label set for a graph node.
484#[allow(dead_code)]
485pub struct LabelSet {
486    labels: Vec<String>,
487}
488#[allow(dead_code)]
489impl LabelSet {
490    /// Creates a new empty label set.
491    pub fn new() -> Self {
492        Self { labels: Vec::new() }
493    }
494    /// Adds a label (deduplicates).
495    pub fn add(&mut self, label: impl Into<String>) {
496        let s = label.into();
497        if !self.labels.contains(&s) {
498            self.labels.push(s);
499        }
500    }
501    /// Returns `true` if `label` is present.
502    pub fn has(&self, label: &str) -> bool {
503        self.labels.iter().any(|l| l == label)
504    }
505    /// Returns the count of labels.
506    pub fn count(&self) -> usize {
507        self.labels.len()
508    }
509    /// Returns all labels.
510    pub fn all(&self) -> &[String] {
511        &self.labels
512    }
513}
514/// A simple decision tree node for rule dispatching.
515#[allow(dead_code)]
516#[allow(missing_docs)]
517pub enum DecisionNode {
518    /// A leaf with an action string.
519    Leaf(String),
520    /// An interior node: check `key` equals `val` → `yes_branch`, else `no_branch`.
521    Branch {
522        key: String,
523        val: String,
524        yes_branch: Box<DecisionNode>,
525        no_branch: Box<DecisionNode>,
526    },
527}
528#[allow(dead_code)]
529impl DecisionNode {
530    /// Evaluates the decision tree with the given context.
531    pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
532        match self {
533            DecisionNode::Leaf(action) => action.as_str(),
534            DecisionNode::Branch {
535                key,
536                val,
537                yes_branch,
538                no_branch,
539            } => {
540                let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
541                if actual == val.as_str() {
542                    yes_branch.evaluate(ctx)
543                } else {
544                    no_branch.evaluate(ctx)
545                }
546            }
547        }
548    }
549    /// Returns the depth of the decision tree.
550    pub fn depth(&self) -> usize {
551        match self {
552            DecisionNode::Leaf(_) => 0,
553            DecisionNode::Branch {
554                yes_branch,
555                no_branch,
556                ..
557            } => 1 + yes_branch.depth().max(no_branch.depth()),
558        }
559    }
560}
561/// Represents a rewrite rule `lhs → rhs`.
562#[allow(dead_code)]
563#[allow(missing_docs)]
564pub struct RewriteRule {
565    /// The name of the rule.
566    pub name: String,
567    /// A string representation of the LHS pattern.
568    pub lhs: String,
569    /// A string representation of the RHS.
570    pub rhs: String,
571    /// Whether this is a conditional rule (has side conditions).
572    pub conditional: bool,
573}
574#[allow(dead_code)]
575impl RewriteRule {
576    /// Creates an unconditional rewrite rule.
577    pub fn unconditional(
578        name: impl Into<String>,
579        lhs: impl Into<String>,
580        rhs: impl Into<String>,
581    ) -> Self {
582        Self {
583            name: name.into(),
584            lhs: lhs.into(),
585            rhs: rhs.into(),
586            conditional: false,
587        }
588    }
589    /// Creates a conditional rewrite rule.
590    pub fn conditional(
591        name: impl Into<String>,
592        lhs: impl Into<String>,
593        rhs: impl Into<String>,
594    ) -> Self {
595        Self {
596            name: name.into(),
597            lhs: lhs.into(),
598            rhs: rhs.into(),
599            conditional: true,
600        }
601    }
602    /// Returns a textual representation.
603    pub fn display(&self) -> String {
604        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
605    }
606}
607/// A hierarchical configuration tree.
608#[allow(dead_code)]
609pub struct ConfigNode {
610    key: String,
611    value: Option<String>,
612    children: Vec<ConfigNode>,
613}
614#[allow(dead_code)]
615impl ConfigNode {
616    /// Creates a leaf config node with a value.
617    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
618        Self {
619            key: key.into(),
620            value: Some(value.into()),
621            children: Vec::new(),
622        }
623    }
624    /// Creates a section node with children.
625    pub fn section(key: impl Into<String>) -> Self {
626        Self {
627            key: key.into(),
628            value: None,
629            children: Vec::new(),
630        }
631    }
632    /// Adds a child node.
633    pub fn add_child(&mut self, child: ConfigNode) {
634        self.children.push(child);
635    }
636    /// Returns the key.
637    pub fn key(&self) -> &str {
638        &self.key
639    }
640    /// Returns the value, or `None` for section nodes.
641    pub fn value(&self) -> Option<&str> {
642        self.value.as_deref()
643    }
644    /// Returns the number of children.
645    pub fn num_children(&self) -> usize {
646        self.children.len()
647    }
648    /// Looks up a dot-separated path.
649    pub fn lookup(&self, path: &str) -> Option<&str> {
650        let mut parts = path.splitn(2, '.');
651        let head = parts.next()?;
652        let tail = parts.next();
653        if head != self.key {
654            return None;
655        }
656        match tail {
657            None => self.value.as_deref(),
658            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
659        }
660    }
661    fn lookup_relative(&self, path: &str) -> Option<&str> {
662        let mut parts = path.splitn(2, '.');
663        let head = parts.next()?;
664        let tail = parts.next();
665        if head != self.key {
666            return None;
667        }
668        match tail {
669            None => self.value.as_deref(),
670            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
671        }
672    }
673}
674/// A set of rewrite rules.
675#[allow(dead_code)]
676pub struct RewriteRuleSet {
677    rules: Vec<RewriteRule>,
678}
679#[allow(dead_code)]
680impl RewriteRuleSet {
681    /// Creates an empty rule set.
682    pub fn new() -> Self {
683        Self { rules: Vec::new() }
684    }
685    /// Adds a rule.
686    pub fn add(&mut self, rule: RewriteRule) {
687        self.rules.push(rule);
688    }
689    /// Returns the number of rules.
690    pub fn len(&self) -> usize {
691        self.rules.len()
692    }
693    /// Returns `true` if the set is empty.
694    pub fn is_empty(&self) -> bool {
695        self.rules.is_empty()
696    }
697    /// Returns all conditional rules.
698    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
699        self.rules.iter().filter(|r| r.conditional).collect()
700    }
701    /// Returns all unconditional rules.
702    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
703        self.rules.iter().filter(|r| !r.conditional).collect()
704    }
705    /// Looks up a rule by name.
706    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
707        self.rules.iter().find(|r| r.name == name)
708    }
709}
710/// A counter that can measure elapsed time between snapshots.
711#[allow(dead_code)]
712pub struct Stopwatch {
713    start: std::time::Instant,
714    splits: Vec<f64>,
715}
716#[allow(dead_code)]
717impl Stopwatch {
718    /// Creates and starts a new stopwatch.
719    pub fn start() -> Self {
720        Self {
721            start: std::time::Instant::now(),
722            splits: Vec::new(),
723        }
724    }
725    /// Records a split time (elapsed since start).
726    pub fn split(&mut self) {
727        self.splits.push(self.elapsed_ms());
728    }
729    /// Returns total elapsed milliseconds since start.
730    pub fn elapsed_ms(&self) -> f64 {
731        self.start.elapsed().as_secs_f64() * 1000.0
732    }
733    /// Returns all recorded split times.
734    pub fn splits(&self) -> &[f64] {
735        &self.splits
736    }
737    /// Returns the number of splits.
738    pub fn num_splits(&self) -> usize {
739        self.splits.len()
740    }
741}
742/// A write-once cell.
743#[allow(dead_code)]
744pub struct WriteOnce<T> {
745    value: std::cell::Cell<Option<T>>,
746}
747#[allow(dead_code)]
748impl<T: Copy> WriteOnce<T> {
749    /// Creates an empty write-once cell.
750    pub fn new() -> Self {
751        Self {
752            value: std::cell::Cell::new(None),
753        }
754    }
755    /// Writes a value.  Returns `false` if already written.
756    pub fn write(&self, val: T) -> bool {
757        if self.value.get().is_some() {
758            return false;
759        }
760        self.value.set(Some(val));
761        true
762    }
763    /// Returns the value if written.
764    pub fn read(&self) -> Option<T> {
765        self.value.get()
766    }
767    /// Returns `true` if the value has been written.
768    pub fn is_written(&self) -> bool {
769        self.value.get().is_some()
770    }
771}
772/// A generic counter that tracks min/max/sum for statistical summaries.
773#[allow(dead_code)]
774pub struct StatSummary {
775    count: u64,
776    sum: f64,
777    min: f64,
778    max: f64,
779}
780#[allow(dead_code)]
781impl StatSummary {
782    /// Creates an empty summary.
783    pub fn new() -> Self {
784        Self {
785            count: 0,
786            sum: 0.0,
787            min: f64::INFINITY,
788            max: f64::NEG_INFINITY,
789        }
790    }
791    /// Records a sample.
792    pub fn record(&mut self, val: f64) {
793        self.count += 1;
794        self.sum += val;
795        if val < self.min {
796            self.min = val;
797        }
798        if val > self.max {
799            self.max = val;
800        }
801    }
802    /// Returns the mean, or `None` if no samples.
803    pub fn mean(&self) -> Option<f64> {
804        if self.count == 0 {
805            None
806        } else {
807            Some(self.sum / self.count as f64)
808        }
809    }
810    /// Returns the minimum, or `None` if no samples.
811    pub fn min(&self) -> Option<f64> {
812        if self.count == 0 {
813            None
814        } else {
815            Some(self.min)
816        }
817    }
818    /// Returns the maximum, or `None` if no samples.
819    pub fn max(&self) -> Option<f64> {
820        if self.count == 0 {
821            None
822        } else {
823            Some(self.max)
824        }
825    }
826    /// Returns the count of recorded samples.
827    pub fn count(&self) -> u64 {
828        self.count
829    }
830}
831/// Literal values supported natively.
832#[derive(Clone, PartialEq, Eq, Hash, Debug)]
833pub enum Literal {
834    /// Natural number literal.
835    Nat(u64),
836    /// String literal.
837    Str(String),
838}
839impl Literal {
840    /// Return true if this is a natural number literal.
841    pub fn is_nat(&self) -> bool {
842        matches!(self, Literal::Nat(_))
843    }
844    /// Return true if this is a string literal.
845    pub fn is_str(&self) -> bool {
846        matches!(self, Literal::Str(_))
847    }
848    /// Extract the natural number value, or `None`.
849    pub fn as_nat(&self) -> Option<u64> {
850        if let Literal::Nat(n) = self {
851            Some(*n)
852        } else {
853            None
854        }
855    }
856    /// Extract the string value, or `None`.
857    pub fn as_str(&self) -> Option<&str> {
858        if let Literal::Str(s) = self {
859            Some(s.as_str())
860        } else {
861            None
862        }
863    }
864    /// Return the Lean type name for this literal.
865    pub fn type_name(&self) -> &'static str {
866        match self {
867            Literal::Nat(_) => "Nat",
868            Literal::Str(_) => "String",
869        }
870    }
871}
872/// A window iterator that yields overlapping windows of size `n`.
873#[allow(dead_code)]
874pub struct WindowIterator<'a, T> {
875    pub(super) data: &'a [T],
876    pub(super) pos: usize,
877    pub(super) window: usize,
878}
879#[allow(dead_code)]
880impl<'a, T> WindowIterator<'a, T> {
881    /// Creates a new window iterator.
882    pub fn new(data: &'a [T], window: usize) -> Self {
883        Self {
884            data,
885            pos: 0,
886            window,
887        }
888    }
889}
890/// A simple stack-based calculator for arithmetic expressions.
891#[allow(dead_code)]
892pub struct StackCalc {
893    stack: Vec<i64>,
894}
895#[allow(dead_code)]
896impl StackCalc {
897    /// Creates a new empty calculator.
898    pub fn new() -> Self {
899        Self { stack: Vec::new() }
900    }
901    /// Pushes an integer literal.
902    pub fn push(&mut self, n: i64) {
903        self.stack.push(n);
904    }
905    /// Adds the top two values.  Panics if fewer than two values.
906    pub fn add(&mut self) {
907        let b = self
908            .stack
909            .pop()
910            .expect("stack must have at least two values for add");
911        let a = self
912            .stack
913            .pop()
914            .expect("stack must have at least two values for add");
915        self.stack.push(a + b);
916    }
917    /// Subtracts top from second.
918    pub fn sub(&mut self) {
919        let b = self
920            .stack
921            .pop()
922            .expect("stack must have at least two values for sub");
923        let a = self
924            .stack
925            .pop()
926            .expect("stack must have at least two values for sub");
927        self.stack.push(a - b);
928    }
929    /// Multiplies the top two values.
930    pub fn mul(&mut self) {
931        let b = self
932            .stack
933            .pop()
934            .expect("stack must have at least two values for mul");
935        let a = self
936            .stack
937            .pop()
938            .expect("stack must have at least two values for mul");
939        self.stack.push(a * b);
940    }
941    /// Peeks the top value.
942    pub fn peek(&self) -> Option<i64> {
943        self.stack.last().copied()
944    }
945    /// Returns the stack depth.
946    pub fn depth(&self) -> usize {
947        self.stack.len()
948    }
949}
950/// A pool of reusable string buffers.
951#[allow(dead_code)]
952pub struct StringPool {
953    free: Vec<String>,
954}
955#[allow(dead_code)]
956impl StringPool {
957    /// Creates a new empty string pool.
958    pub fn new() -> Self {
959        Self { free: Vec::new() }
960    }
961    /// Takes a string from the pool (may be empty).
962    pub fn take(&mut self) -> String {
963        self.free.pop().unwrap_or_default()
964    }
965    /// Returns a string to the pool.
966    pub fn give(&mut self, mut s: String) {
967        s.clear();
968        self.free.push(s);
969    }
970    /// Returns the number of free strings in the pool.
971    pub fn free_count(&self) -> usize {
972        self.free.len()
973    }
974}
975/// Core expression type.
976///
977/// This is the heart of the kernel. All terms in the type theory are
978/// represented as `Expr` values.
979///
980/// We use `Box` for sub-expressions for simplicity in Phase 0.
981/// In later phases, these will be `Idx<Expr>` pointing into an arena.
982#[derive(Clone, PartialEq, Eq, Hash, Debug)]
983pub enum Expr {
984    /// Sort u — universe.
985    ///
986    /// `Prop` is `Sort(Level::Zero)`.
987    /// `Type` is `Sort(Level::Succ(Level::Zero))`.
988    Sort(Level),
989    /// Bound variable (de Bruijn index, 0-based).
990    ///
991    /// `BVar(0)` refers to the innermost binder.
992    /// `BVar(n)` refers to the n-th enclosing binder.
993    BVar(u32),
994    /// Free variable (locally nameless).
995    ///
996    /// Used during type checking to represent local hypotheses.
997    FVar(FVarId),
998    /// Named constant with universe level instantiation.
999    ///
1000    /// `Const(name, levels)` represents a global constant with its
1001    /// universe parameters instantiated.
1002    Const(Name, Vec<Level>),
1003    /// Function application: `f a`.
1004    ///
1005    /// Application is always binary. `f a b` is `App(App(f, a), b)`.
1006    App(Box<Expr>, Box<Expr>),
1007    /// Lambda abstraction: `λ (x : type), body`.
1008    Lam(BinderInfo, Name, Box<Expr>, Box<Expr>),
1009    /// Dependent function type: `Π (x : type), body`.
1010    ///
1011    /// Non-dependent arrows `A → B` are represented as
1012    /// `Pi(Default, _, A, B)` where the body doesn't use `BVar(0)`.
1013    Pi(BinderInfo, Name, Box<Expr>, Box<Expr>),
1014    /// Let binding: `let x : type := val in body`.
1015    Let(Name, Box<Expr>, Box<Expr>, Box<Expr>),
1016    /// Literal value (Nat or String).
1017    Lit(Literal),
1018    /// Structure projection: `projName.idx struct`.
1019    Proj(Name, u32, Box<Expr>),
1020}
1021impl Expr {
1022    /// Check if this is a Sort.
1023    pub fn is_sort(&self) -> bool {
1024        matches!(self, Expr::Sort(_))
1025    }
1026    /// Check if this is Prop.
1027    pub fn is_prop(&self) -> bool {
1028        matches!(self, Expr::Sort(l) if l.is_zero())
1029    }
1030    /// Check if this is a bound variable.
1031    pub fn is_bvar(&self) -> bool {
1032        matches!(self, Expr::BVar(_))
1033    }
1034    /// Check if this is a free variable.
1035    pub fn is_fvar(&self) -> bool {
1036        matches!(self, Expr::FVar(_))
1037    }
1038    /// Check if this is an application.
1039    pub fn is_app(&self) -> bool {
1040        matches!(self, Expr::App(_, _))
1041    }
1042    /// Check if this is a lambda.
1043    pub fn is_lambda(&self) -> bool {
1044        matches!(self, Expr::Lam(_, _, _, _))
1045    }
1046    /// Check if this is a Pi type.
1047    pub fn is_pi(&self) -> bool {
1048        matches!(self, Expr::Pi(_, _, _, _))
1049    }
1050}
1051impl Expr {
1052    /// Check if this expression is a let binding.
1053    pub fn is_let(&self) -> bool {
1054        matches!(self, Expr::Let(_, _, _, _))
1055    }
1056    /// Check if this expression is a literal.
1057    pub fn is_lit(&self) -> bool {
1058        matches!(self, Expr::Lit(_))
1059    }
1060    /// Check if this expression is a named constant.
1061    pub fn is_const(&self) -> bool {
1062        matches!(self, Expr::Const(_, _))
1063    }
1064    /// Check if this expression is a projection.
1065    pub fn is_proj(&self) -> bool {
1066        matches!(self, Expr::Proj(_, _, _))
1067    }
1068    /// Check if this expression is an "atom" (cannot be further reduced
1069    /// without context): Sort, BVar, FVar, Const, Lit.
1070    pub fn is_atom(&self) -> bool {
1071        matches!(
1072            self,
1073            Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_)
1074        )
1075    }
1076    /// Extract the bound variable index, or `None`.
1077    pub fn as_bvar(&self) -> Option<u32> {
1078        if let Expr::BVar(n) = self {
1079            Some(*n)
1080        } else {
1081            None
1082        }
1083    }
1084    /// Extract the free variable ID, or `None`.
1085    pub fn as_fvar(&self) -> Option<FVarId> {
1086        if let Expr::FVar(id) = self {
1087            Some(*id)
1088        } else {
1089            None
1090        }
1091    }
1092    /// Extract the constant name, or `None`.
1093    pub fn as_const_name(&self) -> Option<&Name> {
1094        if let Expr::Const(n, _) = self {
1095            Some(n)
1096        } else {
1097            None
1098        }
1099    }
1100    /// Extract the sort level, or `None`.
1101    pub fn as_sort_level(&self) -> Option<&Level> {
1102        if let Expr::Sort(l) = self {
1103            Some(l)
1104        } else {
1105            None
1106        }
1107    }
1108    /// Return the head and argument list of an application spine.
1109    ///
1110    /// For `f a b c`, returns `(&f, [&a, &b, &c])`.
1111    pub fn app_head_args(&self) -> (&Expr, Vec<&Expr>) {
1112        let mut args = Vec::new();
1113        let mut cur = self;
1114        while let Expr::App(f, a) = cur {
1115            args.push(a.as_ref());
1116            cur = f;
1117        }
1118        args.reverse();
1119        (cur, args)
1120    }
1121    /// Return the number of arguments in an application spine.
1122    pub fn app_arity(&self) -> usize {
1123        let mut n = 0;
1124        let mut cur = self;
1125        while let Expr::App(f, _) = cur {
1126            n += 1;
1127            cur = f;
1128        }
1129        n
1130    }
1131    /// Count the number of leading Pi binders.
1132    pub fn pi_arity(&self) -> usize {
1133        let mut n = 0;
1134        let mut cur = self;
1135        while let Expr::Pi(_, _, _, body) = cur {
1136            n += 1;
1137            cur = body;
1138        }
1139        n
1140    }
1141    /// Count the number of leading Lambda binders.
1142    pub fn lam_arity(&self) -> usize {
1143        let mut n = 0;
1144        let mut cur = self;
1145        while let Expr::Lam(_, _, _, body) = cur {
1146            n += 1;
1147            cur = body;
1148        }
1149        n
1150    }
1151    /// Compute the AST size (total number of nodes).
1152    pub fn size(&self) -> usize {
1153        match self {
1154            Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => 1,
1155            Expr::App(f, a) => 1 + f.size() + a.size(),
1156            Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => 1 + ty.size() + body.size(),
1157            Expr::Let(_, ty, val, body) => 1 + ty.size() + val.size() + body.size(),
1158            Expr::Proj(_, _, s) => 1 + s.size(),
1159        }
1160    }
1161    /// Compute the AST depth (longest path to a leaf).
1162    pub fn ast_depth(&self) -> usize {
1163        match self {
1164            Expr::Sort(_) | Expr::BVar(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => 0,
1165            Expr::App(f, a) => 1 + f.ast_depth().max(a.ast_depth()),
1166            Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
1167                1 + ty.ast_depth().max(body.ast_depth())
1168            }
1169            Expr::Let(_, ty, val, body) => {
1170                1 + ty.ast_depth().max(val.ast_depth()).max(body.ast_depth())
1171            }
1172            Expr::Proj(_, _, s) => 1 + s.ast_depth(),
1173        }
1174    }
1175    /// Apply this expression to a list of arguments.
1176    ///
1177    /// `e.mk_app_many(&[a, b, c])` returns `((e a) b) c`.
1178    pub fn mk_app_many(self, args: &[Expr]) -> Expr {
1179        args.iter()
1180            .fold(self, |acc, a| Expr::App(Box::new(acc), Box::new(a.clone())))
1181    }
1182    /// Check if this expression has any loose bound variables at depth `d`.
1183    pub fn has_loose_bvar_at(&self, d: u32) -> bool {
1184        has_loose_bvar(self, d)
1185    }
1186    /// Count occurrences of bound variable `BVar(idx)` in this expression.
1187    pub fn count_bvar_occurrences(&self, idx: u32) -> usize {
1188        count_bvar_occ(self, idx)
1189    }
1190    /// Check if this expression is closed (no loose bound variables).
1191    pub fn is_closed(&self) -> bool {
1192        !has_loose_bvar(self, 0) && self.max_loose_bvar().is_none()
1193    }
1194    /// Find the maximum loose bound variable index, if any.
1195    fn max_loose_bvar(&self) -> Option<u32> {
1196        max_loose_bvar_impl(self, 0)
1197    }
1198    /// Collect all free variable IDs occurring in this expression.
1199    pub fn free_vars(&self) -> std::collections::HashSet<FVarId> {
1200        let mut set = std::collections::HashSet::new();
1201        collect_fvars(self, &mut set);
1202        set
1203    }
1204    /// Collect all constant names occurring in this expression.
1205    pub fn constants(&self) -> std::collections::HashSet<Name> {
1206        let mut set = std::collections::HashSet::new();
1207        collect_consts(self, &mut set);
1208        set
1209    }
1210}
1211/// A non-empty list (at least one element guaranteed).
1212#[allow(dead_code)]
1213pub struct NonEmptyVec<T> {
1214    head: T,
1215    tail: Vec<T>,
1216}
1217#[allow(dead_code)]
1218impl<T> NonEmptyVec<T> {
1219    /// Creates a non-empty vec with a single element.
1220    pub fn singleton(val: T) -> Self {
1221        Self {
1222            head: val,
1223            tail: Vec::new(),
1224        }
1225    }
1226    /// Pushes an element.
1227    pub fn push(&mut self, val: T) {
1228        self.tail.push(val);
1229    }
1230    /// Returns a reference to the first element.
1231    pub fn first(&self) -> &T {
1232        &self.head
1233    }
1234    /// Returns a reference to the last element.
1235    pub fn last(&self) -> &T {
1236        self.tail.last().unwrap_or(&self.head)
1237    }
1238    /// Returns the number of elements.
1239    pub fn len(&self) -> usize {
1240        1 + self.tail.len()
1241    }
1242    /// Returns whether the collection is empty.
1243    pub fn is_empty(&self) -> bool {
1244        self.len() == 0
1245    }
1246    /// Returns all elements as a Vec.
1247    pub fn to_vec(&self) -> Vec<&T> {
1248        let mut v = vec![&self.head];
1249        v.extend(self.tail.iter());
1250        v
1251    }
1252}
1253/// A simple mutable key-value store for test fixtures.
1254#[allow(dead_code)]
1255pub struct Fixture {
1256    data: std::collections::HashMap<String, String>,
1257}
1258#[allow(dead_code)]
1259impl Fixture {
1260    /// Creates an empty fixture.
1261    pub fn new() -> Self {
1262        Self {
1263            data: std::collections::HashMap::new(),
1264        }
1265    }
1266    /// Sets a key.
1267    pub fn set(&mut self, key: impl Into<String>, val: impl Into<String>) {
1268        self.data.insert(key.into(), val.into());
1269    }
1270    /// Gets a value.
1271    pub fn get(&self, key: &str) -> Option<&str> {
1272        self.data.get(key).map(|s| s.as_str())
1273    }
1274    /// Returns the number of entries.
1275    pub fn len(&self) -> usize {
1276        self.data.len()
1277    }
1278    /// Returns whether the collection is empty.
1279    pub fn is_empty(&self) -> bool {
1280        self.len() == 0
1281    }
1282}
1283/// Generator for fresh free variable IDs.
1284///
1285/// Produces a strictly increasing sequence of unique IDs.
1286#[derive(Debug, Default)]
1287pub struct FVarIdGen {
1288    next: u64,
1289}
1290impl FVarIdGen {
1291    /// Create a new generator starting at 0.
1292    pub fn new() -> Self {
1293        Self::default()
1294    }
1295    /// Produce the next fresh ID.
1296    pub fn fresh(&mut self) -> FVarId {
1297        let id = FVarId::new(self.next);
1298        self.next += 1;
1299        id
1300    }
1301    /// Peek at the next ID without consuming it.
1302    pub fn peek(&self) -> FVarId {
1303        FVarId::new(self.next)
1304    }
1305    /// Reset the generator.
1306    pub fn reset(&mut self) {
1307        self.next = 0;
1308    }
1309    /// Return the current counter value.
1310    pub fn current(&self) -> u64 {
1311        self.next
1312    }
1313}
1314/// A simple key-value store backed by a sorted Vec for small maps.
1315#[allow(dead_code)]
1316pub struct SmallMap<K: Ord + Clone, V: Clone> {
1317    entries: Vec<(K, V)>,
1318}
1319#[allow(dead_code)]
1320impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
1321    /// Creates a new empty small map.
1322    pub fn new() -> Self {
1323        Self {
1324            entries: Vec::new(),
1325        }
1326    }
1327    /// Inserts or replaces the value for `key`.
1328    pub fn insert(&mut self, key: K, val: V) {
1329        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
1330            Ok(i) => self.entries[i].1 = val,
1331            Err(i) => self.entries.insert(i, (key, val)),
1332        }
1333    }
1334    /// Returns the value for `key`, or `None`.
1335    pub fn get(&self, key: &K) -> Option<&V> {
1336        self.entries
1337            .binary_search_by_key(&key, |(k, _)| k)
1338            .ok()
1339            .map(|i| &self.entries[i].1)
1340    }
1341    /// Returns the number of entries.
1342    pub fn len(&self) -> usize {
1343        self.entries.len()
1344    }
1345    /// Returns `true` if empty.
1346    pub fn is_empty(&self) -> bool {
1347        self.entries.is_empty()
1348    }
1349    /// Returns all keys.
1350    pub fn keys(&self) -> Vec<&K> {
1351        self.entries.iter().map(|(k, _)| k).collect()
1352    }
1353    /// Returns all values.
1354    pub fn values(&self) -> Vec<&V> {
1355        self.entries.iter().map(|(_, v)| v).collect()
1356    }
1357}
1358/// A token bucket rate limiter.
1359#[allow(dead_code)]
1360pub struct TokenBucket {
1361    capacity: u64,
1362    tokens: u64,
1363    refill_per_ms: u64,
1364    last_refill: std::time::Instant,
1365}
1366#[allow(dead_code)]
1367impl TokenBucket {
1368    /// Creates a new token bucket.
1369    pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
1370        Self {
1371            capacity,
1372            tokens: capacity,
1373            refill_per_ms,
1374            last_refill: std::time::Instant::now(),
1375        }
1376    }
1377    /// Attempts to consume `n` tokens.  Returns `true` on success.
1378    pub fn try_consume(&mut self, n: u64) -> bool {
1379        self.refill();
1380        if self.tokens >= n {
1381            self.tokens -= n;
1382            true
1383        } else {
1384            false
1385        }
1386    }
1387    fn refill(&mut self) {
1388        let now = std::time::Instant::now();
1389        let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
1390        if elapsed_ms > 0 {
1391            let new_tokens = elapsed_ms * self.refill_per_ms;
1392            self.tokens = (self.tokens + new_tokens).min(self.capacity);
1393            self.last_refill = now;
1394        }
1395    }
1396    /// Returns the number of currently available tokens.
1397    pub fn available(&self) -> u64 {
1398        self.tokens
1399    }
1400    /// Returns the bucket capacity.
1401    pub fn capacity(&self) -> u64 {
1402        self.capacity
1403    }
1404}
1405/// A fixed-size sliding window that computes a running sum.
1406#[allow(dead_code)]
1407pub struct SlidingSum {
1408    window: Vec<f64>,
1409    capacity: usize,
1410    pos: usize,
1411    sum: f64,
1412    count: usize,
1413}
1414#[allow(dead_code)]
1415impl SlidingSum {
1416    /// Creates a sliding sum with the given window size.
1417    pub fn new(capacity: usize) -> Self {
1418        Self {
1419            window: vec![0.0; capacity],
1420            capacity,
1421            pos: 0,
1422            sum: 0.0,
1423            count: 0,
1424        }
1425    }
1426    /// Adds a value to the window, removing the oldest if full.
1427    pub fn push(&mut self, val: f64) {
1428        let oldest = self.window[self.pos];
1429        self.sum -= oldest;
1430        self.sum += val;
1431        self.window[self.pos] = val;
1432        self.pos = (self.pos + 1) % self.capacity;
1433        if self.count < self.capacity {
1434            self.count += 1;
1435        }
1436    }
1437    /// Returns the current window sum.
1438    pub fn sum(&self) -> f64 {
1439        self.sum
1440    }
1441    /// Returns the window mean, or `None` if empty.
1442    pub fn mean(&self) -> Option<f64> {
1443        if self.count == 0 {
1444            None
1445        } else {
1446            Some(self.sum / self.count as f64)
1447        }
1448    }
1449    /// Returns the current window size (number of valid elements).
1450    pub fn count(&self) -> usize {
1451        self.count
1452    }
1453}
1454/// A versioned record that stores a history of values.
1455#[allow(dead_code)]
1456pub struct VersionedRecord<T: Clone> {
1457    history: Vec<T>,
1458}
1459#[allow(dead_code)]
1460impl<T: Clone> VersionedRecord<T> {
1461    /// Creates a new record with an initial value.
1462    pub fn new(initial: T) -> Self {
1463        Self {
1464            history: vec![initial],
1465        }
1466    }
1467    /// Updates the record with a new version.
1468    pub fn update(&mut self, val: T) {
1469        self.history.push(val);
1470    }
1471    /// Returns the current (latest) value.
1472    pub fn current(&self) -> &T {
1473        self.history
1474            .last()
1475            .expect("VersionedRecord history is always non-empty after construction")
1476    }
1477    /// Returns the value at version `n` (0-indexed), or `None`.
1478    pub fn at_version(&self, n: usize) -> Option<&T> {
1479        self.history.get(n)
1480    }
1481    /// Returns the version number of the current value.
1482    pub fn version(&self) -> usize {
1483        self.history.len() - 1
1484    }
1485    /// Returns `true` if more than one version exists.
1486    pub fn has_history(&self) -> bool {
1487        self.history.len() > 1
1488    }
1489}
1490/// A mutable reference stack for tracking the current "focus" in a tree traversal.
1491#[allow(dead_code)]
1492pub struct FocusStack<T> {
1493    items: Vec<T>,
1494}
1495#[allow(dead_code)]
1496impl<T> FocusStack<T> {
1497    /// Creates an empty focus stack.
1498    pub fn new() -> Self {
1499        Self { items: Vec::new() }
1500    }
1501    /// Focuses on `item`.
1502    pub fn focus(&mut self, item: T) {
1503        self.items.push(item);
1504    }
1505    /// Blurs (pops) the current focus.
1506    pub fn blur(&mut self) -> Option<T> {
1507        self.items.pop()
1508    }
1509    /// Returns the current focus, or `None`.
1510    pub fn current(&self) -> Option<&T> {
1511        self.items.last()
1512    }
1513    /// Returns the focus depth.
1514    pub fn depth(&self) -> usize {
1515        self.items.len()
1516    }
1517    /// Returns `true` if there is no current focus.
1518    pub fn is_empty(&self) -> bool {
1519        self.items.is_empty()
1520    }
1521}
1522/// A trie-based prefix counter.
1523#[allow(dead_code)]
1524pub struct PrefixCounter {
1525    children: std::collections::HashMap<char, PrefixCounter>,
1526    count: usize,
1527}
1528#[allow(dead_code)]
1529impl PrefixCounter {
1530    /// Creates an empty prefix counter.
1531    pub fn new() -> Self {
1532        Self {
1533            children: std::collections::HashMap::new(),
1534            count: 0,
1535        }
1536    }
1537    /// Records a string.
1538    pub fn record(&mut self, s: &str) {
1539        self.count += 1;
1540        let mut node = self;
1541        for c in s.chars() {
1542            node = node.children.entry(c).or_default();
1543            node.count += 1;
1544        }
1545    }
1546    /// Returns how many strings have been recorded that start with `prefix`.
1547    pub fn count_with_prefix(&self, prefix: &str) -> usize {
1548        let mut node = self;
1549        for c in prefix.chars() {
1550            match node.children.get(&c) {
1551                Some(n) => node = n,
1552                None => return 0,
1553            }
1554        }
1555        node.count
1556    }
1557}