Skip to main content

oxilean_kernel/reduce/
types.rs

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