Skip to main content

oxilean_kernel/builtin/
types.rs

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