Skip to main content

oxilean_kernel/whnf/
types.rs

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