Skip to main content

oxilean_kernel/expr_cache/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::{BinderInfo, Expr, Level, Literal, Name};
6use std::collections::HashMap;
7use std::hash::{Hash, Hasher};
8
9/// A pair of `StatSummary` values tracking before/after a transformation.
10#[allow(dead_code)]
11pub struct TransformStat {
12    before: StatSummary,
13    after: StatSummary,
14}
15#[allow(dead_code)]
16impl TransformStat {
17    /// Creates a new transform stat recorder.
18    pub fn new() -> Self {
19        Self {
20            before: StatSummary::new(),
21            after: StatSummary::new(),
22        }
23    }
24    /// Records a before value.
25    pub fn record_before(&mut self, v: f64) {
26        self.before.record(v);
27    }
28    /// Records an after value.
29    pub fn record_after(&mut self, v: f64) {
30        self.after.record(v);
31    }
32    /// Returns the mean reduction ratio (after/before).
33    pub fn mean_ratio(&self) -> Option<f64> {
34        let b = self.before.mean()?;
35        let a = self.after.mean()?;
36        if b.abs() < f64::EPSILON {
37            return None;
38        }
39        Some(a / b)
40    }
41}
42/// Unique identifier for an interned expression.
43///
44/// Two expressions with the same `ExprId` are guaranteed to be structurally
45/// identical (hash-consing invariant).
46#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
47pub struct ExprId(pub(crate) u32);
48impl ExprId {
49    /// Return the underlying u32 value.
50    pub fn as_u32(&self) -> u32 {
51        self.0
52    }
53}
54/// Hash-consing table for `Expr` values.
55///
56/// Every unique expression (by structural equality) is assigned a stable
57/// `ExprId`. Subsequent `intern` calls for the same expression return the
58/// same `ExprId` and increment `hit_count`.
59pub struct ExprHashcons {
60    /// All interned expressions, indexed by `ExprId.0`.
61    id_to_expr: Vec<Expr>,
62    /// Mapping from structural expression key to its assigned `ExprId`.
63    expr_to_id: HashMap<ExprKey, ExprId>,
64    /// Number of times `intern` returned an existing ID.
65    hit_count: u64,
66    /// Number of times `intern` created a new entry.
67    miss_count: u64,
68}
69impl ExprHashcons {
70    /// Create a new, empty hash-consing table.
71    pub fn new() -> Self {
72        ExprHashcons {
73            id_to_expr: Vec::new(),
74            expr_to_id: HashMap::new(),
75            hit_count: 0,
76            miss_count: 0,
77        }
78    }
79    /// Intern an expression.
80    ///
81    /// Returns `(id, was_new)` where `was_new` is `true` if this expression
82    /// had never been interned before.
83    pub fn intern(&mut self, expr: Expr) -> (ExprId, bool) {
84        let key = ExprKey(expr.clone());
85        if let Some(&id) = self.expr_to_id.get(&key) {
86            self.hit_count += 1;
87            (id, false)
88        } else {
89            let id = ExprId(self.id_to_expr.len() as u32);
90            self.id_to_expr.push(expr);
91            self.expr_to_id.insert(key, id);
92            self.miss_count += 1;
93            (id, true)
94        }
95    }
96    /// Look up an expression by `ExprId`.
97    ///
98    /// Returns `None` if the ID is out of range (should not happen for
99    /// IDs produced by this table).
100    pub fn get(&self, id: ExprId) -> Option<&Expr> {
101        self.id_to_expr.get(id.0 as usize)
102    }
103    /// Find the `ExprId` for a structurally equal expression, if it exists.
104    ///
105    /// Does **not** update hit/miss counters.
106    pub fn get_id(&self, expr: &Expr) -> Option<ExprId> {
107        let key = ExprKey(expr.clone());
108        self.expr_to_id.get(&key).copied()
109    }
110    /// Return the number of distinct expressions currently interned.
111    pub fn size(&self) -> usize {
112        self.id_to_expr.len()
113    }
114    /// Compute the cache hit rate as a value in [0.0, 1.0].
115    ///
116    /// Returns `0.0` if no `intern` calls have been made.
117    pub fn hit_rate(&self) -> f64 {
118        let total = self.hit_count + self.miss_count;
119        if total == 0 {
120            0.0
121        } else {
122            self.hit_count as f64 / total as f64
123        }
124    }
125    /// Return a formatted statistics string.
126    pub fn stats(&self) -> String {
127        format!(
128            "ExprHashcons {{ size: {}, hits: {}, misses: {}, hit_rate: {:.2}% }}",
129            self.size(),
130            self.hit_count,
131            self.miss_count,
132            self.hit_rate() * 100.0,
133        )
134    }
135}
136/// A tagged union for representing a simple two-case discriminated union.
137#[allow(dead_code)]
138pub enum Either2<A, B> {
139    /// The first alternative.
140    First(A),
141    /// The second alternative.
142    Second(B),
143}
144#[allow(dead_code)]
145impl<A, B> Either2<A, B> {
146    /// Returns `true` if this is the first alternative.
147    pub fn is_first(&self) -> bool {
148        matches!(self, Either2::First(_))
149    }
150    /// Returns `true` if this is the second alternative.
151    pub fn is_second(&self) -> bool {
152        matches!(self, Either2::Second(_))
153    }
154    /// Returns the first value if present.
155    pub fn first(self) -> Option<A> {
156        match self {
157            Either2::First(a) => Some(a),
158            _ => None,
159        }
160    }
161    /// Returns the second value if present.
162    pub fn second(self) -> Option<B> {
163        match self {
164            Either2::Second(b) => Some(b),
165            _ => None,
166        }
167    }
168    /// Maps over the first alternative.
169    pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
170        match self {
171            Either2::First(a) => Either2::First(f(a)),
172            Either2::Second(b) => Either2::Second(b),
173        }
174    }
175}
176/// A small fixed-size set implemented as a bit array.
177#[allow(dead_code)]
178pub struct BitSet64 {
179    bits: u64,
180}
181#[allow(dead_code)]
182impl BitSet64 {
183    /// Creates an empty set.
184    pub const fn new() -> Self {
185        Self { bits: 0 }
186    }
187    /// Inserts element `i` (0–63).
188    pub fn insert(&mut self, i: u32) {
189        if i < 64 {
190            self.bits |= 1u64 << i;
191        }
192    }
193    /// Removes element `i`.
194    pub fn remove(&mut self, i: u32) {
195        if i < 64 {
196            self.bits &= !(1u64 << i);
197        }
198    }
199    /// Returns `true` if `i` is in the set.
200    pub fn contains(&self, i: u32) -> bool {
201        i < 64 && (self.bits >> i) & 1 != 0
202    }
203    /// Returns the cardinality.
204    pub fn len(&self) -> u32 {
205        self.bits.count_ones()
206    }
207    /// Returns `true` if empty.
208    pub fn is_empty(&self) -> bool {
209        self.bits == 0
210    }
211    /// Returns the union with `other`.
212    pub fn union(self, other: BitSet64) -> BitSet64 {
213        BitSet64 {
214            bits: self.bits | other.bits,
215        }
216    }
217    /// Returns the intersection with `other`.
218    pub fn intersect(self, other: BitSet64) -> BitSet64 {
219        BitSet64 {
220            bits: self.bits & other.bits,
221        }
222    }
223}
224/// A pool of reusable string buffers.
225#[allow(dead_code)]
226pub struct StringPool {
227    free: Vec<String>,
228}
229#[allow(dead_code)]
230impl StringPool {
231    /// Creates a new empty string pool.
232    pub fn new() -> Self {
233        Self { free: Vec::new() }
234    }
235    /// Takes a string from the pool (may be empty).
236    pub fn take(&mut self) -> String {
237        self.free.pop().unwrap_or_default()
238    }
239    /// Returns a string to the pool.
240    pub fn give(&mut self, mut s: String) {
241        s.clear();
242        self.free.push(s);
243    }
244    /// Returns the number of free strings in the pool.
245    pub fn free_count(&self) -> usize {
246        self.free.len()
247    }
248}
249/// A simple decision tree node for rule dispatching.
250#[allow(dead_code)]
251#[allow(missing_docs)]
252pub enum DecisionNode {
253    /// A leaf with an action string.
254    Leaf(String),
255    /// An interior node: check `key` equals `val` → `yes_branch`, else `no_branch`.
256    Branch {
257        key: String,
258        val: String,
259        yes_branch: Box<DecisionNode>,
260        no_branch: Box<DecisionNode>,
261    },
262}
263#[allow(dead_code)]
264impl DecisionNode {
265    /// Evaluates the decision tree with the given context.
266    pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
267        match self {
268            DecisionNode::Leaf(action) => action.as_str(),
269            DecisionNode::Branch {
270                key,
271                val,
272                yes_branch,
273                no_branch,
274            } => {
275                let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
276                if actual == val.as_str() {
277                    yes_branch.evaluate(ctx)
278                } else {
279                    no_branch.evaluate(ctx)
280                }
281            }
282        }
283    }
284    /// Returns the depth of the decision tree.
285    pub fn depth(&self) -> usize {
286        match self {
287            DecisionNode::Leaf(_) => 0,
288            DecisionNode::Branch {
289                yes_branch,
290                no_branch,
291                ..
292            } => 1 + yes_branch.depth().max(no_branch.depth()),
293        }
294    }
295}
296/// A window iterator that yields overlapping windows of size `n`.
297#[allow(dead_code)]
298pub struct WindowIterator<'a, T> {
299    pub(super) data: &'a [T],
300    pub(super) pos: usize,
301    pub(super) window: usize,
302}
303#[allow(dead_code)]
304impl<'a, T> WindowIterator<'a, T> {
305    /// Creates a new window iterator.
306    pub fn new(data: &'a [T], window: usize) -> Self {
307        Self {
308            data,
309            pos: 0,
310            window,
311        }
312    }
313}
314/// A token bucket rate limiter.
315#[allow(dead_code)]
316pub struct TokenBucket {
317    capacity: u64,
318    tokens: u64,
319    refill_per_ms: u64,
320    last_refill: std::time::Instant,
321}
322#[allow(dead_code)]
323impl TokenBucket {
324    /// Creates a new token bucket.
325    pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
326        Self {
327            capacity,
328            tokens: capacity,
329            refill_per_ms,
330            last_refill: std::time::Instant::now(),
331        }
332    }
333    /// Attempts to consume `n` tokens.  Returns `true` on success.
334    pub fn try_consume(&mut self, n: u64) -> bool {
335        self.refill();
336        if self.tokens >= n {
337            self.tokens -= n;
338            true
339        } else {
340            false
341        }
342    }
343    fn refill(&mut self) {
344        let now = std::time::Instant::now();
345        let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
346        if elapsed_ms > 0 {
347            let new_tokens = elapsed_ms * self.refill_per_ms;
348            self.tokens = (self.tokens + new_tokens).min(self.capacity);
349            self.last_refill = now;
350        }
351    }
352    /// Returns the number of currently available tokens.
353    pub fn available(&self) -> u64 {
354        self.tokens
355    }
356    /// Returns the bucket capacity.
357    pub fn capacity(&self) -> u64 {
358        self.capacity
359    }
360}
361/// A simple key-value store backed by a sorted Vec for small maps.
362#[allow(dead_code)]
363pub struct SmallMap<K: Ord + Clone, V: Clone> {
364    entries: Vec<(K, V)>,
365}
366#[allow(dead_code)]
367impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
368    /// Creates a new empty small map.
369    pub fn new() -> Self {
370        Self {
371            entries: Vec::new(),
372        }
373    }
374    /// Inserts or replaces the value for `key`.
375    pub fn insert(&mut self, key: K, val: V) {
376        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
377            Ok(i) => self.entries[i].1 = val,
378            Err(i) => self.entries.insert(i, (key, val)),
379        }
380    }
381    /// Returns the value for `key`, or `None`.
382    pub fn get(&self, key: &K) -> Option<&V> {
383        self.entries
384            .binary_search_by_key(&key, |(k, _)| k)
385            .ok()
386            .map(|i| &self.entries[i].1)
387    }
388    /// Returns the number of entries.
389    pub fn len(&self) -> usize {
390        self.entries.len()
391    }
392    /// Returns `true` if empty.
393    pub fn is_empty(&self) -> bool {
394        self.entries.is_empty()
395    }
396    /// Returns all keys.
397    pub fn keys(&self) -> Vec<&K> {
398        self.entries.iter().map(|(k, _)| k).collect()
399    }
400    /// Returns all values.
401    pub fn values(&self) -> Vec<&V> {
402        self.entries.iter().map(|(_, v)| v).collect()
403    }
404}
405/// A mutable reference stack for tracking the current "focus" in a tree traversal.
406#[allow(dead_code)]
407pub struct FocusStack<T> {
408    items: Vec<T>,
409}
410#[allow(dead_code)]
411impl<T> FocusStack<T> {
412    /// Creates an empty focus stack.
413    pub fn new() -> Self {
414        Self { items: Vec::new() }
415    }
416    /// Focuses on `item`.
417    pub fn focus(&mut self, item: T) {
418        self.items.push(item);
419    }
420    /// Blurs (pops) the current focus.
421    pub fn blur(&mut self) -> Option<T> {
422        self.items.pop()
423    }
424    /// Returns the current focus, or `None`.
425    pub fn current(&self) -> Option<&T> {
426        self.items.last()
427    }
428    /// Returns the focus depth.
429    pub fn depth(&self) -> usize {
430        self.items.len()
431    }
432    /// Returns `true` if there is no current focus.
433    pub fn is_empty(&self) -> bool {
434        self.items.is_empty()
435    }
436}
437/// A label set for a graph node.
438#[allow(dead_code)]
439pub struct LabelSet {
440    labels: Vec<String>,
441}
442#[allow(dead_code)]
443impl LabelSet {
444    /// Creates a new empty label set.
445    pub fn new() -> Self {
446        Self { labels: Vec::new() }
447    }
448    /// Adds a label (deduplicates).
449    pub fn add(&mut self, label: impl Into<String>) {
450        let s = label.into();
451        if !self.labels.contains(&s) {
452            self.labels.push(s);
453        }
454    }
455    /// Returns `true` if `label` is present.
456    pub fn has(&self, label: &str) -> bool {
457        self.labels.iter().any(|l| l == label)
458    }
459    /// Returns the count of labels.
460    pub fn count(&self) -> usize {
461        self.labels.len()
462    }
463    /// Returns all labels.
464    pub fn all(&self) -> &[String] {
465        &self.labels
466    }
467}
468/// Represents a rewrite rule `lhs → rhs`.
469#[allow(dead_code)]
470#[allow(missing_docs)]
471pub struct RewriteRule {
472    /// The name of the rule.
473    pub name: String,
474    /// A string representation of the LHS pattern.
475    pub lhs: String,
476    /// A string representation of the RHS.
477    pub rhs: String,
478    /// Whether this is a conditional rule (has side conditions).
479    pub conditional: bool,
480}
481#[allow(dead_code)]
482impl RewriteRule {
483    /// Creates an unconditional rewrite rule.
484    pub fn unconditional(
485        name: impl Into<String>,
486        lhs: impl Into<String>,
487        rhs: impl Into<String>,
488    ) -> Self {
489        Self {
490            name: name.into(),
491            lhs: lhs.into(),
492            rhs: rhs.into(),
493            conditional: false,
494        }
495    }
496    /// Creates a conditional rewrite rule.
497    pub fn conditional(
498        name: impl Into<String>,
499        lhs: impl Into<String>,
500        rhs: impl Into<String>,
501    ) -> Self {
502        Self {
503            name: name.into(),
504            lhs: lhs.into(),
505            rhs: rhs.into(),
506            conditional: true,
507        }
508    }
509    /// Returns a textual representation.
510    pub fn display(&self) -> String {
511        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
512    }
513}
514/// A simple directed acyclic graph.
515#[allow(dead_code)]
516pub struct SimpleDag {
517    /// `edges[i]` is the list of direct successors of node `i`.
518    edges: Vec<Vec<usize>>,
519}
520#[allow(dead_code)]
521impl SimpleDag {
522    /// Creates a DAG with `n` nodes and no edges.
523    pub fn new(n: usize) -> Self {
524        Self {
525            edges: vec![Vec::new(); n],
526        }
527    }
528    /// Adds an edge from `from` to `to`.
529    pub fn add_edge(&mut self, from: usize, to: usize) {
530        if from < self.edges.len() {
531            self.edges[from].push(to);
532        }
533    }
534    /// Returns the successors of `node`.
535    pub fn successors(&self, node: usize) -> &[usize] {
536        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
537    }
538    /// Returns `true` if `from` can reach `to` via DFS.
539    pub fn can_reach(&self, from: usize, to: usize) -> bool {
540        let mut visited = vec![false; self.edges.len()];
541        self.dfs(from, to, &mut visited)
542    }
543    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
544        if cur == target {
545            return true;
546        }
547        if cur >= visited.len() || visited[cur] {
548            return false;
549        }
550        visited[cur] = true;
551        for &next in self.successors(cur) {
552            if self.dfs(next, target, visited) {
553                return true;
554            }
555        }
556        false
557    }
558    /// Returns the topological order of nodes, or `None` if a cycle is detected.
559    pub fn topological_sort(&self) -> Option<Vec<usize>> {
560        let n = self.edges.len();
561        let mut in_degree = vec![0usize; n];
562        for succs in &self.edges {
563            for &s in succs {
564                if s < n {
565                    in_degree[s] += 1;
566                }
567            }
568        }
569        let mut queue: std::collections::VecDeque<usize> =
570            (0..n).filter(|&i| in_degree[i] == 0).collect();
571        let mut order = Vec::new();
572        while let Some(node) = queue.pop_front() {
573            order.push(node);
574            for &s in self.successors(node) {
575                if s < n {
576                    in_degree[s] -= 1;
577                    if in_degree[s] == 0 {
578                        queue.push_back(s);
579                    }
580                }
581            }
582        }
583        if order.len() == n {
584            Some(order)
585        } else {
586            None
587        }
588    }
589    /// Returns the number of nodes.
590    pub fn num_nodes(&self) -> usize {
591        self.edges.len()
592    }
593}
594/// A type-erased function pointer with arity tracking.
595#[allow(dead_code)]
596pub struct RawFnPtr {
597    /// The raw function pointer (stored as usize for type erasure).
598    ptr: usize,
599    arity: usize,
600    name: String,
601}
602#[allow(dead_code)]
603impl RawFnPtr {
604    /// Creates a new raw function pointer descriptor.
605    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
606        Self {
607            ptr,
608            arity,
609            name: name.into(),
610        }
611    }
612    /// Returns the arity.
613    pub fn arity(&self) -> usize {
614        self.arity
615    }
616    /// Returns the name.
617    pub fn name(&self) -> &str {
618        &self.name
619    }
620    /// Returns the raw pointer value.
621    pub fn raw(&self) -> usize {
622        self.ptr
623    }
624}
625/// Statistics about a cache lookup session.
626#[allow(dead_code)]
627#[derive(Debug, Clone, Default)]
628pub struct CacheSessionStats {
629    pub hits: u64,
630    pub misses: u64,
631    pub insertions: u64,
632    pub evictions: u64,
633}
634#[allow(dead_code)]
635impl CacheSessionStats {
636    /// Create zeroed stats.
637    pub fn new() -> Self {
638        CacheSessionStats::default()
639    }
640    /// Return the hit rate as a fraction in \[0,1\].
641    pub fn hit_rate(&self) -> f64 {
642        let total = self.hits + self.misses;
643        if total == 0 {
644            1.0
645        } else {
646            self.hits as f64 / total as f64
647        }
648    }
649    /// Merge another stats struct into this one.
650    pub fn merge(&mut self, other: &CacheSessionStats) {
651        self.hits += other.hits;
652        self.misses += other.misses;
653        self.insertions += other.insertions;
654        self.evictions += other.evictions;
655    }
656    /// Reset to zero.
657    pub fn reset(&mut self) {
658        *self = CacheSessionStats::default();
659    }
660    /// Format a one-line summary.
661    pub fn summary(&self) -> String {
662        format!(
663            "hits={} misses={} insertions={} evictions={} hit_rate={:.1}%",
664            self.hits,
665            self.misses,
666            self.insertions,
667            self.evictions,
668            self.hit_rate() * 100.0
669        )
670    }
671}
672/// A non-empty list (at least one element guaranteed).
673#[allow(dead_code)]
674pub struct NonEmptyVec<T> {
675    head: T,
676    tail: Vec<T>,
677}
678#[allow(dead_code)]
679impl<T> NonEmptyVec<T> {
680    /// Creates a non-empty vec with a single element.
681    pub fn singleton(val: T) -> Self {
682        Self {
683            head: val,
684            tail: Vec::new(),
685        }
686    }
687    /// Pushes an element.
688    pub fn push(&mut self, val: T) {
689        self.tail.push(val);
690    }
691    /// Returns a reference to the first element.
692    pub fn first(&self) -> &T {
693        &self.head
694    }
695    /// Returns a reference to the last element.
696    pub fn last(&self) -> &T {
697        self.tail.last().unwrap_or(&self.head)
698    }
699    /// Returns the number of elements.
700    pub fn len(&self) -> usize {
701        1 + self.tail.len()
702    }
703    /// Returns whether the collection is empty.
704    pub fn is_empty(&self) -> bool {
705        self.len() == 0
706    }
707    /// Returns all elements as a Vec.
708    pub fn to_vec(&self) -> Vec<&T> {
709        let mut v = vec![&self.head];
710        v.extend(self.tail.iter());
711        v
712    }
713}
714/// A min-heap implemented as a binary heap.
715#[allow(dead_code)]
716pub struct MinHeap<T: Ord> {
717    data: Vec<T>,
718}
719#[allow(dead_code)]
720impl<T: Ord> MinHeap<T> {
721    /// Creates a new empty min-heap.
722    pub fn new() -> Self {
723        Self { data: Vec::new() }
724    }
725    /// Inserts an element.
726    pub fn push(&mut self, val: T) {
727        self.data.push(val);
728        self.sift_up(self.data.len() - 1);
729    }
730    /// Removes and returns the minimum element.
731    pub fn pop(&mut self) -> Option<T> {
732        if self.data.is_empty() {
733            return None;
734        }
735        let n = self.data.len();
736        self.data.swap(0, n - 1);
737        let min = self.data.pop();
738        if !self.data.is_empty() {
739            self.sift_down(0);
740        }
741        min
742    }
743    /// Returns a reference to the minimum element.
744    pub fn peek(&self) -> Option<&T> {
745        self.data.first()
746    }
747    /// Returns the number of elements.
748    pub fn len(&self) -> usize {
749        self.data.len()
750    }
751    /// Returns `true` if empty.
752    pub fn is_empty(&self) -> bool {
753        self.data.is_empty()
754    }
755    fn sift_up(&mut self, mut i: usize) {
756        while i > 0 {
757            let parent = (i - 1) / 2;
758            if self.data[i] < self.data[parent] {
759                self.data.swap(i, parent);
760                i = parent;
761            } else {
762                break;
763            }
764        }
765    }
766    fn sift_down(&mut self, mut i: usize) {
767        let n = self.data.len();
768        loop {
769            let left = 2 * i + 1;
770            let right = 2 * i + 2;
771            let mut smallest = i;
772            if left < n && self.data[left] < self.data[smallest] {
773                smallest = left;
774            }
775            if right < n && self.data[right] < self.data[smallest] {
776                smallest = right;
777            }
778            if smallest == i {
779                break;
780            }
781            self.data.swap(i, smallest);
782            i = smallest;
783        }
784    }
785}
786/// A trie-based prefix counter.
787#[allow(dead_code)]
788pub struct PrefixCounter {
789    children: std::collections::HashMap<char, PrefixCounter>,
790    count: usize,
791}
792#[allow(dead_code)]
793impl PrefixCounter {
794    /// Creates an empty prefix counter.
795    pub fn new() -> Self {
796        Self {
797            children: std::collections::HashMap::new(),
798            count: 0,
799        }
800    }
801    /// Records a string.
802    pub fn record(&mut self, s: &str) {
803        self.count += 1;
804        let mut node = self;
805        for c in s.chars() {
806            node = node.children.entry(c).or_default();
807            node.count += 1;
808        }
809    }
810    /// Returns how many strings have been recorded that start with `prefix`.
811    pub fn count_with_prefix(&self, prefix: &str) -> usize {
812        let mut node = self;
813        for c in prefix.chars() {
814            match node.children.get(&c) {
815                Some(n) => node = n,
816                None => return 0,
817            }
818        }
819        node.count
820    }
821}
822/// A two-level cache: a small hot cache backed by a larger cold cache.
823#[allow(dead_code)]
824pub struct TwoLevelCache {
825    hot: MemoTable,
826    cold: MemoTable,
827    hot_capacity: usize,
828    stats: CacheSessionStats,
829}
830#[allow(dead_code)]
831impl TwoLevelCache {
832    /// Create a two-level cache with the given hot capacity.
833    pub fn new(hot_capacity: usize) -> Self {
834        TwoLevelCache {
835            hot: MemoTable::new(),
836            cold: MemoTable::new(),
837            hot_capacity,
838            stats: CacheSessionStats::new(),
839        }
840    }
841    /// Look up a key; checks hot first, then cold (and promotes on hit).
842    pub fn get(&mut self, key: u64) -> Option<u64> {
843        if let Some(v) = self.hot.get(key) {
844            self.stats.hits += 1;
845            return Some(v);
846        }
847        if let Some(v) = self.cold.get(key) {
848            self.stats.hits += 1;
849            self.promote(key, v);
850            return Some(v);
851        }
852        self.stats.misses += 1;
853        None
854    }
855    /// Insert a value; placed in hot cache (evicts to cold if full).
856    pub fn insert(&mut self, key: u64, val: u64) {
857        if self.hot.len() >= self.hot_capacity {
858            if let Some((k, v)) = self.hot.entries.first().copied() {
859                self.hot.remove(k);
860                self.cold.insert(k, v);
861                self.stats.evictions += 1;
862            }
863        }
864        self.hot.insert(key, val);
865        self.stats.insertions += 1;
866    }
867    fn promote(&mut self, key: u64, val: u64) {
868        self.cold.remove(key);
869        self.insert(key, val);
870    }
871    /// Return a reference to the current session stats.
872    pub fn stats(&self) -> &CacheSessionStats {
873        &self.stats
874    }
875    /// Return total number of entries (hot + cold).
876    pub fn total_len(&self) -> usize {
877        self.hot.len() + self.cold.len()
878    }
879    /// Clear both levels.
880    pub fn clear(&mut self) {
881        self.hot.clear();
882        self.cold.clear();
883        self.stats.reset();
884    }
885}
886/// A dependency closure builder (transitive closure via BFS).
887#[allow(dead_code)]
888pub struct TransitiveClosure {
889    adj: Vec<Vec<usize>>,
890    n: usize,
891}
892#[allow(dead_code)]
893impl TransitiveClosure {
894    /// Creates a transitive closure builder for `n` nodes.
895    pub fn new(n: usize) -> Self {
896        Self {
897            adj: vec![Vec::new(); n],
898            n,
899        }
900    }
901    /// Adds a direct edge.
902    pub fn add_edge(&mut self, from: usize, to: usize) {
903        if from < self.n {
904            self.adj[from].push(to);
905        }
906    }
907    /// Computes all nodes reachable from `start` (including `start`).
908    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
909        let mut visited = vec![false; self.n];
910        let mut queue = std::collections::VecDeque::new();
911        queue.push_back(start);
912        while let Some(node) = queue.pop_front() {
913            if node >= self.n || visited[node] {
914                continue;
915            }
916            visited[node] = true;
917            for &next in &self.adj[node] {
918                queue.push_back(next);
919            }
920        }
921        (0..self.n).filter(|&i| visited[i]).collect()
922    }
923    /// Returns `true` if `from` can transitively reach `to`.
924    pub fn can_reach(&self, from: usize, to: usize) -> bool {
925        self.reachable_from(from).contains(&to)
926    }
927}
928#[allow(dead_code)]
929struct PathNode {
930    key: u32,
931    value: Option<u64>,
932    children: Vec<usize>,
933}
934/// A sparse vector: stores only non-default elements.
935#[allow(dead_code)]
936pub struct SparseVec<T: Default + Clone + PartialEq> {
937    entries: std::collections::HashMap<usize, T>,
938    default_: T,
939    logical_len: usize,
940}
941#[allow(dead_code)]
942impl<T: Default + Clone + PartialEq> SparseVec<T> {
943    /// Creates a new sparse vector with logical length `len`.
944    pub fn new(len: usize) -> Self {
945        Self {
946            entries: std::collections::HashMap::new(),
947            default_: T::default(),
948            logical_len: len,
949        }
950    }
951    /// Sets element at `idx`.
952    pub fn set(&mut self, idx: usize, val: T) {
953        if val == self.default_ {
954            self.entries.remove(&idx);
955        } else {
956            self.entries.insert(idx, val);
957        }
958    }
959    /// Gets element at `idx`.
960    pub fn get(&self, idx: usize) -> &T {
961        self.entries.get(&idx).unwrap_or(&self.default_)
962    }
963    /// Returns the logical length.
964    pub fn len(&self) -> usize {
965        self.logical_len
966    }
967    /// Returns whether the collection is empty.
968    pub fn is_empty(&self) -> bool {
969        self.len() == 0
970    }
971    /// Returns the number of non-default elements.
972    pub fn nnz(&self) -> usize {
973        self.entries.len()
974    }
975}
976/// A simple mutable key-value store for test fixtures.
977#[allow(dead_code)]
978pub struct Fixture {
979    data: std::collections::HashMap<String, String>,
980}
981#[allow(dead_code)]
982impl Fixture {
983    /// Creates an empty fixture.
984    pub fn new() -> Self {
985        Self {
986            data: std::collections::HashMap::new(),
987        }
988    }
989    /// Sets a key.
990    pub fn set(&mut self, key: impl Into<String>, val: impl Into<String>) {
991        self.data.insert(key.into(), val.into());
992    }
993    /// Gets a value.
994    pub fn get(&self, key: &str) -> Option<&str> {
995        self.data.get(key).map(|s| s.as_str())
996    }
997    /// Returns the number of entries.
998    pub fn len(&self) -> usize {
999        self.data.len()
1000    }
1001    /// Returns whether the collection is empty.
1002    pub fn is_empty(&self) -> bool {
1003        self.len() == 0
1004    }
1005}
1006/// A versioned record that stores a history of values.
1007#[allow(dead_code)]
1008pub struct VersionedRecord<T: Clone> {
1009    history: Vec<T>,
1010}
1011#[allow(dead_code)]
1012impl<T: Clone> VersionedRecord<T> {
1013    /// Creates a new record with an initial value.
1014    pub fn new(initial: T) -> Self {
1015        Self {
1016            history: vec![initial],
1017        }
1018    }
1019    /// Updates the record with a new version.
1020    pub fn update(&mut self, val: T) {
1021        self.history.push(val);
1022    }
1023    /// Returns the current (latest) value.
1024    pub fn current(&self) -> &T {
1025        self.history
1026            .last()
1027            .expect("VersionedRecord history is always non-empty after construction")
1028    }
1029    /// Returns the value at version `n` (0-indexed), or `None`.
1030    pub fn at_version(&self, n: usize) -> Option<&T> {
1031        self.history.get(n)
1032    }
1033    /// Returns the version number of the current value.
1034    pub fn version(&self) -> usize {
1035        self.history.len() - 1
1036    }
1037    /// Returns `true` if more than one version exists.
1038    pub fn has_history(&self) -> bool {
1039        self.history.len() > 1
1040    }
1041}
1042/// A cache invalidation set tracking which expression ids to evict.
1043#[allow(dead_code)]
1044pub struct InvalidationSet {
1045    ids: Vec<u64>,
1046}
1047#[allow(dead_code)]
1048impl InvalidationSet {
1049    /// Create an empty set.
1050    pub fn new() -> Self {
1051        InvalidationSet { ids: Vec::new() }
1052    }
1053    /// Add an id to invalidate.
1054    pub fn add(&mut self, id: u64) {
1055        if !self.ids.contains(&id) {
1056            self.ids.push(id);
1057        }
1058    }
1059    /// Return whether the set contains the given id.
1060    pub fn contains(&self, id: u64) -> bool {
1061        self.ids.contains(&id)
1062    }
1063    /// Return all ids to invalidate.
1064    pub fn ids(&self) -> &[u64] {
1065        &self.ids
1066    }
1067    /// Clear the set.
1068    pub fn clear(&mut self) {
1069        self.ids.clear();
1070    }
1071    /// Return the number of ids.
1072    pub fn len(&self) -> usize {
1073        self.ids.len()
1074    }
1075    /// Return whether empty.
1076    pub fn is_empty(&self) -> bool {
1077        self.ids.is_empty()
1078    }
1079}
1080/// A reference-counted expression pool that garbage-collects dead entries.
1081#[allow(dead_code)]
1082pub struct RcExprPool {
1083    entries: Vec<SharedCacheEntry>,
1084}
1085#[allow(dead_code)]
1086impl RcExprPool {
1087    /// Create an empty pool.
1088    pub fn new() -> Self {
1089        RcExprPool {
1090            entries: Vec::new(),
1091        }
1092    }
1093    /// Allocate a new entry and return its pool index.
1094    pub fn alloc(&mut self, hash: u64) -> usize {
1095        let id = self.entries.len() as u64;
1096        self.entries.push(SharedCacheEntry::new(id, hash));
1097        self.entries.len() - 1
1098    }
1099    /// Increment ref count for the entry at `idx`.
1100    pub fn inc_ref(&mut self, idx: usize) {
1101        if let Some(e) = self.entries.get_mut(idx) {
1102            e.inc_ref();
1103        }
1104    }
1105    /// Decrement ref count; returns true if still alive.
1106    pub fn dec_ref(&mut self, idx: usize) -> bool {
1107        if let Some(e) = self.entries.get_mut(idx) {
1108            e.dec_ref()
1109        } else {
1110            false
1111        }
1112    }
1113    /// Collect all dead entries and return their indices.
1114    pub fn collect_garbage(&mut self) -> Vec<usize> {
1115        self.entries
1116            .iter()
1117            .enumerate()
1118            .filter(|(_, e)| e.is_dead())
1119            .map(|(i, _)| i)
1120            .collect()
1121    }
1122    /// Return the total number of entries (including dead).
1123    pub fn len(&self) -> usize {
1124        self.entries.len()
1125    }
1126    /// Return whether the pool is empty.
1127    pub fn is_empty(&self) -> bool {
1128        self.entries.is_empty()
1129    }
1130    /// Return number of live entries.
1131    pub fn live_count(&self) -> usize {
1132        self.entries.iter().filter(|e| !e.is_dead()).count()
1133    }
1134}
1135/// A simple LRU-like eviction policy tracker for the expression cache.
1136#[allow(dead_code)]
1137pub struct EvictionTracker {
1138    capacity: usize,
1139    order: Vec<u64>,
1140}
1141#[allow(dead_code)]
1142impl EvictionTracker {
1143    /// Create an eviction tracker with a given capacity.
1144    pub fn new(capacity: usize) -> Self {
1145        EvictionTracker {
1146            capacity,
1147            order: Vec::with_capacity(capacity),
1148        }
1149    }
1150    /// Record an access for the given id; promotes it to most-recent.
1151    pub fn access(&mut self, id: u64) {
1152        if let Some(pos) = self.order.iter().position(|&x| x == id) {
1153            self.order.remove(pos);
1154        }
1155        self.order.push(id);
1156        while self.order.len() > self.capacity {
1157            self.order.remove(0);
1158        }
1159    }
1160    /// Return the least-recently-used id, if any.
1161    pub fn lru(&self) -> Option<u64> {
1162        self.order.first().copied()
1163    }
1164    /// Return the most-recently-used id, if any.
1165    pub fn mru(&self) -> Option<u64> {
1166        self.order.last().copied()
1167    }
1168    /// Return how many entries are tracked.
1169    pub fn len(&self) -> usize {
1170        self.order.len()
1171    }
1172    /// Return whether tracker is empty.
1173    pub fn is_empty(&self) -> bool {
1174        self.order.is_empty()
1175    }
1176    /// Evict and return the LRU entry.
1177    pub fn evict_lru(&mut self) -> Option<u64> {
1178        if self.order.is_empty() {
1179            None
1180        } else {
1181            Some(self.order.remove(0))
1182        }
1183    }
1184}
1185/// A generic counter that tracks min/max/sum for statistical summaries.
1186#[allow(dead_code)]
1187pub struct StatSummary {
1188    count: u64,
1189    sum: f64,
1190    min: f64,
1191    max: f64,
1192}
1193#[allow(dead_code)]
1194impl StatSummary {
1195    /// Creates an empty summary.
1196    pub fn new() -> Self {
1197        Self {
1198            count: 0,
1199            sum: 0.0,
1200            min: f64::INFINITY,
1201            max: f64::NEG_INFINITY,
1202        }
1203    }
1204    /// Records a sample.
1205    pub fn record(&mut self, val: f64) {
1206        self.count += 1;
1207        self.sum += val;
1208        if val < self.min {
1209            self.min = val;
1210        }
1211        if val > self.max {
1212            self.max = val;
1213        }
1214    }
1215    /// Returns the mean, or `None` if no samples.
1216    pub fn mean(&self) -> Option<f64> {
1217        if self.count == 0 {
1218            None
1219        } else {
1220            Some(self.sum / self.count as f64)
1221        }
1222    }
1223    /// Returns the minimum, or `None` if no samples.
1224    pub fn min(&self) -> Option<f64> {
1225        if self.count == 0 {
1226            None
1227        } else {
1228            Some(self.min)
1229        }
1230    }
1231    /// Returns the maximum, or `None` if no samples.
1232    pub fn max(&self) -> Option<f64> {
1233        if self.count == 0 {
1234            None
1235        } else {
1236            Some(self.max)
1237        }
1238    }
1239    /// Returns the count of recorded samples.
1240    pub fn count(&self) -> u64 {
1241        self.count
1242    }
1243}
1244/// A hierarchical configuration tree.
1245#[allow(dead_code)]
1246pub struct ConfigNode {
1247    key: String,
1248    value: Option<String>,
1249    children: Vec<ConfigNode>,
1250}
1251#[allow(dead_code)]
1252impl ConfigNode {
1253    /// Creates a leaf config node with a value.
1254    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
1255        Self {
1256            key: key.into(),
1257            value: Some(value.into()),
1258            children: Vec::new(),
1259        }
1260    }
1261    /// Creates a section node with children.
1262    pub fn section(key: impl Into<String>) -> Self {
1263        Self {
1264            key: key.into(),
1265            value: None,
1266            children: Vec::new(),
1267        }
1268    }
1269    /// Adds a child node.
1270    pub fn add_child(&mut self, child: ConfigNode) {
1271        self.children.push(child);
1272    }
1273    /// Returns the key.
1274    pub fn key(&self) -> &str {
1275        &self.key
1276    }
1277    /// Returns the value, or `None` for section nodes.
1278    pub fn value(&self) -> Option<&str> {
1279        self.value.as_deref()
1280    }
1281    /// Returns the number of children.
1282    pub fn num_children(&self) -> usize {
1283        self.children.len()
1284    }
1285    /// Looks up a dot-separated path.
1286    pub fn lookup(&self, path: &str) -> Option<&str> {
1287        let mut parts = path.splitn(2, '.');
1288        let head = parts.next()?;
1289        let tail = parts.next();
1290        if head != self.key {
1291            return None;
1292        }
1293        match tail {
1294            None => self.value.as_deref(),
1295            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1296        }
1297    }
1298    fn lookup_relative(&self, path: &str) -> Option<&str> {
1299        let mut parts = path.splitn(2, '.');
1300        let head = parts.next()?;
1301        let tail = parts.next();
1302        if head != self.key {
1303            return None;
1304        }
1305        match tail {
1306            None => self.value.as_deref(),
1307            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1308        }
1309    }
1310}
1311/// A set of rewrite rules.
1312#[allow(dead_code)]
1313pub struct RewriteRuleSet {
1314    rules: Vec<RewriteRule>,
1315}
1316#[allow(dead_code)]
1317impl RewriteRuleSet {
1318    /// Creates an empty rule set.
1319    pub fn new() -> Self {
1320        Self { rules: Vec::new() }
1321    }
1322    /// Adds a rule.
1323    pub fn add(&mut self, rule: RewriteRule) {
1324        self.rules.push(rule);
1325    }
1326    /// Returns the number of rules.
1327    pub fn len(&self) -> usize {
1328        self.rules.len()
1329    }
1330    /// Returns `true` if the set is empty.
1331    pub fn is_empty(&self) -> bool {
1332        self.rules.is_empty()
1333    }
1334    /// Returns all conditional rules.
1335    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
1336        self.rules.iter().filter(|r| r.conditional).collect()
1337    }
1338    /// Returns all unconditional rules.
1339    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
1340        self.rules.iter().filter(|r| !r.conditional).collect()
1341    }
1342    /// Looks up a rule by name.
1343    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
1344        self.rules.iter().find(|r| r.name == name)
1345    }
1346}
1347/// A simple stack-based calculator for arithmetic expressions.
1348#[allow(dead_code)]
1349pub struct StackCalc {
1350    stack: Vec<i64>,
1351}
1352#[allow(dead_code)]
1353impl StackCalc {
1354    /// Creates a new empty calculator.
1355    pub fn new() -> Self {
1356        Self { stack: Vec::new() }
1357    }
1358    /// Pushes an integer literal.
1359    pub fn push(&mut self, n: i64) {
1360        self.stack.push(n);
1361    }
1362    /// Adds the top two values.  Panics if fewer than two values.
1363    pub fn add(&mut self) {
1364        let b = self
1365            .stack
1366            .pop()
1367            .expect("stack must have at least two values for add");
1368        let a = self
1369            .stack
1370            .pop()
1371            .expect("stack must have at least two values for add");
1372        self.stack.push(a + b);
1373    }
1374    /// Subtracts top from second.
1375    pub fn sub(&mut self) {
1376        let b = self
1377            .stack
1378            .pop()
1379            .expect("stack must have at least two values for sub");
1380        let a = self
1381            .stack
1382            .pop()
1383            .expect("stack must have at least two values for sub");
1384        self.stack.push(a - b);
1385    }
1386    /// Multiplies the top two values.
1387    pub fn mul(&mut self) {
1388        let b = self
1389            .stack
1390            .pop()
1391            .expect("stack must have at least two values for mul");
1392        let a = self
1393            .stack
1394            .pop()
1395            .expect("stack must have at least two values for mul");
1396        self.stack.push(a * b);
1397    }
1398    /// Peeks the top value.
1399    pub fn peek(&self) -> Option<i64> {
1400        self.stack.last().copied()
1401    }
1402    /// Returns the stack depth.
1403    pub fn depth(&self) -> usize {
1404        self.stack.len()
1405    }
1406}
1407/// A versioned cache that associates a monotonic version number with each entry.
1408#[allow(dead_code)]
1409pub struct VersionedCache {
1410    entries: Vec<(u64, u64, u64)>,
1411    current_version: u64,
1412}
1413#[allow(dead_code)]
1414impl VersionedCache {
1415    /// Create a new versioned cache.
1416    pub fn new() -> Self {
1417        VersionedCache {
1418            entries: Vec::new(),
1419            current_version: 0,
1420        }
1421    }
1422    /// Bump the version (e.g., after a global environment change).
1423    pub fn bump_version(&mut self) {
1424        self.current_version += 1;
1425    }
1426    /// Insert with the current version.
1427    pub fn insert(&mut self, key: u64, val: u64) {
1428        let ver = self.current_version;
1429        if let Some(e) = self.entries.iter_mut().find(|(k, _, _)| *k == key) {
1430            e.1 = val;
1431            e.2 = ver;
1432        } else {
1433            self.entries.push((key, val, ver));
1434        }
1435    }
1436    /// Look up a value; returns None if the entry's version is stale.
1437    pub fn get(&self, key: u64) -> Option<u64> {
1438        self.entries
1439            .iter()
1440            .find(|(k, _, v)| *k == key && *v == self.current_version)
1441            .map(|(_, val, _)| *val)
1442    }
1443    /// Return the current version number.
1444    pub fn version(&self) -> u64 {
1445        self.current_version
1446    }
1447    /// Evict all entries from a previous version.
1448    pub fn evict_stale(&mut self) {
1449        let cur = self.current_version;
1450        self.entries.retain(|(_, _, v)| *v == cur);
1451    }
1452    /// Return the number of valid (current-version) entries.
1453    pub fn valid_count(&self) -> usize {
1454        let cur = self.current_version;
1455        self.entries.iter().filter(|(_, _, v)| *v == cur).count()
1456    }
1457}
1458/// A fixed-size sliding window that computes a running sum.
1459#[allow(dead_code)]
1460pub struct SlidingSum {
1461    window: Vec<f64>,
1462    capacity: usize,
1463    pos: usize,
1464    sum: f64,
1465    count: usize,
1466}
1467#[allow(dead_code)]
1468impl SlidingSum {
1469    /// Creates a sliding sum with the given window size.
1470    pub fn new(capacity: usize) -> Self {
1471        Self {
1472            window: vec![0.0; capacity],
1473            capacity,
1474            pos: 0,
1475            sum: 0.0,
1476            count: 0,
1477        }
1478    }
1479    /// Adds a value to the window, removing the oldest if full.
1480    pub fn push(&mut self, val: f64) {
1481        let oldest = self.window[self.pos];
1482        self.sum -= oldest;
1483        self.sum += val;
1484        self.window[self.pos] = val;
1485        self.pos = (self.pos + 1) % self.capacity;
1486        if self.count < self.capacity {
1487            self.count += 1;
1488        }
1489    }
1490    /// Returns the current window sum.
1491    pub fn sum(&self) -> f64 {
1492        self.sum
1493    }
1494    /// Returns the window mean, or `None` if empty.
1495    pub fn mean(&self) -> Option<f64> {
1496        if self.count == 0 {
1497            None
1498        } else {
1499            Some(self.sum / self.count as f64)
1500        }
1501    }
1502    /// Returns the current window size (number of valid elements).
1503    pub fn count(&self) -> usize {
1504        self.count
1505    }
1506}
1507/// A counter for tracking how many items are in each of `N` buckets.
1508#[allow(dead_code)]
1509pub struct BucketCounter<const N: usize> {
1510    buckets: [u64; N],
1511}
1512#[allow(dead_code)]
1513impl<const N: usize> BucketCounter<N> {
1514    /// Creates a zeroed bucket counter.
1515    pub const fn new() -> Self {
1516        Self { buckets: [0u64; N] }
1517    }
1518    /// Increments bucket `i`.
1519    pub fn inc(&mut self, i: usize) {
1520        if i < N {
1521            self.buckets[i] += 1;
1522        }
1523    }
1524    /// Returns the count for bucket `i`.
1525    pub fn get(&self, i: usize) -> u64 {
1526        if i < N {
1527            self.buckets[i]
1528        } else {
1529            0
1530        }
1531    }
1532    /// Returns the total count across all buckets.
1533    pub fn total(&self) -> u64 {
1534        self.buckets.iter().sum()
1535    }
1536    /// Returns the index of the bucket with the highest count.
1537    pub fn argmax(&self) -> usize {
1538        self.buckets
1539            .iter()
1540            .enumerate()
1541            .max_by_key(|(_, &v)| v)
1542            .map(|(i, _)| i)
1543            .unwrap_or(0)
1544    }
1545}
1546/// A flat list of substitution pairs `(from, to)`.
1547#[allow(dead_code)]
1548pub struct FlatSubstitution {
1549    pairs: Vec<(String, String)>,
1550}
1551#[allow(dead_code)]
1552impl FlatSubstitution {
1553    /// Creates an empty substitution.
1554    pub fn new() -> Self {
1555        Self { pairs: Vec::new() }
1556    }
1557    /// Adds a pair.
1558    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
1559        self.pairs.push((from.into(), to.into()));
1560    }
1561    /// Applies all substitutions to `s` (leftmost-first order).
1562    pub fn apply(&self, s: &str) -> String {
1563        let mut result = s.to_string();
1564        for (from, to) in &self.pairs {
1565            result = result.replace(from.as_str(), to.as_str());
1566        }
1567        result
1568    }
1569    /// Returns the number of pairs.
1570    pub fn len(&self) -> usize {
1571        self.pairs.len()
1572    }
1573    /// Returns `true` if empty.
1574    pub fn is_empty(&self) -> bool {
1575        self.pairs.is_empty()
1576    }
1577}
1578/// A trie-style path cache that maps expression path hashes to node ids.
1579#[allow(dead_code)]
1580pub struct PathCache {
1581    nodes: Vec<PathNode>,
1582}
1583#[allow(dead_code)]
1584impl PathCache {
1585    /// Create an empty path cache.
1586    pub fn new() -> Self {
1587        PathCache {
1588            nodes: vec![PathNode {
1589                key: 0,
1590                value: None,
1591                children: Vec::new(),
1592            }],
1593        }
1594    }
1595    /// Insert a path (sequence of u32 keys) mapping to a u64 id.
1596    pub fn insert(&mut self, path: &[u32], id: u64) {
1597        let mut node_idx = 0;
1598        for &step in path {
1599            let child_idx = self.nodes[node_idx]
1600                .children
1601                .iter()
1602                .copied()
1603                .find(|&c| self.nodes[c].key == step);
1604            match child_idx {
1605                Some(ci) => node_idx = ci,
1606                None => {
1607                    let new_idx = self.nodes.len();
1608                    self.nodes.push(PathNode {
1609                        key: step,
1610                        value: None,
1611                        children: Vec::new(),
1612                    });
1613                    self.nodes[node_idx].children.push(new_idx);
1614                    node_idx = new_idx;
1615                }
1616            }
1617        }
1618        self.nodes[node_idx].value = Some(id);
1619    }
1620    /// Look up a path.
1621    pub fn get(&self, path: &[u32]) -> Option<u64> {
1622        let mut node_idx = 0;
1623        for &step in path {
1624            let child_idx = self.nodes[node_idx]
1625                .children
1626                .iter()
1627                .copied()
1628                .find(|&c| self.nodes[c].key == step)?;
1629            node_idx = child_idx;
1630        }
1631        self.nodes[node_idx].value
1632    }
1633    /// Return total number of nodes in the trie.
1634    pub fn node_count(&self) -> usize {
1635        self.nodes.len()
1636    }
1637}
1638/// A counter that can measure elapsed time between snapshots.
1639#[allow(dead_code)]
1640pub struct Stopwatch {
1641    start: std::time::Instant,
1642    splits: Vec<f64>,
1643}
1644#[allow(dead_code)]
1645impl Stopwatch {
1646    /// Creates and starts a new stopwatch.
1647    pub fn start() -> Self {
1648        Self {
1649            start: std::time::Instant::now(),
1650            splits: Vec::new(),
1651        }
1652    }
1653    /// Records a split time (elapsed since start).
1654    pub fn split(&mut self) {
1655        self.splits.push(self.elapsed_ms());
1656    }
1657    /// Returns total elapsed milliseconds since start.
1658    pub fn elapsed_ms(&self) -> f64 {
1659        self.start.elapsed().as_secs_f64() * 1000.0
1660    }
1661    /// Returns all recorded split times.
1662    pub fn splits(&self) -> &[f64] {
1663        &self.splits
1664    }
1665    /// Returns the number of splits.
1666    pub fn num_splits(&self) -> usize {
1667        self.splits.len()
1668    }
1669}
1670/// A shared reference-counted expression cache entry.
1671#[allow(dead_code)]
1672#[derive(Debug, Clone)]
1673pub struct SharedCacheEntry {
1674    pub id: u64,
1675    pub hash: u64,
1676    pub ref_count: u32,
1677}
1678#[allow(dead_code)]
1679impl SharedCacheEntry {
1680    /// Create a new entry with ref count 1.
1681    pub fn new(id: u64, hash: u64) -> Self {
1682        SharedCacheEntry {
1683            id,
1684            hash,
1685            ref_count: 1,
1686        }
1687    }
1688    /// Increment the reference count.
1689    pub fn inc_ref(&mut self) {
1690        self.ref_count = self.ref_count.saturating_add(1);
1691    }
1692    /// Decrement the reference count. Returns true if still alive.
1693    pub fn dec_ref(&mut self) -> bool {
1694        if self.ref_count > 0 {
1695            self.ref_count -= 1;
1696        }
1697        self.ref_count > 0
1698    }
1699    /// Return true if this entry has no live references.
1700    pub fn is_dead(&self) -> bool {
1701        self.ref_count == 0
1702    }
1703}
1704/// A reusable scratch buffer for path computations.
1705#[allow(dead_code)]
1706pub struct PathBuf {
1707    components: Vec<String>,
1708}
1709#[allow(dead_code)]
1710impl PathBuf {
1711    /// Creates a new empty path buffer.
1712    pub fn new() -> Self {
1713        Self {
1714            components: Vec::new(),
1715        }
1716    }
1717    /// Pushes a component.
1718    pub fn push(&mut self, comp: impl Into<String>) {
1719        self.components.push(comp.into());
1720    }
1721    /// Pops the last component.
1722    pub fn pop(&mut self) {
1723        self.components.pop();
1724    }
1725    /// Returns the current path as a `/`-separated string.
1726    pub fn as_str(&self) -> String {
1727        self.components.join("/")
1728    }
1729    /// Returns the depth of the path.
1730    pub fn depth(&self) -> usize {
1731        self.components.len()
1732    }
1733    /// Clears the path.
1734    pub fn clear(&mut self) {
1735        self.components.clear();
1736    }
1737}
1738/// Memoization table mapping u64 keys to u64 values.
1739#[allow(dead_code)]
1740pub struct MemoTable {
1741    entries: Vec<(u64, u64)>,
1742}
1743#[allow(dead_code)]
1744impl MemoTable {
1745    /// Create a new empty memo table.
1746    pub fn new() -> Self {
1747        MemoTable {
1748            entries: Vec::new(),
1749        }
1750    }
1751    /// Insert a key→value pair; replaces if key already present.
1752    pub fn insert(&mut self, key: u64, val: u64) {
1753        if let Some(e) = self.entries.iter_mut().find(|(k, _)| *k == key) {
1754            e.1 = val;
1755        } else {
1756            self.entries.push((key, val));
1757        }
1758    }
1759    /// Look up a value by key.
1760    pub fn get(&self, key: u64) -> Option<u64> {
1761        self.entries
1762            .iter()
1763            .find(|(k, _)| *k == key)
1764            .map(|(_, v)| *v)
1765    }
1766    /// Remove a key; returns the old value if present.
1767    pub fn remove(&mut self, key: u64) -> Option<u64> {
1768        if let Some(pos) = self.entries.iter().position(|(k, _)| *k == key) {
1769            Some(self.entries.remove(pos).1)
1770        } else {
1771            None
1772        }
1773    }
1774    /// Number of entries.
1775    pub fn len(&self) -> usize {
1776        self.entries.len()
1777    }
1778    /// Return whether the table is empty.
1779    pub fn is_empty(&self) -> bool {
1780        self.entries.is_empty()
1781    }
1782    /// Clear all entries.
1783    pub fn clear(&mut self) {
1784        self.entries.clear();
1785    }
1786    /// Drain all entries into a vec.
1787    pub fn drain_all(&mut self) -> Vec<(u64, u64)> {
1788        let v = self.entries.clone();
1789        self.entries.clear();
1790        v
1791    }
1792}
1793/// Wrapper around `Expr` that implements `Hash` and `PartialEq` by structure.
1794///
1795/// This is used as the key in `ExprHashcons::expr_to_id` so that structurally
1796/// identical expressions map to the same slot.
1797#[derive(Clone, Debug)]
1798pub(crate) struct ExprKey(pub(crate) Expr);
1799/// Arena-like pool for `Expr` values backed by a hash-consing table.
1800///
1801/// `ExprPool` layers GC-root tracking on top of `ExprHashcons`.  Any
1802/// expression added via `add_root` (or subsequently marked with
1803/// `mark_root`) is considered live.
1804pub struct ExprPool {
1805    /// The underlying hash-consing table.
1806    hashcons: ExprHashcons,
1807    /// Root expression IDs (GC roots — considered permanently live).
1808    roots: Vec<ExprId>,
1809}
1810impl ExprPool {
1811    /// Create a new, empty pool.
1812    pub fn new() -> Self {
1813        ExprPool {
1814            hashcons: ExprHashcons::new(),
1815            roots: Vec::new(),
1816        }
1817    }
1818    /// Intern an expression without marking it as a root.
1819    pub fn add(&mut self, expr: Expr) -> ExprId {
1820        let (id, _) = self.hashcons.intern(expr);
1821        id
1822    }
1823    /// Intern an expression and mark it as a GC root.
1824    pub fn add_root(&mut self, expr: Expr) -> ExprId {
1825        let (id, _) = self.hashcons.intern(expr);
1826        self.roots.push(id);
1827        id
1828    }
1829    /// Look up an expression by ID.
1830    pub fn get(&self, id: ExprId) -> Option<&Expr> {
1831        self.hashcons.get(id)
1832    }
1833    /// Mark an existing interned expression as a GC root.
1834    ///
1835    /// If `id` is already a root it will appear twice in `roots`;
1836    /// `live_count` uses a deduplicated count so this is harmless.
1837    pub fn mark_root(&mut self, id: ExprId) {
1838        self.roots.push(id);
1839    }
1840    /// Return the number of distinct live (root) expressions.
1841    ///
1842    /// Duplicates in `roots` are ignored.
1843    pub fn live_count(&self) -> usize {
1844        let mut seen = std::collections::HashSet::new();
1845        for &id in &self.roots {
1846            seen.insert(id);
1847        }
1848        seen.len()
1849    }
1850    /// Return the total number of distinct expressions in the pool.
1851    pub fn total_count(&self) -> usize {
1852        self.hashcons.size()
1853    }
1854    /// Compute the deduplication ratio: hits / total intern calls.
1855    ///
1856    /// A value close to 1.0 means most `add`/`add_root` calls were
1857    /// satisfied from cache.
1858    pub fn dedup_ratio(&self) -> f64 {
1859        self.hashcons.hit_rate()
1860    }
1861    /// Look up the  for a structurally equal expression, if interned.
1862    pub fn get_id(&self, expr: &Expr) -> Option<ExprId> {
1863        self.hashcons.get_id(expr)
1864    }
1865}
1866/// A write-once cell.
1867#[allow(dead_code)]
1868pub struct WriteOnce<T> {
1869    value: std::cell::Cell<Option<T>>,
1870}
1871#[allow(dead_code)]
1872impl<T: Copy> WriteOnce<T> {
1873    /// Creates an empty write-once cell.
1874    pub fn new() -> Self {
1875        Self {
1876            value: std::cell::Cell::new(None),
1877        }
1878    }
1879    /// Writes a value.  Returns `false` if already written.
1880    pub fn write(&self, val: T) -> bool {
1881        if self.value.get().is_some() {
1882            return false;
1883        }
1884        self.value.set(Some(val));
1885        true
1886    }
1887    /// Returns the value if written.
1888    pub fn read(&self) -> Option<T> {
1889        self.value.get()
1890    }
1891    /// Returns `true` if the value has been written.
1892    pub fn is_written(&self) -> bool {
1893        self.value.get().is_some()
1894    }
1895}