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