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