Skip to main content

oxilean_kernel/subst/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use crate::Node;
7use crate::{Expr, FVarId};
8use std::collections::HashMap;
9use std::rc::Rc;
10
11/// A pair of `StatSummary` values tracking before/after a transformation.
12#[allow(dead_code)]
13pub struct TransformStat {
14    before: StatSummary,
15    after: StatSummary,
16}
17#[allow(dead_code)]
18impl TransformStat {
19    /// Creates a new transform stat recorder.
20    pub fn new() -> Self {
21        Self {
22            before: StatSummary::new(),
23            after: StatSummary::new(),
24        }
25    }
26    /// Records a before value.
27    pub fn record_before(&mut self, v: f64) {
28        self.before.record(v);
29    }
30    /// Records an after value.
31    pub fn record_after(&mut self, v: f64) {
32        self.after.record(v);
33    }
34    /// Returns the mean reduction ratio (after/before).
35    pub fn mean_ratio(&self) -> Option<f64> {
36        let b = self.before.mean()?;
37        let a = self.after.mean()?;
38        if b.abs() < f64::EPSILON {
39            return None;
40        }
41        Some(a / b)
42    }
43}
44/// A mutable reference stack for tracking the current "focus" in a tree traversal.
45#[allow(dead_code)]
46pub struct FocusStack<T> {
47    items: Vec<T>,
48}
49#[allow(dead_code)]
50impl<T> FocusStack<T> {
51    /// Creates an empty focus stack.
52    pub fn new() -> Self {
53        Self { items: Vec::new() }
54    }
55    /// Focuses on `item`.
56    pub fn focus(&mut self, item: T) {
57        self.items.push(item);
58    }
59    /// Blurs (pops) the current focus.
60    pub fn blur(&mut self) -> Option<T> {
61        self.items.pop()
62    }
63    /// Returns the current focus, or `None`.
64    pub fn current(&self) -> Option<&T> {
65        self.items.last()
66    }
67    /// Returns the focus depth.
68    pub fn depth(&self) -> usize {
69        self.items.len()
70    }
71    /// Returns `true` if there is no current focus.
72    pub fn is_empty(&self) -> bool {
73        self.items.is_empty()
74    }
75}
76/// A reusable scratch buffer for path computations.
77#[allow(dead_code)]
78pub struct PathBuf {
79    components: Vec<String>,
80}
81#[allow(dead_code)]
82impl PathBuf {
83    /// Creates a new empty path buffer.
84    pub fn new() -> Self {
85        Self {
86            components: Vec::new(),
87        }
88    }
89    /// Pushes a component.
90    pub fn push(&mut self, comp: impl Into<String>) {
91        self.components.push(comp.into());
92    }
93    /// Pops the last component.
94    pub fn pop(&mut self) {
95        self.components.pop();
96    }
97    /// Returns the current path as a `/`-separated string.
98    pub fn as_str(&self) -> String {
99        self.components.join("/")
100    }
101    /// Returns the depth of the path.
102    pub fn depth(&self) -> usize {
103        self.components.len()
104    }
105    /// Clears the path.
106    pub fn clear(&mut self) {
107        self.components.clear();
108    }
109}
110/// A token bucket rate limiter.
111#[allow(dead_code)]
112pub struct TokenBucket {
113    capacity: u64,
114    tokens: u64,
115    refill_per_ms: u64,
116    last_refill: crate::wall_clock::Instant,
117}
118#[allow(dead_code)]
119impl TokenBucket {
120    /// Creates a new token bucket.
121    pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
122        Self {
123            capacity,
124            tokens: capacity,
125            refill_per_ms,
126            last_refill: crate::wall_clock::Instant::now(),
127        }
128    }
129    /// Attempts to consume `n` tokens.  Returns `true` on success.
130    pub fn try_consume(&mut self, n: u64) -> bool {
131        self.refill();
132        if self.tokens >= n {
133            self.tokens -= n;
134            true
135        } else {
136            false
137        }
138    }
139    fn refill(&mut self) {
140        let now = crate::wall_clock::Instant::now();
141        let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
142        if elapsed_ms > 0 {
143            let new_tokens = elapsed_ms * self.refill_per_ms;
144            self.tokens = (self.tokens + new_tokens).min(self.capacity);
145            self.last_refill = now;
146        }
147    }
148    /// Returns the number of currently available tokens.
149    pub fn available(&self) -> u64 {
150        self.tokens
151    }
152    /// Returns the bucket capacity.
153    pub fn capacity(&self) -> u64 {
154        self.capacity
155    }
156}
157/// A type-erased function pointer with arity tracking.
158#[allow(dead_code)]
159pub struct RawFnPtr {
160    /// The raw function pointer (stored as usize for type erasure).
161    ptr: usize,
162    arity: usize,
163    name: String,
164}
165#[allow(dead_code)]
166impl RawFnPtr {
167    /// Creates a new raw function pointer descriptor.
168    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
169        Self {
170            ptr,
171            arity,
172            name: name.into(),
173        }
174    }
175    /// Returns the arity.
176    pub fn arity(&self) -> usize {
177        self.arity
178    }
179    /// Returns the name.
180    pub fn name(&self) -> &str {
181        &self.name
182    }
183    /// Returns the raw pointer value.
184    pub fn raw(&self) -> usize {
185        self.ptr
186    }
187}
188/// A tagged union for representing a simple two-case discriminated union.
189#[allow(dead_code)]
190pub enum Either2<A, B> {
191    /// The first alternative.
192    First(A),
193    /// The second alternative.
194    Second(B),
195}
196#[allow(dead_code)]
197impl<A, B> Either2<A, B> {
198    /// Returns `true` if this is the first alternative.
199    pub fn is_first(&self) -> bool {
200        matches!(self, Either2::First(_))
201    }
202    /// Returns `true` if this is the second alternative.
203    pub fn is_second(&self) -> bool {
204        matches!(self, Either2::Second(_))
205    }
206    /// Returns the first value if present.
207    pub fn first(self) -> Option<A> {
208        match self {
209            Either2::First(a) => Some(a),
210            _ => None,
211        }
212    }
213    /// Returns the second value if present.
214    pub fn second(self) -> Option<B> {
215        match self {
216            Either2::Second(b) => Some(b),
217            _ => None,
218        }
219    }
220    /// Maps over the first alternative.
221    pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
222        match self {
223            Either2::First(a) => Either2::First(f(a)),
224            Either2::Second(b) => Either2::Second(b),
225        }
226    }
227}
228/// A fixed-size sliding window that computes a running sum.
229#[allow(dead_code)]
230pub struct SlidingSum {
231    window: Vec<f64>,
232    capacity: usize,
233    pos: usize,
234    sum: f64,
235    count: usize,
236}
237#[allow(dead_code)]
238impl SlidingSum {
239    /// Creates a sliding sum with the given window size.
240    pub fn new(capacity: usize) -> Self {
241        Self {
242            window: vec![0.0; capacity],
243            capacity,
244            pos: 0,
245            sum: 0.0,
246            count: 0,
247        }
248    }
249    /// Adds a value to the window, removing the oldest if full.
250    pub fn push(&mut self, val: f64) {
251        let oldest = self.window[self.pos];
252        self.sum -= oldest;
253        self.sum += val;
254        self.window[self.pos] = val;
255        self.pos = (self.pos + 1) % self.capacity;
256        if self.count < self.capacity {
257            self.count += 1;
258        }
259    }
260    /// Returns the current window sum.
261    pub fn sum(&self) -> f64 {
262        self.sum
263    }
264    /// Returns the window mean, or `None` if empty.
265    pub fn mean(&self) -> Option<f64> {
266        if self.count == 0 {
267            None
268        } else {
269            Some(self.sum / self.count as f64)
270        }
271    }
272    /// Returns the current window size (number of valid elements).
273    pub fn count(&self) -> usize {
274        self.count
275    }
276}
277/// A write-once cell.
278#[allow(dead_code)]
279pub struct WriteOnce<T> {
280    value: std::cell::Cell<Option<T>>,
281}
282#[allow(dead_code)]
283impl<T: Copy> WriteOnce<T> {
284    /// Creates an empty write-once cell.
285    pub fn new() -> Self {
286        Self {
287            value: std::cell::Cell::new(None),
288        }
289    }
290    /// Writes a value.  Returns `false` if already written.
291    pub fn write(&self, val: T) -> bool {
292        if self.value.get().is_some() {
293            return false;
294        }
295        self.value.set(Some(val));
296        true
297    }
298    /// Returns the value if written.
299    pub fn read(&self) -> Option<T> {
300        self.value.get()
301    }
302    /// Returns `true` if the value has been written.
303    pub fn is_written(&self) -> bool {
304        self.value.get().is_some()
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 pool of reusable string buffers.
363#[allow(dead_code)]
364pub struct StringPool {
365    free: Vec<String>,
366}
367#[allow(dead_code)]
368impl StringPool {
369    /// Creates a new empty string pool.
370    pub fn new() -> Self {
371        Self { free: Vec::new() }
372    }
373    /// Takes a string from the pool (may be empty).
374    pub fn take(&mut self) -> String {
375        self.free.pop().unwrap_or_default()
376    }
377    /// Returns a string to the pool.
378    pub fn give(&mut self, mut s: String) {
379        s.clear();
380        self.free.push(s);
381    }
382    /// Returns the number of free strings in the pool.
383    pub fn free_count(&self) -> usize {
384        self.free.len()
385    }
386}
387/// A non-empty list (at least one element guaranteed).
388#[allow(dead_code)]
389pub struct NonEmptyVec<T> {
390    head: T,
391    tail: Vec<T>,
392}
393#[allow(dead_code)]
394impl<T> NonEmptyVec<T> {
395    /// Creates a non-empty vec with a single element.
396    pub fn singleton(val: T) -> Self {
397        Self {
398            head: val,
399            tail: Vec::new(),
400        }
401    }
402    /// Pushes an element.
403    pub fn push(&mut self, val: T) {
404        self.tail.push(val);
405    }
406    /// Returns a reference to the first element.
407    pub fn first(&self) -> &T {
408        &self.head
409    }
410    /// Returns a reference to the last element.
411    pub fn last(&self) -> &T {
412        self.tail.last().unwrap_or(&self.head)
413    }
414    /// Returns the number of elements.
415    pub fn len(&self) -> usize {
416        1 + self.tail.len()
417    }
418    /// Returns whether the collection is empty.
419    pub fn is_empty(&self) -> bool {
420        self.len() == 0
421    }
422    /// Returns all elements as a Vec.
423    pub fn to_vec(&self) -> Vec<&T> {
424        let mut v = vec![&self.head];
425        v.extend(self.tail.iter());
426        v
427    }
428}
429/// Profiling counters for substitution operations.
430#[derive(Debug, Clone, Default, PartialEq, Eq)]
431pub struct SubstStats {
432    /// Number of successful `BVar` hits.
433    pub bvar_hits: usize,
434    /// Number of `BVar` misses.
435    pub bvar_misses: usize,
436    /// Number of `FVar` substitutions performed.
437    pub fvar_hits: usize,
438    /// Number of nodes visited in total.
439    pub nodes_visited: usize,
440}
441impl SubstStats {
442    /// Merge another stats record into `self`.
443    pub fn merge(&mut self, other: &SubstStats) {
444        self.bvar_hits += other.bvar_hits;
445        self.bvar_misses += other.bvar_misses;
446        self.fvar_hits += other.fvar_hits;
447        self.nodes_visited += other.nodes_visited;
448    }
449    /// Total substitutions performed.
450    pub fn total_substs(&self) -> usize {
451        self.bvar_hits + self.fvar_hits
452    }
453}
454/// A hierarchical configuration tree.
455#[allow(dead_code)]
456pub struct ConfigNode {
457    key: String,
458    value: Option<String>,
459    children: Vec<ConfigNode>,
460}
461#[allow(dead_code)]
462impl ConfigNode {
463    /// Creates a leaf config node with a value.
464    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
465        Self {
466            key: key.into(),
467            value: Some(value.into()),
468            children: Vec::new(),
469        }
470    }
471    /// Creates a section node with children.
472    pub fn section(key: impl Into<String>) -> Self {
473        Self {
474            key: key.into(),
475            value: None,
476            children: Vec::new(),
477        }
478    }
479    /// Adds a child node.
480    pub fn add_child(&mut self, child: ConfigNode) {
481        self.children.push(child);
482    }
483    /// Returns the key.
484    pub fn key(&self) -> &str {
485        &self.key
486    }
487    /// Returns the value, or `None` for section nodes.
488    pub fn value(&self) -> Option<&str> {
489        self.value.as_deref()
490    }
491    /// Returns the number of children.
492    pub fn num_children(&self) -> usize {
493        self.children.len()
494    }
495    /// Looks up a dot-separated path.
496    pub fn lookup(&self, path: &str) -> Option<&str> {
497        let mut parts = path.splitn(2, '.');
498        let head = parts.next()?;
499        let tail = parts.next();
500        if head != self.key {
501            return None;
502        }
503        match tail {
504            None => self.value.as_deref(),
505            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
506        }
507    }
508    fn lookup_relative(&self, path: &str) -> Option<&str> {
509        let mut parts = path.splitn(2, '.');
510        let head = parts.next()?;
511        let tail = parts.next();
512        if head != self.key {
513            return None;
514        }
515        match tail {
516            None => self.value.as_deref(),
517            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
518        }
519    }
520}
521/// A generic counter that tracks min/max/sum for statistical summaries.
522#[allow(dead_code)]
523pub struct StatSummary {
524    count: u64,
525    sum: f64,
526    min: f64,
527    max: f64,
528}
529#[allow(dead_code)]
530impl StatSummary {
531    /// Creates an empty summary.
532    pub fn new() -> Self {
533        Self {
534            count: 0,
535            sum: 0.0,
536            min: f64::INFINITY,
537            max: f64::NEG_INFINITY,
538        }
539    }
540    /// Records a sample.
541    pub fn record(&mut self, val: f64) {
542        self.count += 1;
543        self.sum += val;
544        if val < self.min {
545            self.min = val;
546        }
547        if val > self.max {
548            self.max = val;
549        }
550    }
551    /// Returns the mean, or `None` if no samples.
552    pub fn mean(&self) -> Option<f64> {
553        if self.count == 0 {
554            None
555        } else {
556            Some(self.sum / self.count as f64)
557        }
558    }
559    /// Returns the minimum, or `None` if no samples.
560    pub fn min(&self) -> Option<f64> {
561        if self.count == 0 {
562            None
563        } else {
564            Some(self.min)
565        }
566    }
567    /// Returns the maximum, or `None` if no samples.
568    pub fn max(&self) -> Option<f64> {
569        if self.count == 0 {
570            None
571        } else {
572            Some(self.max)
573        }
574    }
575    /// Returns the count of recorded samples.
576    pub fn count(&self) -> u64 {
577        self.count
578    }
579}
580/// A versioned record that stores a history of values.
581#[allow(dead_code)]
582pub struct VersionedRecord<T: Clone> {
583    /// Every earlier value, oldest first: version `n` is `earlier[n]`.
584    earlier: Vec<T>,
585    /// The latest value (version `earlier.len()`), declared last so it drops last.
586    current: T,
587}
588#[allow(dead_code)]
589impl<T: Clone> VersionedRecord<T> {
590    /// Creates a new record with an initial value.
591    pub fn new(initial: T) -> Self {
592        Self {
593            earlier: Vec::new(),
594            current: initial,
595        }
596    }
597    /// Updates the record with a new version.
598    pub fn update(&mut self, val: T) {
599        let previous = std::mem::replace(&mut self.current, val);
600        self.earlier.push(previous);
601    }
602    /// Returns the current (latest) value.
603    pub fn current(&self) -> &T {
604        &self.current
605    }
606    /// Returns the value at version `n` (0-indexed), or `None`.
607    pub fn at_version(&self, n: usize) -> Option<&T> {
608        match self.earlier.get(n) {
609            Some(value) => Some(value),
610            None if n == self.earlier.len() => Some(&self.current),
611            None => None,
612        }
613    }
614    /// Returns the version number of the current value.
615    pub fn version(&self) -> usize {
616        self.earlier.len()
617    }
618    /// Returns `true` if more than one version exists.
619    pub fn has_history(&self) -> bool {
620        !self.earlier.is_empty()
621    }
622}
623/// A dependency closure builder (transitive closure via BFS).
624#[allow(dead_code)]
625pub struct TransitiveClosure {
626    adj: Vec<Vec<usize>>,
627    n: usize,
628}
629#[allow(dead_code)]
630impl TransitiveClosure {
631    /// Creates a transitive closure builder for `n` nodes.
632    pub fn new(n: usize) -> Self {
633        Self {
634            adj: vec![Vec::new(); n],
635            n,
636        }
637    }
638    /// Adds a direct edge.
639    pub fn add_edge(&mut self, from: usize, to: usize) {
640        if from < self.n {
641            self.adj[from].push(to);
642        }
643    }
644    /// Computes all nodes reachable from `start` (including `start`).
645    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
646        let mut visited = vec![false; self.n];
647        let mut queue = std::collections::VecDeque::new();
648        queue.push_back(start);
649        while let Some(node) = queue.pop_front() {
650            if node >= self.n || visited[node] {
651                continue;
652            }
653            visited[node] = true;
654            for &next in &self.adj[node] {
655                queue.push_back(next);
656            }
657        }
658        (0..self.n).filter(|&i| visited[i]).collect()
659    }
660    /// Returns `true` if `from` can transitively reach `to`.
661    pub fn can_reach(&self, from: usize, to: usize) -> bool {
662        self.reachable_from(from).contains(&to)
663    }
664}
665/// Represents a rewrite rule `lhs → rhs`.
666#[allow(dead_code)]
667#[allow(missing_docs)]
668pub struct RewriteRule {
669    /// The name of the rule.
670    pub name: String,
671    /// A string representation of the LHS pattern.
672    pub lhs: String,
673    /// A string representation of the RHS.
674    pub rhs: String,
675    /// Whether this is a conditional rule (has side conditions).
676    pub conditional: bool,
677}
678#[allow(dead_code)]
679impl RewriteRule {
680    /// Creates an unconditional rewrite rule.
681    pub fn unconditional(
682        name: impl Into<String>,
683        lhs: impl Into<String>,
684        rhs: impl Into<String>,
685    ) -> Self {
686        Self {
687            name: name.into(),
688            lhs: lhs.into(),
689            rhs: rhs.into(),
690            conditional: false,
691        }
692    }
693    /// Creates a conditional rewrite rule.
694    pub fn conditional(
695        name: impl Into<String>,
696        lhs: impl Into<String>,
697        rhs: impl Into<String>,
698    ) -> Self {
699        Self {
700            name: name.into(),
701            lhs: lhs.into(),
702            rhs: rhs.into(),
703            conditional: true,
704        }
705    }
706    /// Returns a textual representation.
707    pub fn display(&self) -> String {
708        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
709    }
710}
711/// A window iterator that yields overlapping windows of size `n`.
712#[allow(dead_code)]
713pub struct WindowIterator<'a, T> {
714    pub(super) data: &'a [T],
715    pub(super) pos: usize,
716    pub(super) window: usize,
717}
718#[allow(dead_code)]
719impl<'a, T> WindowIterator<'a, T> {
720    /// Creates a new window iterator.
721    pub fn new(data: &'a [T], window: usize) -> Self {
722        Self {
723            data,
724            pos: 0,
725            window,
726        }
727    }
728}
729/// A flat list of substitution pairs `(from, to)`.
730#[allow(dead_code)]
731pub struct FlatSubstitution {
732    pairs: Vec<(String, String)>,
733}
734#[allow(dead_code)]
735impl FlatSubstitution {
736    /// Creates an empty substitution.
737    pub fn new() -> Self {
738        Self { pairs: Vec::new() }
739    }
740    /// Adds a pair.
741    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
742        self.pairs.push((from.into(), to.into()));
743    }
744    /// Applies all substitutions to `s` (leftmost-first order).
745    pub fn apply(&self, s: &str) -> String {
746        let mut result = s.to_string();
747        for (from, to) in &self.pairs {
748            result = result.replace(from.as_str(), to.as_str());
749        }
750        result
751    }
752    /// Returns the number of pairs.
753    pub fn len(&self) -> usize {
754        self.pairs.len()
755    }
756    /// Returns `true` if empty.
757    pub fn is_empty(&self) -> bool {
758        self.pairs.is_empty()
759    }
760}
761/// A sparse vector: stores only non-default elements.
762#[allow(dead_code)]
763pub struct SparseVec<T: Default + Clone + PartialEq> {
764    entries: std::collections::HashMap<usize, T>,
765    default_: T,
766    logical_len: usize,
767}
768#[allow(dead_code)]
769impl<T: Default + Clone + PartialEq> SparseVec<T> {
770    /// Creates a new sparse vector with logical length `len`.
771    pub fn new(len: usize) -> Self {
772        Self {
773            entries: std::collections::HashMap::new(),
774            default_: T::default(),
775            logical_len: len,
776        }
777    }
778    /// Sets element at `idx`.
779    pub fn set(&mut self, idx: usize, val: T) {
780        if val == self.default_ {
781            self.entries.remove(&idx);
782        } else {
783            self.entries.insert(idx, val);
784        }
785    }
786    /// Gets element at `idx`.
787    pub fn get(&self, idx: usize) -> &T {
788        self.entries.get(&idx).unwrap_or(&self.default_)
789    }
790    /// Returns the logical length.
791    pub fn len(&self) -> usize {
792        self.logical_len
793    }
794    /// Returns whether the collection is empty.
795    pub fn is_empty(&self) -> bool {
796        self.len() == 0
797    }
798    /// Returns the number of non-default elements.
799    pub fn nnz(&self) -> usize {
800        self.entries.len()
801    }
802}
803/// A simple key-value store backed by a sorted Vec for small maps.
804#[allow(dead_code)]
805pub struct SmallMap<K: Ord + Clone, V: Clone> {
806    entries: Vec<(K, V)>,
807}
808#[allow(dead_code)]
809impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
810    /// Creates a new empty small map.
811    pub fn new() -> Self {
812        Self {
813            entries: Vec::new(),
814        }
815    }
816    /// Inserts or replaces the value for `key`.
817    pub fn insert(&mut self, key: K, val: V) {
818        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
819            Ok(i) => self.entries[i].1 = val,
820            Err(i) => self.entries.insert(i, (key, val)),
821        }
822    }
823    /// Returns the value for `key`, or `None`.
824    pub fn get(&self, key: &K) -> Option<&V> {
825        self.entries
826            .binary_search_by_key(&key, |(k, _)| k)
827            .ok()
828            .map(|i| &self.entries[i].1)
829    }
830    /// Returns the number of entries.
831    pub fn len(&self) -> usize {
832        self.entries.len()
833    }
834    /// Returns `true` if empty.
835    pub fn is_empty(&self) -> bool {
836        self.entries.is_empty()
837    }
838    /// Returns all keys.
839    pub fn keys(&self) -> Vec<&K> {
840        self.entries.iter().map(|(k, _)| k).collect()
841    }
842    /// Returns all values.
843    pub fn values(&self) -> Vec<&V> {
844        self.entries.iter().map(|(_, v)| v).collect()
845    }
846}
847/// A set of rewrite rules.
848#[allow(dead_code)]
849pub struct RewriteRuleSet {
850    rules: Vec<RewriteRule>,
851}
852#[allow(dead_code)]
853impl RewriteRuleSet {
854    /// Creates an empty rule set.
855    pub fn new() -> Self {
856        Self { rules: Vec::new() }
857    }
858    /// Adds a rule.
859    pub fn add(&mut self, rule: RewriteRule) {
860        self.rules.push(rule);
861    }
862    /// Returns the number of rules.
863    pub fn len(&self) -> usize {
864        self.rules.len()
865    }
866    /// Returns `true` if the set is empty.
867    pub fn is_empty(&self) -> bool {
868        self.rules.is_empty()
869    }
870    /// Returns all conditional rules.
871    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
872        self.rules.iter().filter(|r| r.conditional).collect()
873    }
874    /// Returns all unconditional rules.
875    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
876        self.rules.iter().filter(|r| !r.conditional).collect()
877    }
878    /// Looks up a rule by name.
879    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
880        self.rules.iter().find(|r| r.name == name)
881    }
882}
883/// A label set for a graph node.
884#[allow(dead_code)]
885pub struct LabelSet {
886    labels: Vec<String>,
887}
888#[allow(dead_code)]
889impl LabelSet {
890    /// Creates a new empty label set.
891    pub fn new() -> Self {
892        Self { labels: Vec::new() }
893    }
894    /// Adds a label (deduplicates).
895    pub fn add(&mut self, label: impl Into<String>) {
896        let s = label.into();
897        if !self.labels.contains(&s) {
898            self.labels.push(s);
899        }
900    }
901    /// Returns `true` if `label` is present.
902    pub fn has(&self, label: &str) -> bool {
903        self.labels.iter().any(|l| l == label)
904    }
905    /// Returns the count of labels.
906    pub fn count(&self) -> usize {
907        self.labels.len()
908    }
909    /// Returns all labels.
910    pub fn all(&self) -> &[String] {
911        &self.labels
912    }
913}
914/// A simple decision tree node for rule dispatching.
915#[allow(dead_code)]
916#[allow(missing_docs)]
917pub enum DecisionNode {
918    /// A leaf with an action string.
919    Leaf(String),
920    /// An interior node: check `key` equals `val` → `yes_branch`, else `no_branch`.
921    Branch {
922        key: String,
923        val: String,
924        yes_branch: Box<DecisionNode>,
925        no_branch: Box<DecisionNode>,
926    },
927}
928#[allow(dead_code)]
929impl DecisionNode {
930    /// Evaluates the decision tree with the given context.
931    pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
932        match self {
933            DecisionNode::Leaf(action) => action.as_str(),
934            DecisionNode::Branch {
935                key,
936                val,
937                yes_branch,
938                no_branch,
939            } => {
940                let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
941                if actual == val.as_str() {
942                    yes_branch.evaluate(ctx)
943                } else {
944                    no_branch.evaluate(ctx)
945                }
946            }
947        }
948    }
949    /// Returns the depth of the decision tree.
950    pub fn depth(&self) -> usize {
951        match self {
952            DecisionNode::Leaf(_) => 0,
953            DecisionNode::Branch {
954                yes_branch,
955                no_branch,
956                ..
957            } => 1 + yes_branch.depth().max(no_branch.depth()),
958        }
959    }
960}
961/// A simple directed acyclic graph.
962#[allow(dead_code)]
963pub struct SimpleDag {
964    /// `edges[i]` is the list of direct successors of node `i`.
965    edges: Vec<Vec<usize>>,
966}
967#[allow(dead_code)]
968impl SimpleDag {
969    /// Creates a DAG with `n` nodes and no edges.
970    pub fn new(n: usize) -> Self {
971        Self {
972            edges: vec![Vec::new(); n],
973        }
974    }
975    /// Adds an edge from `from` to `to`.
976    pub fn add_edge(&mut self, from: usize, to: usize) {
977        if from < self.edges.len() {
978            self.edges[from].push(to);
979        }
980    }
981    /// Returns the successors of `node`.
982    pub fn successors(&self, node: usize) -> &[usize] {
983        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
984    }
985    /// Returns `true` if `from` can reach `to` via DFS.
986    pub fn can_reach(&self, from: usize, to: usize) -> bool {
987        let mut visited = vec![false; self.edges.len()];
988        self.dfs(from, to, &mut visited)
989    }
990    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
991        if cur == target {
992            return true;
993        }
994        if cur >= visited.len() || visited[cur] {
995            return false;
996        }
997        visited[cur] = true;
998        for &next in self.successors(cur) {
999            if self.dfs(next, target, visited) {
1000                return true;
1001            }
1002        }
1003        false
1004    }
1005    /// Returns the topological order of nodes, or `None` if a cycle is detected.
1006    pub fn topological_sort(&self) -> Option<Vec<usize>> {
1007        let n = self.edges.len();
1008        let mut in_degree = vec![0usize; n];
1009        for succs in &self.edges {
1010            for &s in succs {
1011                if s < n {
1012                    in_degree[s] += 1;
1013                }
1014            }
1015        }
1016        let mut queue: std::collections::VecDeque<usize> =
1017            (0..n).filter(|&i| in_degree[i] == 0).collect();
1018        let mut order = Vec::new();
1019        while let Some(node) = queue.pop_front() {
1020            order.push(node);
1021            for &s in self.successors(node) {
1022                if s < n {
1023                    in_degree[s] -= 1;
1024                    if in_degree[s] == 0 {
1025                        queue.push_back(s);
1026                    }
1027                }
1028            }
1029        }
1030        if order.len() == n {
1031            Some(order)
1032        } else {
1033            None
1034        }
1035    }
1036    /// Returns the number of nodes.
1037    pub fn num_nodes(&self) -> usize {
1038        self.edges.len()
1039    }
1040}
1041/// A substitution mapping: a list of (FVarId, Expr) pairs.
1042///
1043/// Used to represent simultaneous substitution of free variables.
1044#[allow(dead_code)]
1045#[derive(Debug, Clone, Default)]
1046pub struct Substitution {
1047    pairs: Vec<(FVarId, Expr)>,
1048}
1049impl Substitution {
1050    /// Create an empty substitution.
1051    #[allow(dead_code)]
1052    pub fn new() -> Self {
1053        Self { pairs: Vec::new() }
1054    }
1055    /// Add a mapping `fvar → expr` to the substitution.
1056    #[allow(dead_code)]
1057    pub fn insert(&mut self, fvar: FVarId, expr: Expr) {
1058        if let Some(existing) = self.pairs.iter_mut().find(|(f, _)| *f == fvar) {
1059            existing.1 = expr;
1060        } else {
1061            self.pairs.push((fvar, expr));
1062        }
1063    }
1064    /// Look up the image of a free variable.
1065    #[allow(dead_code)]
1066    pub fn get(&self, fvar: FVarId) -> Option<&Expr> {
1067        self.pairs.iter().find(|(f, _)| *f == fvar).map(|(_, e)| e)
1068    }
1069    /// Number of mappings.
1070    #[allow(dead_code)]
1071    pub fn len(&self) -> usize {
1072        self.pairs.len()
1073    }
1074    /// Check if empty.
1075    #[allow(dead_code)]
1076    pub fn is_empty(&self) -> bool {
1077        self.pairs.is_empty()
1078    }
1079    /// Apply this substitution to an expression.
1080    ///
1081    /// Replaces each `FVar(id)` found in `expr` with the corresponding image.
1082    #[allow(dead_code)]
1083    pub fn apply(&self, expr: &Expr) -> Expr {
1084        if self.is_empty() {
1085            return expr.clone();
1086        }
1087        self.apply_inner(expr)
1088    }
1089    fn apply_inner(&self, expr: &Expr) -> Expr {
1090        match expr {
1091            Expr::FVar(id) => {
1092                if let Some(replacement) = self.get(*id) {
1093                    replacement.clone()
1094                } else {
1095                    expr.clone()
1096                }
1097            }
1098            Expr::App(f, a) => Expr::App(
1099                Node::new(self.apply_inner(f)),
1100                Node::new(self.apply_inner(a)),
1101            ),
1102            Expr::Lam(bi, name, ty, body) => Expr::Lam(
1103                *bi,
1104                name.clone(),
1105                Node::new(self.apply_inner(ty)),
1106                Node::new(self.apply_inner(body)),
1107            ),
1108            Expr::Pi(bi, name, ty, body) => Expr::Pi(
1109                *bi,
1110                name.clone(),
1111                Node::new(self.apply_inner(ty)),
1112                Node::new(self.apply_inner(body)),
1113            ),
1114            Expr::Let(name, ty, val, body) => Expr::Let(
1115                name.clone(),
1116                Node::new(self.apply_inner(ty)),
1117                Node::new(self.apply_inner(val)),
1118                Node::new(self.apply_inner(body)),
1119            ),
1120            Expr::Proj(name, idx, inner) => {
1121                Expr::Proj(name.clone(), *idx, Node::new(self.apply_inner(inner)))
1122            }
1123            _ => expr.clone(),
1124        }
1125    }
1126    /// Compose `self` with `other`: apply `other` to all images in `self`,
1127    /// then merge. Maps in `self` override `other` for the same variable.
1128    #[allow(dead_code)]
1129    pub fn compose(&self, other: &Substitution) -> Substitution {
1130        let mut result = Substitution::new();
1131        for (fvar, expr) in &self.pairs {
1132            result.insert(*fvar, other.apply(expr));
1133        }
1134        for (fvar, expr) in &other.pairs {
1135            if self.get(*fvar).is_none() {
1136                result.insert(*fvar, expr.clone());
1137            }
1138        }
1139        result
1140    }
1141    /// Remove a mapping.
1142    #[allow(dead_code)]
1143    pub fn remove(&mut self, fvar: FVarId) {
1144        self.pairs.retain(|(f, _)| *f != fvar);
1145    }
1146    /// Restrict the substitution to only those fvars in `keep`.
1147    #[allow(dead_code)]
1148    pub fn restrict(&self, keep: &[FVarId]) -> Substitution {
1149        let pairs = self
1150            .pairs
1151            .iter()
1152            .filter(|(f, _)| keep.contains(f))
1153            .cloned()
1154            .collect();
1155        Substitution { pairs }
1156    }
1157}
1158/// A counter that can measure elapsed time between snapshots.
1159#[allow(dead_code)]
1160pub struct Stopwatch {
1161    start: crate::wall_clock::Instant,
1162    splits: Vec<f64>,
1163}
1164#[allow(dead_code)]
1165impl Stopwatch {
1166    /// Creates and starts a new stopwatch.
1167    pub fn start() -> Self {
1168        Self {
1169            start: crate::wall_clock::Instant::now(),
1170            splits: Vec::new(),
1171        }
1172    }
1173    /// Records a split time (elapsed since start).
1174    pub fn split(&mut self) {
1175        self.splits.push(self.elapsed_ms());
1176    }
1177    /// Returns total elapsed milliseconds since start.
1178    pub fn elapsed_ms(&self) -> f64 {
1179        self.start.elapsed().as_secs_f64() * 1000.0
1180    }
1181    /// Returns all recorded split times.
1182    pub fn splits(&self) -> &[f64] {
1183        &self.splits
1184    }
1185    /// Returns the number of splits.
1186    pub fn num_splits(&self) -> usize {
1187        self.splits.len()
1188    }
1189}