Skip to main content

oxilean_kernel/normalize/
types.rs

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