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: crate::wall_clock::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: crate::wall_clock::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: crate::wall_clock::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: crate::wall_clock::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 = crate::wall_clock::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    /// Replaces the top two values, `b` on top of `a`, with `op(a, b)` and
459    /// returns it.  Returns `None`, leaving the stack unchanged, if it holds
460    /// fewer than two values.
461    fn apply_binary(&mut self, op: impl FnOnce(i64, i64) -> i64) -> Option<i64> {
462        let (rest_len, a, b) = match self.stack[..] {
463            [ref rest @ .., a, b] => (rest.len(), a, b),
464            _ => return None,
465        };
466        let value = op(a, b);
467        self.stack.truncate(rest_len);
468        self.stack.push(value);
469        Some(value)
470    }
471    /// Adds the top two values and returns the sum, which replaces them.
472    /// Returns `None`, leaving the stack unchanged, if it holds fewer than
473    /// two values.
474    pub fn add(&mut self) -> Option<i64> {
475        self.apply_binary(|a, b| a + b)
476    }
477    /// Subtracts top from second and returns the difference, which replaces
478    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
479    /// than two values.
480    pub fn sub(&mut self) -> Option<i64> {
481        self.apply_binary(|a, b| a - b)
482    }
483    /// Multiplies the top two values and returns the product, which replaces
484    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
485    /// than two values.
486    pub fn mul(&mut self) -> Option<i64> {
487        self.apply_binary(|a, b| a * b)
488    }
489    /// Peeks the top value.
490    pub fn peek(&self) -> Option<i64> {
491        self.stack.last().copied()
492    }
493    /// Returns the stack depth.
494    pub fn depth(&self) -> usize {
495        self.stack.len()
496    }
497}
498/// A dependency closure builder (transitive closure via BFS).
499#[allow(dead_code)]
500pub struct TransitiveClosure {
501    adj: Vec<Vec<usize>>,
502    n: usize,
503}
504#[allow(dead_code)]
505impl TransitiveClosure {
506    /// Creates a transitive closure builder for `n` nodes.
507    pub fn new(n: usize) -> Self {
508        Self {
509            adj: vec![Vec::new(); n],
510            n,
511        }
512    }
513    /// Adds a direct edge.
514    pub fn add_edge(&mut self, from: usize, to: usize) {
515        if from < self.n {
516            self.adj[from].push(to);
517        }
518    }
519    /// Computes all nodes reachable from `start` (including `start`).
520    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
521        let mut visited = vec![false; self.n];
522        let mut queue = std::collections::VecDeque::new();
523        queue.push_back(start);
524        while let Some(node) = queue.pop_front() {
525            if node >= self.n || visited[node] {
526                continue;
527            }
528            visited[node] = true;
529            for &next in &self.adj[node] {
530                queue.push_back(next);
531            }
532        }
533        (0..self.n).filter(|&i| visited[i]).collect()
534    }
535    /// Returns `true` if `from` can transitively reach `to`.
536    pub fn can_reach(&self, from: usize, to: usize) -> bool {
537        self.reachable_from(from).contains(&to)
538    }
539}
540/// A tagged union for representing a simple two-case discriminated union.
541#[allow(dead_code)]
542pub enum Either2<A, B> {
543    /// The first alternative.
544    First(A),
545    /// The second alternative.
546    Second(B),
547}
548#[allow(dead_code)]
549impl<A, B> Either2<A, B> {
550    /// Returns `true` if this is the first alternative.
551    pub fn is_first(&self) -> bool {
552        matches!(self, Either2::First(_))
553    }
554    /// Returns `true` if this is the second alternative.
555    pub fn is_second(&self) -> bool {
556        matches!(self, Either2::Second(_))
557    }
558    /// Returns the first value if present.
559    pub fn first(self) -> Option<A> {
560        match self {
561            Either2::First(a) => Some(a),
562            _ => None,
563        }
564    }
565    /// Returns the second value if present.
566    pub fn second(self) -> Option<B> {
567        match self {
568            Either2::Second(b) => Some(b),
569            _ => None,
570        }
571    }
572    /// Maps over the first alternative.
573    pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
574        match self {
575            Either2::First(a) => Either2::First(f(a)),
576            Either2::Second(b) => Either2::Second(b),
577        }
578    }
579}
580/// A type-erased function pointer with arity tracking.
581#[allow(dead_code)]
582pub struct RawFnPtr {
583    /// The raw function pointer (stored as usize for type erasure).
584    ptr: usize,
585    arity: usize,
586    name: String,
587}
588#[allow(dead_code)]
589impl RawFnPtr {
590    /// Creates a new raw function pointer descriptor.
591    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
592        Self {
593            ptr,
594            arity,
595            name: name.into(),
596        }
597    }
598    /// Returns the arity.
599    pub fn arity(&self) -> usize {
600        self.arity
601    }
602    /// Returns the name.
603    pub fn name(&self) -> &str {
604        &self.name
605    }
606    /// Returns the raw pointer value.
607    pub fn raw(&self) -> usize {
608        self.ptr
609    }
610}
611/// A label set for a graph node.
612#[allow(dead_code)]
613pub struct LabelSet {
614    labels: Vec<String>,
615}
616#[allow(dead_code)]
617impl LabelSet {
618    /// Creates a new empty label set.
619    pub fn new() -> Self {
620        Self { labels: Vec::new() }
621    }
622    /// Adds a label (deduplicates).
623    pub fn add(&mut self, label: impl Into<String>) {
624        let s = label.into();
625        if !self.labels.contains(&s) {
626            self.labels.push(s);
627        }
628    }
629    /// Returns `true` if `label` is present.
630    pub fn has(&self, label: &str) -> bool {
631        self.labels.iter().any(|l| l == label)
632    }
633    /// Returns the count of labels.
634    pub fn count(&self) -> usize {
635        self.labels.len()
636    }
637    /// Returns all labels.
638    pub fn all(&self) -> &[String] {
639        &self.labels
640    }
641}
642/// A pool of reusable string buffers.
643#[allow(dead_code)]
644pub struct StringPool {
645    free: Vec<String>,
646}
647#[allow(dead_code)]
648impl StringPool {
649    /// Creates a new empty string pool.
650    pub fn new() -> Self {
651        Self { free: Vec::new() }
652    }
653    /// Takes a string from the pool (may be empty).
654    pub fn take(&mut self) -> String {
655        self.free.pop().unwrap_or_default()
656    }
657    /// Returns a string to the pool.
658    pub fn give(&mut self, mut s: String) {
659        s.clear();
660        self.free.push(s);
661    }
662    /// Returns the number of free strings in the pool.
663    pub fn free_count(&self) -> usize {
664        self.free.len()
665    }
666}
667/// A simple directed acyclic graph.
668#[allow(dead_code)]
669pub struct SimpleDag {
670    /// `edges[i]` is the list of direct successors of node `i`.
671    edges: Vec<Vec<usize>>,
672}
673#[allow(dead_code)]
674impl SimpleDag {
675    /// Creates a DAG with `n` nodes and no edges.
676    pub fn new(n: usize) -> Self {
677        Self {
678            edges: vec![Vec::new(); n],
679        }
680    }
681    /// Adds an edge from `from` to `to`.
682    pub fn add_edge(&mut self, from: usize, to: usize) {
683        if from < self.edges.len() {
684            self.edges[from].push(to);
685        }
686    }
687    /// Returns the successors of `node`.
688    pub fn successors(&self, node: usize) -> &[usize] {
689        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
690    }
691    /// Returns `true` if `from` can reach `to` via DFS.
692    pub fn can_reach(&self, from: usize, to: usize) -> bool {
693        let mut visited = vec![false; self.edges.len()];
694        self.dfs(from, to, &mut visited)
695    }
696    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
697        if cur == target {
698            return true;
699        }
700        if cur >= visited.len() || visited[cur] {
701            return false;
702        }
703        visited[cur] = true;
704        for &next in self.successors(cur) {
705            if self.dfs(next, target, visited) {
706                return true;
707            }
708        }
709        false
710    }
711    /// Returns the topological order of nodes, or `None` if a cycle is detected.
712    pub fn topological_sort(&self) -> Option<Vec<usize>> {
713        let n = self.edges.len();
714        let mut in_degree = vec![0usize; n];
715        for succs in &self.edges {
716            for &s in succs {
717                if s < n {
718                    in_degree[s] += 1;
719                }
720            }
721        }
722        let mut queue: std::collections::VecDeque<usize> =
723            (0..n).filter(|&i| in_degree[i] == 0).collect();
724        let mut order = Vec::new();
725        while let Some(node) = queue.pop_front() {
726            order.push(node);
727            for &s in self.successors(node) {
728                if s < n {
729                    in_degree[s] -= 1;
730                    if in_degree[s] == 0 {
731                        queue.push_back(s);
732                    }
733                }
734            }
735        }
736        if order.len() == n {
737            Some(order)
738        } else {
739            None
740        }
741    }
742    /// Returns the number of nodes.
743    pub fn num_nodes(&self) -> usize {
744        self.edges.len()
745    }
746}
747/// A set of rewrite rules.
748#[allow(dead_code)]
749pub struct RewriteRuleSet {
750    rules: Vec<RewriteRule>,
751}
752#[allow(dead_code)]
753impl RewriteRuleSet {
754    /// Creates an empty rule set.
755    pub fn new() -> Self {
756        Self { rules: Vec::new() }
757    }
758    /// Adds a rule.
759    pub fn add(&mut self, rule: RewriteRule) {
760        self.rules.push(rule);
761    }
762    /// Returns the number of rules.
763    pub fn len(&self) -> usize {
764        self.rules.len()
765    }
766    /// Returns `true` if the set is empty.
767    pub fn is_empty(&self) -> bool {
768        self.rules.is_empty()
769    }
770    /// Returns all conditional rules.
771    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
772        self.rules.iter().filter(|r| r.conditional).collect()
773    }
774    /// Returns all unconditional rules.
775    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
776        self.rules.iter().filter(|r| !r.conditional).collect()
777    }
778    /// Looks up a rule by name.
779    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
780        self.rules.iter().find(|r| r.name == name)
781    }
782}
783/// A strategy enum for choosing the normalization depth.
784#[derive(Clone, Copy, Debug, PartialEq, Eq)]
785pub enum NormStrategy {
786    /// No normalization (identity).
787    None,
788    /// Only WHNF (outermost).
789    Whnf,
790    /// Full normalization (under binders).
791    Full,
792    /// Bounded normalization (up to a depth limit).
793    Bounded(usize),
794}
795/// A flat list of substitution pairs `(from, to)`.
796#[allow(dead_code)]
797pub struct FlatSubstitution {
798    pairs: Vec<(String, String)>,
799}
800#[allow(dead_code)]
801impl FlatSubstitution {
802    /// Creates an empty substitution.
803    pub fn new() -> Self {
804        Self { pairs: Vec::new() }
805    }
806    /// Adds a pair.
807    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
808        self.pairs.push((from.into(), to.into()));
809    }
810    /// Applies all substitutions to `s` (leftmost-first order).
811    pub fn apply(&self, s: &str) -> String {
812        let mut result = s.to_string();
813        for (from, to) in &self.pairs {
814            result = result.replace(from.as_str(), to.as_str());
815        }
816        result
817    }
818    /// Returns the number of pairs.
819    pub fn len(&self) -> usize {
820        self.pairs.len()
821    }
822    /// Returns `true` if empty.
823    pub fn is_empty(&self) -> bool {
824        self.pairs.is_empty()
825    }
826}
827/// A versioned record that stores a history of values.
828#[allow(dead_code)]
829pub struct VersionedRecord<T: Clone> {
830    /// Every earlier value, oldest first: version `n` is `earlier[n]`.
831    earlier: Vec<T>,
832    /// The latest value (version `earlier.len()`), declared last so it drops last.
833    current: T,
834}
835#[allow(dead_code)]
836impl<T: Clone> VersionedRecord<T> {
837    /// Creates a new record with an initial value.
838    pub fn new(initial: T) -> Self {
839        Self {
840            earlier: Vec::new(),
841            current: initial,
842        }
843    }
844    /// Updates the record with a new version.
845    pub fn update(&mut self, val: T) {
846        let previous = std::mem::replace(&mut self.current, val);
847        self.earlier.push(previous);
848    }
849    /// Returns the current (latest) value.
850    pub fn current(&self) -> &T {
851        &self.current
852    }
853    /// Returns the value at version `n` (0-indexed), or `None`.
854    pub fn at_version(&self, n: usize) -> Option<&T> {
855        match self.earlier.get(n) {
856            Some(value) => Some(value),
857            None if n == self.earlier.len() => Some(&self.current),
858            None => None,
859        }
860    }
861    /// Returns the version number of the current value.
862    pub fn version(&self) -> usize {
863        self.earlier.len()
864    }
865    /// Returns `true` if more than one version exists.
866    pub fn has_history(&self) -> bool {
867        !self.earlier.is_empty()
868    }
869}
870/// A memoized normalizer that caches normal forms by expression identity.
871///
872/// This is useful when normalizing large expressions with shared subterms.
873#[allow(dead_code)]
874pub struct MemoizedNormalizer {
875    pub(super) cache: HashMap<String, Expr>,
876}
877impl MemoizedNormalizer {
878    /// Create a new memoized normalizer with an empty cache.
879    #[allow(dead_code)]
880    pub fn new() -> Self {
881        MemoizedNormalizer {
882            cache: HashMap::new(),
883        }
884    }
885    /// Normalize the given expression, caching the result.
886    #[allow(dead_code)]
887    pub fn normalize(&mut self, expr: &Expr) -> Expr {
888        let key = format!("{:?}", expr);
889        if let Some(cached) = self.cache.get(&key) {
890            return cached.clone();
891        }
892        let result = normalize(expr);
893        self.cache.insert(key, result.clone());
894        result
895    }
896    /// Clear the memoization cache.
897    #[allow(dead_code)]
898    pub fn clear_cache(&mut self) {
899        self.cache.clear();
900    }
901    /// Return the number of cached entries.
902    #[allow(dead_code)]
903    pub fn cache_size(&self) -> usize {
904        self.cache.len()
905    }
906}
907/// A simple mutable key-value store for test fixtures.
908#[allow(dead_code)]
909pub struct Fixture {
910    data: std::collections::HashMap<String, String>,
911}
912#[allow(dead_code)]
913impl Fixture {
914    /// Creates an empty fixture.
915    pub fn new() -> Self {
916        Self {
917            data: std::collections::HashMap::new(),
918        }
919    }
920    /// Sets a key.
921    pub fn set(&mut self, key: impl Into<String>, val: impl Into<String>) {
922        self.data.insert(key.into(), val.into());
923    }
924    /// Gets a value.
925    pub fn get(&self, key: &str) -> Option<&str> {
926        self.data.get(key).map(|s| s.as_str())
927    }
928    /// Returns the number of entries.
929    pub fn len(&self) -> usize {
930        self.data.len()
931    }
932    /// Returns whether the collection is empty.
933    pub fn is_empty(&self) -> bool {
934        self.len() == 0
935    }
936}
937/// A fixed-size sliding window that computes a running sum.
938#[allow(dead_code)]
939pub struct SlidingSum {
940    window: Vec<f64>,
941    capacity: usize,
942    pos: usize,
943    sum: f64,
944    count: usize,
945}
946#[allow(dead_code)]
947impl SlidingSum {
948    /// Creates a sliding sum with the given window size.
949    pub fn new(capacity: usize) -> Self {
950        Self {
951            window: vec![0.0; capacity],
952            capacity,
953            pos: 0,
954            sum: 0.0,
955            count: 0,
956        }
957    }
958    /// Adds a value to the window, removing the oldest if full.
959    pub fn push(&mut self, val: f64) {
960        let oldest = self.window[self.pos];
961        self.sum -= oldest;
962        self.sum += val;
963        self.window[self.pos] = val;
964        self.pos = (self.pos + 1) % self.capacity;
965        if self.count < self.capacity {
966            self.count += 1;
967        }
968    }
969    /// Returns the current window sum.
970    pub fn sum(&self) -> f64 {
971        self.sum
972    }
973    /// Returns the window mean, or `None` if empty.
974    pub fn mean(&self) -> Option<f64> {
975        if self.count == 0 {
976            None
977        } else {
978            Some(self.sum / self.count as f64)
979        }
980    }
981    /// Returns the current window size (number of valid elements).
982    pub fn count(&self) -> usize {
983        self.count
984    }
985}
986/// A non-empty list (at least one element guaranteed).
987#[allow(dead_code)]
988pub struct NonEmptyVec<T> {
989    head: T,
990    tail: Vec<T>,
991}
992#[allow(dead_code)]
993impl<T> NonEmptyVec<T> {
994    /// Creates a non-empty vec with a single element.
995    pub fn singleton(val: T) -> Self {
996        Self {
997            head: val,
998            tail: Vec::new(),
999        }
1000    }
1001    /// Pushes an element.
1002    pub fn push(&mut self, val: T) {
1003        self.tail.push(val);
1004    }
1005    /// Returns a reference to the first element.
1006    pub fn first(&self) -> &T {
1007        &self.head
1008    }
1009    /// Returns a reference to the last element.
1010    pub fn last(&self) -> &T {
1011        self.tail.last().unwrap_or(&self.head)
1012    }
1013    /// Returns the number of elements.
1014    pub fn len(&self) -> usize {
1015        1 + self.tail.len()
1016    }
1017    /// Returns whether the collection is empty.
1018    pub fn is_empty(&self) -> bool {
1019        self.len() == 0
1020    }
1021    /// Returns all elements as a Vec.
1022    pub fn to_vec(&self) -> Vec<&T> {
1023        let mut v = vec![&self.head];
1024        v.extend(self.tail.iter());
1025        v
1026    }
1027}
1028/// A trie-based prefix counter.
1029#[allow(dead_code)]
1030pub struct PrefixCounter {
1031    children: std::collections::HashMap<char, PrefixCounter>,
1032    count: usize,
1033}
1034#[allow(dead_code)]
1035impl PrefixCounter {
1036    /// Creates an empty prefix counter.
1037    pub fn new() -> Self {
1038        Self {
1039            children: std::collections::HashMap::new(),
1040            count: 0,
1041        }
1042    }
1043    /// Records a string.
1044    pub fn record(&mut self, s: &str) {
1045        self.count += 1;
1046        let mut node = self;
1047        for c in s.chars() {
1048            node = node.children.entry(c).or_default();
1049            node.count += 1;
1050        }
1051    }
1052    /// Returns how many strings have been recorded that start with `prefix`.
1053    pub fn count_with_prefix(&self, prefix: &str) -> usize {
1054        let mut node = self;
1055        for c in prefix.chars() {
1056            match node.children.get(&c) {
1057                Some(n) => node = n,
1058                None => return 0,
1059            }
1060        }
1061        node.count
1062    }
1063}
1064/// A lazily-normalized expression.
1065///
1066/// The expression is stored un-normalized and only computed on first access.
1067pub struct LazyNormal {
1068    pub(super) original: Expr,
1069    pub(super) normal: std::cell::OnceCell<Expr>,
1070}
1071impl LazyNormal {
1072    /// Wrap an expression without normalizing.
1073    pub fn new(e: Expr) -> Self {
1074        Self {
1075            original: e,
1076            normal: std::cell::OnceCell::new(),
1077        }
1078    }
1079    /// Get the original expression without normalizing.
1080    pub fn original(&self) -> &Expr {
1081        &self.original
1082    }
1083    /// Get the normalized expression (computed lazily).
1084    pub fn normalized(&self) -> &Expr {
1085        self.normal.get_or_init(|| normalize(&self.original))
1086    }
1087    /// Check if the expression has already been normalized.
1088    pub fn is_evaluated(&self) -> bool {
1089        self.normal.get().is_some()
1090    }
1091}
1092/// Statistics about a normalization run.
1093#[derive(Clone, Debug, Default)]
1094pub struct NormStats {
1095    /// Expressions visited.
1096    pub visited: usize,
1097    /// Cache hits (same expression normalized more than once).
1098    pub cache_hits: usize,
1099    /// Beta reductions performed.
1100    pub beta_steps: usize,
1101    /// Let unfoldings performed.
1102    pub let_steps: usize,
1103}
1104impl NormStats {
1105    /// Create empty statistics.
1106    pub fn new() -> Self {
1107        Self::default()
1108    }
1109    /// Total reduction steps.
1110    pub fn total_steps(&self) -> usize {
1111        self.beta_steps + self.let_steps
1112    }
1113}
1114/// A pair of `StatSummary` values tracking before/after a transformation.
1115#[allow(dead_code)]
1116pub struct TransformStat {
1117    before: StatSummary,
1118    after: StatSummary,
1119}
1120#[allow(dead_code)]
1121impl TransformStat {
1122    /// Creates a new transform stat recorder.
1123    pub fn new() -> Self {
1124        Self {
1125            before: StatSummary::new(),
1126            after: StatSummary::new(),
1127        }
1128    }
1129    /// Records a before value.
1130    pub fn record_before(&mut self, v: f64) {
1131        self.before.record(v);
1132    }
1133    /// Records an after value.
1134    pub fn record_after(&mut self, v: f64) {
1135        self.after.record(v);
1136    }
1137    /// Returns the mean reduction ratio (after/before).
1138    pub fn mean_ratio(&self) -> Option<f64> {
1139        let b = self.before.mean()?;
1140        let a = self.after.mean()?;
1141        if b.abs() < f64::EPSILON {
1142            return None;
1143        }
1144        Some(a / b)
1145    }
1146}
1147/// A window iterator that yields overlapping windows of size `n`.
1148#[allow(dead_code)]
1149pub struct WindowIterator<'a, T> {
1150    pub(super) data: &'a [T],
1151    pub(super) pos: usize,
1152    pub(super) window: usize,
1153}
1154#[allow(dead_code)]
1155impl<'a, T> WindowIterator<'a, T> {
1156    /// Creates a new window iterator.
1157    pub fn new(data: &'a [T], window: usize) -> Self {
1158        Self {
1159            data,
1160            pos: 0,
1161            window,
1162        }
1163    }
1164}
1165/// A min-heap implemented as a binary heap.
1166#[allow(dead_code)]
1167pub struct MinHeap<T: Ord> {
1168    data: Vec<T>,
1169}
1170#[allow(dead_code)]
1171impl<T: Ord> MinHeap<T> {
1172    /// Creates a new empty min-heap.
1173    pub fn new() -> Self {
1174        Self { data: Vec::new() }
1175    }
1176    /// Inserts an element.
1177    pub fn push(&mut self, val: T) {
1178        self.data.push(val);
1179        self.sift_up(self.data.len() - 1);
1180    }
1181    /// Removes and returns the minimum element.
1182    pub fn pop(&mut self) -> Option<T> {
1183        if self.data.is_empty() {
1184            return None;
1185        }
1186        let n = self.data.len();
1187        self.data.swap(0, n - 1);
1188        let min = self.data.pop();
1189        if !self.data.is_empty() {
1190            self.sift_down(0);
1191        }
1192        min
1193    }
1194    /// Returns a reference to the minimum element.
1195    pub fn peek(&self) -> Option<&T> {
1196        self.data.first()
1197    }
1198    /// Returns the number of elements.
1199    pub fn len(&self) -> usize {
1200        self.data.len()
1201    }
1202    /// Returns `true` if empty.
1203    pub fn is_empty(&self) -> bool {
1204        self.data.is_empty()
1205    }
1206    fn sift_up(&mut self, mut i: usize) {
1207        while i > 0 {
1208            let parent = (i - 1) / 2;
1209            if self.data[i] < self.data[parent] {
1210                self.data.swap(i, parent);
1211                i = parent;
1212            } else {
1213                break;
1214            }
1215        }
1216    }
1217    fn sift_down(&mut self, mut i: usize) {
1218        let n = self.data.len();
1219        loop {
1220            let left = 2 * i + 1;
1221            let right = 2 * i + 2;
1222            let mut smallest = i;
1223            if left < n && self.data[left] < self.data[smallest] {
1224                smallest = left;
1225            }
1226            if right < n && self.data[right] < self.data[smallest] {
1227                smallest = right;
1228            }
1229            if smallest == i {
1230                break;
1231            }
1232            self.data.swap(i, smallest);
1233            i = smallest;
1234        }
1235    }
1236}
1237/// Represents a rewrite rule `lhs → rhs`.
1238#[allow(dead_code)]
1239#[allow(missing_docs)]
1240pub struct RewriteRule {
1241    /// The name of the rule.
1242    pub name: String,
1243    /// A string representation of the LHS pattern.
1244    pub lhs: String,
1245    /// A string representation of the RHS.
1246    pub rhs: String,
1247    /// Whether this is a conditional rule (has side conditions).
1248    pub conditional: bool,
1249}
1250#[allow(dead_code)]
1251impl RewriteRule {
1252    /// Creates an unconditional rewrite rule.
1253    pub fn unconditional(
1254        name: impl Into<String>,
1255        lhs: impl Into<String>,
1256        rhs: impl Into<String>,
1257    ) -> Self {
1258        Self {
1259            name: name.into(),
1260            lhs: lhs.into(),
1261            rhs: rhs.into(),
1262            conditional: false,
1263        }
1264    }
1265    /// Creates a conditional rewrite rule.
1266    pub fn conditional(
1267        name: impl Into<String>,
1268        lhs: impl Into<String>,
1269        rhs: impl Into<String>,
1270    ) -> Self {
1271        Self {
1272            name: name.into(),
1273            lhs: lhs.into(),
1274            rhs: rhs.into(),
1275            conditional: true,
1276        }
1277    }
1278    /// Returns a textual representation.
1279    pub fn display(&self) -> String {
1280        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
1281    }
1282}