Skip to main content

oxilean_kernel/error/
types.rs

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