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: crate::wall_clock::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: crate::wall_clock::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 = crate::wall_clock::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    /// Every earlier value, oldest first: version `n` is `earlier[n]`.
1010    earlier: Vec<T>,
1011    /// The latest value (version `earlier.len()`), declared last so it drops last.
1012    current: T,
1013}
1014#[allow(dead_code)]
1015impl<T: Clone> VersionedRecord<T> {
1016    /// Creates a new record with an initial value.
1017    pub fn new(initial: T) -> Self {
1018        Self {
1019            earlier: Vec::new(),
1020            current: initial,
1021        }
1022    }
1023    /// Updates the record with a new version.
1024    pub fn update(&mut self, val: T) {
1025        let previous = std::mem::replace(&mut self.current, val);
1026        self.earlier.push(previous);
1027    }
1028    /// Returns the current (latest) value.
1029    pub fn current(&self) -> &T {
1030        &self.current
1031    }
1032    /// Returns the value at version `n` (0-indexed), or `None`.
1033    pub fn at_version(&self, n: usize) -> Option<&T> {
1034        match self.earlier.get(n) {
1035            Some(value) => Some(value),
1036            None if n == self.earlier.len() => Some(&self.current),
1037            None => None,
1038        }
1039    }
1040    /// Returns the version number of the current value.
1041    pub fn version(&self) -> usize {
1042        self.earlier.len()
1043    }
1044    /// Returns `true` if more than one version exists.
1045    pub fn has_history(&self) -> bool {
1046        !self.earlier.is_empty()
1047    }
1048}
1049/// A cache invalidation set tracking which expression ids to evict.
1050#[allow(dead_code)]
1051pub struct InvalidationSet {
1052    ids: Vec<u64>,
1053}
1054#[allow(dead_code)]
1055impl InvalidationSet {
1056    /// Create an empty set.
1057    pub fn new() -> Self {
1058        InvalidationSet { ids: Vec::new() }
1059    }
1060    /// Add an id to invalidate.
1061    pub fn add(&mut self, id: u64) {
1062        if !self.ids.contains(&id) {
1063            self.ids.push(id);
1064        }
1065    }
1066    /// Return whether the set contains the given id.
1067    pub fn contains(&self, id: u64) -> bool {
1068        self.ids.contains(&id)
1069    }
1070    /// Return all ids to invalidate.
1071    pub fn ids(&self) -> &[u64] {
1072        &self.ids
1073    }
1074    /// Clear the set.
1075    pub fn clear(&mut self) {
1076        self.ids.clear();
1077    }
1078    /// Return the number of ids.
1079    pub fn len(&self) -> usize {
1080        self.ids.len()
1081    }
1082    /// Return whether empty.
1083    pub fn is_empty(&self) -> bool {
1084        self.ids.is_empty()
1085    }
1086}
1087/// A reference-counted expression pool that garbage-collects dead entries.
1088#[allow(dead_code)]
1089pub struct RcExprPool {
1090    entries: Vec<SharedCacheEntry>,
1091}
1092#[allow(dead_code)]
1093impl RcExprPool {
1094    /// Create an empty pool.
1095    pub fn new() -> Self {
1096        RcExprPool {
1097            entries: Vec::new(),
1098        }
1099    }
1100    /// Allocate a new entry and return its pool index.
1101    pub fn alloc(&mut self, hash: u64) -> usize {
1102        let id = self.entries.len() as u64;
1103        self.entries.push(SharedCacheEntry::new(id, hash));
1104        self.entries.len() - 1
1105    }
1106    /// Increment ref count for the entry at `idx`.
1107    pub fn inc_ref(&mut self, idx: usize) {
1108        if let Some(e) = self.entries.get_mut(idx) {
1109            e.inc_ref();
1110        }
1111    }
1112    /// Decrement ref count; returns true if still alive.
1113    pub fn dec_ref(&mut self, idx: usize) -> bool {
1114        if let Some(e) = self.entries.get_mut(idx) {
1115            e.dec_ref()
1116        } else {
1117            false
1118        }
1119    }
1120    /// Collect all dead entries and return their indices.
1121    pub fn collect_garbage(&mut self) -> Vec<usize> {
1122        self.entries
1123            .iter()
1124            .enumerate()
1125            .filter(|(_, e)| e.is_dead())
1126            .map(|(i, _)| i)
1127            .collect()
1128    }
1129    /// Return the total number of entries (including dead).
1130    pub fn len(&self) -> usize {
1131        self.entries.len()
1132    }
1133    /// Return whether the pool is empty.
1134    pub fn is_empty(&self) -> bool {
1135        self.entries.is_empty()
1136    }
1137    /// Return number of live entries.
1138    pub fn live_count(&self) -> usize {
1139        self.entries.iter().filter(|e| !e.is_dead()).count()
1140    }
1141}
1142/// A simple LRU-like eviction policy tracker for the expression cache.
1143#[allow(dead_code)]
1144pub struct EvictionTracker {
1145    capacity: usize,
1146    order: Vec<u64>,
1147}
1148#[allow(dead_code)]
1149impl EvictionTracker {
1150    /// Create an eviction tracker with a given capacity.
1151    pub fn new(capacity: usize) -> Self {
1152        EvictionTracker {
1153            capacity,
1154            order: Vec::with_capacity(capacity),
1155        }
1156    }
1157    /// Record an access for the given id; promotes it to most-recent.
1158    pub fn access(&mut self, id: u64) {
1159        if let Some(pos) = self.order.iter().position(|&x| x == id) {
1160            self.order.remove(pos);
1161        }
1162        self.order.push(id);
1163        while self.order.len() > self.capacity {
1164            self.order.remove(0);
1165        }
1166    }
1167    /// Return the least-recently-used id, if any.
1168    pub fn lru(&self) -> Option<u64> {
1169        self.order.first().copied()
1170    }
1171    /// Return the most-recently-used id, if any.
1172    pub fn mru(&self) -> Option<u64> {
1173        self.order.last().copied()
1174    }
1175    /// Return how many entries are tracked.
1176    pub fn len(&self) -> usize {
1177        self.order.len()
1178    }
1179    /// Return whether tracker is empty.
1180    pub fn is_empty(&self) -> bool {
1181        self.order.is_empty()
1182    }
1183    /// Evict and return the LRU entry.
1184    pub fn evict_lru(&mut self) -> Option<u64> {
1185        if self.order.is_empty() {
1186            None
1187        } else {
1188            Some(self.order.remove(0))
1189        }
1190    }
1191}
1192/// A generic counter that tracks min/max/sum for statistical summaries.
1193#[allow(dead_code)]
1194pub struct StatSummary {
1195    count: u64,
1196    sum: f64,
1197    min: f64,
1198    max: f64,
1199}
1200#[allow(dead_code)]
1201impl StatSummary {
1202    /// Creates an empty summary.
1203    pub fn new() -> Self {
1204        Self {
1205            count: 0,
1206            sum: 0.0,
1207            min: f64::INFINITY,
1208            max: f64::NEG_INFINITY,
1209        }
1210    }
1211    /// Records a sample.
1212    pub fn record(&mut self, val: f64) {
1213        self.count += 1;
1214        self.sum += val;
1215        if val < self.min {
1216            self.min = val;
1217        }
1218        if val > self.max {
1219            self.max = val;
1220        }
1221    }
1222    /// Returns the mean, or `None` if no samples.
1223    pub fn mean(&self) -> Option<f64> {
1224        if self.count == 0 {
1225            None
1226        } else {
1227            Some(self.sum / self.count as f64)
1228        }
1229    }
1230    /// Returns the minimum, or `None` if no samples.
1231    pub fn min(&self) -> Option<f64> {
1232        if self.count == 0 {
1233            None
1234        } else {
1235            Some(self.min)
1236        }
1237    }
1238    /// Returns the maximum, or `None` if no samples.
1239    pub fn max(&self) -> Option<f64> {
1240        if self.count == 0 {
1241            None
1242        } else {
1243            Some(self.max)
1244        }
1245    }
1246    /// Returns the count of recorded samples.
1247    pub fn count(&self) -> u64 {
1248        self.count
1249    }
1250}
1251/// A hierarchical configuration tree.
1252#[allow(dead_code)]
1253pub struct ConfigNode {
1254    key: String,
1255    value: Option<String>,
1256    children: Vec<ConfigNode>,
1257}
1258#[allow(dead_code)]
1259impl ConfigNode {
1260    /// Creates a leaf config node with a value.
1261    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
1262        Self {
1263            key: key.into(),
1264            value: Some(value.into()),
1265            children: Vec::new(),
1266        }
1267    }
1268    /// Creates a section node with children.
1269    pub fn section(key: impl Into<String>) -> Self {
1270        Self {
1271            key: key.into(),
1272            value: None,
1273            children: Vec::new(),
1274        }
1275    }
1276    /// Adds a child node.
1277    pub fn add_child(&mut self, child: ConfigNode) {
1278        self.children.push(child);
1279    }
1280    /// Returns the key.
1281    pub fn key(&self) -> &str {
1282        &self.key
1283    }
1284    /// Returns the value, or `None` for section nodes.
1285    pub fn value(&self) -> Option<&str> {
1286        self.value.as_deref()
1287    }
1288    /// Returns the number of children.
1289    pub fn num_children(&self) -> usize {
1290        self.children.len()
1291    }
1292    /// Looks up a dot-separated path.
1293    pub fn lookup(&self, path: &str) -> Option<&str> {
1294        let mut parts = path.splitn(2, '.');
1295        let head = parts.next()?;
1296        let tail = parts.next();
1297        if head != self.key {
1298            return None;
1299        }
1300        match tail {
1301            None => self.value.as_deref(),
1302            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1303        }
1304    }
1305    fn lookup_relative(&self, path: &str) -> Option<&str> {
1306        let mut parts = path.splitn(2, '.');
1307        let head = parts.next()?;
1308        let tail = parts.next();
1309        if head != self.key {
1310            return None;
1311        }
1312        match tail {
1313            None => self.value.as_deref(),
1314            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1315        }
1316    }
1317}
1318/// A set of rewrite rules.
1319#[allow(dead_code)]
1320pub struct RewriteRuleSet {
1321    rules: Vec<RewriteRule>,
1322}
1323#[allow(dead_code)]
1324impl RewriteRuleSet {
1325    /// Creates an empty rule set.
1326    pub fn new() -> Self {
1327        Self { rules: Vec::new() }
1328    }
1329    /// Adds a rule.
1330    pub fn add(&mut self, rule: RewriteRule) {
1331        self.rules.push(rule);
1332    }
1333    /// Returns the number of rules.
1334    pub fn len(&self) -> usize {
1335        self.rules.len()
1336    }
1337    /// Returns `true` if the set is empty.
1338    pub fn is_empty(&self) -> bool {
1339        self.rules.is_empty()
1340    }
1341    /// Returns all conditional rules.
1342    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
1343        self.rules.iter().filter(|r| r.conditional).collect()
1344    }
1345    /// Returns all unconditional rules.
1346    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
1347        self.rules.iter().filter(|r| !r.conditional).collect()
1348    }
1349    /// Looks up a rule by name.
1350    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
1351        self.rules.iter().find(|r| r.name == name)
1352    }
1353}
1354/// A simple stack-based calculator for arithmetic expressions.
1355#[allow(dead_code)]
1356pub struct StackCalc {
1357    stack: Vec<i64>,
1358}
1359#[allow(dead_code)]
1360impl StackCalc {
1361    /// Creates a new empty calculator.
1362    pub fn new() -> Self {
1363        Self { stack: Vec::new() }
1364    }
1365    /// Pushes an integer literal.
1366    pub fn push(&mut self, n: i64) {
1367        self.stack.push(n);
1368    }
1369    /// Replaces the top two values, `b` on top of `a`, with `op(a, b)` and
1370    /// returns it.  Returns `None`, leaving the stack unchanged, if it holds
1371    /// fewer than two values.
1372    fn apply_binary(&mut self, op: impl FnOnce(i64, i64) -> i64) -> Option<i64> {
1373        let (rest_len, a, b) = match self.stack[..] {
1374            [ref rest @ .., a, b] => (rest.len(), a, b),
1375            _ => return None,
1376        };
1377        let value = op(a, b);
1378        self.stack.truncate(rest_len);
1379        self.stack.push(value);
1380        Some(value)
1381    }
1382    /// Adds the top two values and returns the sum, which replaces them.
1383    /// Returns `None`, leaving the stack unchanged, if it holds fewer than
1384    /// two values.
1385    pub fn add(&mut self) -> Option<i64> {
1386        self.apply_binary(|a, b| a + b)
1387    }
1388    /// Subtracts top from second and returns the difference, which replaces
1389    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
1390    /// than two values.
1391    pub fn sub(&mut self) -> Option<i64> {
1392        self.apply_binary(|a, b| a - b)
1393    }
1394    /// Multiplies the top two values and returns the product, which replaces
1395    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
1396    /// than two values.
1397    pub fn mul(&mut self) -> Option<i64> {
1398        self.apply_binary(|a, b| a * b)
1399    }
1400    /// Peeks the top value.
1401    pub fn peek(&self) -> Option<i64> {
1402        self.stack.last().copied()
1403    }
1404    /// Returns the stack depth.
1405    pub fn depth(&self) -> usize {
1406        self.stack.len()
1407    }
1408}
1409/// A versioned cache that associates a monotonic version number with each entry.
1410#[allow(dead_code)]
1411pub struct VersionedCache {
1412    entries: Vec<(u64, u64, u64)>,
1413    current_version: u64,
1414}
1415#[allow(dead_code)]
1416impl VersionedCache {
1417    /// Create a new versioned cache.
1418    pub fn new() -> Self {
1419        VersionedCache {
1420            entries: Vec::new(),
1421            current_version: 0,
1422        }
1423    }
1424    /// Bump the version (e.g., after a global environment change).
1425    pub fn bump_version(&mut self) {
1426        self.current_version += 1;
1427    }
1428    /// Insert with the current version.
1429    pub fn insert(&mut self, key: u64, val: u64) {
1430        let ver = self.current_version;
1431        if let Some(e) = self.entries.iter_mut().find(|(k, _, _)| *k == key) {
1432            e.1 = val;
1433            e.2 = ver;
1434        } else {
1435            self.entries.push((key, val, ver));
1436        }
1437    }
1438    /// Look up a value; returns None if the entry's version is stale.
1439    pub fn get(&self, key: u64) -> Option<u64> {
1440        self.entries
1441            .iter()
1442            .find(|(k, _, v)| *k == key && *v == self.current_version)
1443            .map(|(_, val, _)| *val)
1444    }
1445    /// Return the current version number.
1446    pub fn version(&self) -> u64 {
1447        self.current_version
1448    }
1449    /// Evict all entries from a previous version.
1450    pub fn evict_stale(&mut self) {
1451        let cur = self.current_version;
1452        self.entries.retain(|(_, _, v)| *v == cur);
1453    }
1454    /// Return the number of valid (current-version) entries.
1455    pub fn valid_count(&self) -> usize {
1456        let cur = self.current_version;
1457        self.entries.iter().filter(|(_, _, v)| *v == cur).count()
1458    }
1459}
1460/// A fixed-size sliding window that computes a running sum.
1461#[allow(dead_code)]
1462pub struct SlidingSum {
1463    window: Vec<f64>,
1464    capacity: usize,
1465    pos: usize,
1466    sum: f64,
1467    count: usize,
1468}
1469#[allow(dead_code)]
1470impl SlidingSum {
1471    /// Creates a sliding sum with the given window size.
1472    pub fn new(capacity: usize) -> Self {
1473        Self {
1474            window: vec![0.0; capacity],
1475            capacity,
1476            pos: 0,
1477            sum: 0.0,
1478            count: 0,
1479        }
1480    }
1481    /// Adds a value to the window, removing the oldest if full.
1482    pub fn push(&mut self, val: f64) {
1483        let oldest = self.window[self.pos];
1484        self.sum -= oldest;
1485        self.sum += val;
1486        self.window[self.pos] = val;
1487        self.pos = (self.pos + 1) % self.capacity;
1488        if self.count < self.capacity {
1489            self.count += 1;
1490        }
1491    }
1492    /// Returns the current window sum.
1493    pub fn sum(&self) -> f64 {
1494        self.sum
1495    }
1496    /// Returns the window mean, or `None` if empty.
1497    pub fn mean(&self) -> Option<f64> {
1498        if self.count == 0 {
1499            None
1500        } else {
1501            Some(self.sum / self.count as f64)
1502        }
1503    }
1504    /// Returns the current window size (number of valid elements).
1505    pub fn count(&self) -> usize {
1506        self.count
1507    }
1508}
1509/// A counter for tracking how many items are in each of `N` buckets.
1510#[allow(dead_code)]
1511pub struct BucketCounter<const N: usize> {
1512    buckets: [u64; N],
1513}
1514#[allow(dead_code)]
1515impl<const N: usize> BucketCounter<N> {
1516    /// Creates a zeroed bucket counter.
1517    pub const fn new() -> Self {
1518        Self { buckets: [0u64; N] }
1519    }
1520    /// Increments bucket `i`.
1521    pub fn inc(&mut self, i: usize) {
1522        if i < N {
1523            self.buckets[i] += 1;
1524        }
1525    }
1526    /// Returns the count for bucket `i`.
1527    pub fn get(&self, i: usize) -> u64 {
1528        if i < N {
1529            self.buckets[i]
1530        } else {
1531            0
1532        }
1533    }
1534    /// Returns the total count across all buckets.
1535    pub fn total(&self) -> u64 {
1536        self.buckets.iter().sum()
1537    }
1538    /// Returns the index of the bucket with the highest count.
1539    pub fn argmax(&self) -> usize {
1540        self.buckets
1541            .iter()
1542            .enumerate()
1543            .max_by_key(|(_, &v)| v)
1544            .map(|(i, _)| i)
1545            .unwrap_or(0)
1546    }
1547}
1548/// A flat list of substitution pairs `(from, to)`.
1549#[allow(dead_code)]
1550pub struct FlatSubstitution {
1551    pairs: Vec<(String, String)>,
1552}
1553#[allow(dead_code)]
1554impl FlatSubstitution {
1555    /// Creates an empty substitution.
1556    pub fn new() -> Self {
1557        Self { pairs: Vec::new() }
1558    }
1559    /// Adds a pair.
1560    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
1561        self.pairs.push((from.into(), to.into()));
1562    }
1563    /// Applies all substitutions to `s` (leftmost-first order).
1564    pub fn apply(&self, s: &str) -> String {
1565        let mut result = s.to_string();
1566        for (from, to) in &self.pairs {
1567            result = result.replace(from.as_str(), to.as_str());
1568        }
1569        result
1570    }
1571    /// Returns the number of pairs.
1572    pub fn len(&self) -> usize {
1573        self.pairs.len()
1574    }
1575    /// Returns `true` if empty.
1576    pub fn is_empty(&self) -> bool {
1577        self.pairs.is_empty()
1578    }
1579}
1580/// A trie-style path cache that maps expression path hashes to node ids.
1581#[allow(dead_code)]
1582pub struct PathCache {
1583    nodes: Vec<PathNode>,
1584}
1585#[allow(dead_code)]
1586impl PathCache {
1587    /// Create an empty path cache.
1588    pub fn new() -> Self {
1589        PathCache {
1590            nodes: vec![PathNode {
1591                key: 0,
1592                value: None,
1593                children: Vec::new(),
1594            }],
1595        }
1596    }
1597    /// Insert a path (sequence of u32 keys) mapping to a u64 id.
1598    pub fn insert(&mut self, path: &[u32], id: u64) {
1599        let mut node_idx = 0;
1600        for &step in path {
1601            let child_idx = self.nodes[node_idx]
1602                .children
1603                .iter()
1604                .copied()
1605                .find(|&c| self.nodes[c].key == step);
1606            match child_idx {
1607                Some(ci) => node_idx = ci,
1608                None => {
1609                    let new_idx = self.nodes.len();
1610                    self.nodes.push(PathNode {
1611                        key: step,
1612                        value: None,
1613                        children: Vec::new(),
1614                    });
1615                    self.nodes[node_idx].children.push(new_idx);
1616                    node_idx = new_idx;
1617                }
1618            }
1619        }
1620        self.nodes[node_idx].value = Some(id);
1621    }
1622    /// Look up a path.
1623    pub fn get(&self, path: &[u32]) -> Option<u64> {
1624        let mut node_idx = 0;
1625        for &step in path {
1626            let child_idx = self.nodes[node_idx]
1627                .children
1628                .iter()
1629                .copied()
1630                .find(|&c| self.nodes[c].key == step)?;
1631            node_idx = child_idx;
1632        }
1633        self.nodes[node_idx].value
1634    }
1635    /// Return total number of nodes in the trie.
1636    pub fn node_count(&self) -> usize {
1637        self.nodes.len()
1638    }
1639}
1640/// A counter that can measure elapsed time between snapshots.
1641#[allow(dead_code)]
1642pub struct Stopwatch {
1643    start: crate::wall_clock::Instant,
1644    splits: Vec<f64>,
1645}
1646#[allow(dead_code)]
1647impl Stopwatch {
1648    /// Creates and starts a new stopwatch.
1649    pub fn start() -> Self {
1650        Self {
1651            start: crate::wall_clock::Instant::now(),
1652            splits: Vec::new(),
1653        }
1654    }
1655    /// Records a split time (elapsed since start).
1656    pub fn split(&mut self) {
1657        self.splits.push(self.elapsed_ms());
1658    }
1659    /// Returns total elapsed milliseconds since start.
1660    pub fn elapsed_ms(&self) -> f64 {
1661        self.start.elapsed().as_secs_f64() * 1000.0
1662    }
1663    /// Returns all recorded split times.
1664    pub fn splits(&self) -> &[f64] {
1665        &self.splits
1666    }
1667    /// Returns the number of splits.
1668    pub fn num_splits(&self) -> usize {
1669        self.splits.len()
1670    }
1671}
1672/// A shared reference-counted expression cache entry.
1673#[allow(dead_code)]
1674#[derive(Debug, Clone)]
1675pub struct SharedCacheEntry {
1676    pub id: u64,
1677    pub hash: u64,
1678    pub ref_count: u32,
1679}
1680#[allow(dead_code)]
1681impl SharedCacheEntry {
1682    /// Create a new entry with ref count 1.
1683    pub fn new(id: u64, hash: u64) -> Self {
1684        SharedCacheEntry {
1685            id,
1686            hash,
1687            ref_count: 1,
1688        }
1689    }
1690    /// Increment the reference count.
1691    pub fn inc_ref(&mut self) {
1692        self.ref_count = self.ref_count.saturating_add(1);
1693    }
1694    /// Decrement the reference count. Returns true if still alive.
1695    pub fn dec_ref(&mut self) -> bool {
1696        if self.ref_count > 0 {
1697            self.ref_count -= 1;
1698        }
1699        self.ref_count > 0
1700    }
1701    /// Return true if this entry has no live references.
1702    pub fn is_dead(&self) -> bool {
1703        self.ref_count == 0
1704    }
1705}
1706/// A reusable scratch buffer for path computations.
1707#[allow(dead_code)]
1708pub struct PathBuf {
1709    components: Vec<String>,
1710}
1711#[allow(dead_code)]
1712impl PathBuf {
1713    /// Creates a new empty path buffer.
1714    pub fn new() -> Self {
1715        Self {
1716            components: Vec::new(),
1717        }
1718    }
1719    /// Pushes a component.
1720    pub fn push(&mut self, comp: impl Into<String>) {
1721        self.components.push(comp.into());
1722    }
1723    /// Pops the last component.
1724    pub fn pop(&mut self) {
1725        self.components.pop();
1726    }
1727    /// Returns the current path as a `/`-separated string.
1728    pub fn as_str(&self) -> String {
1729        self.components.join("/")
1730    }
1731    /// Returns the depth of the path.
1732    pub fn depth(&self) -> usize {
1733        self.components.len()
1734    }
1735    /// Clears the path.
1736    pub fn clear(&mut self) {
1737        self.components.clear();
1738    }
1739}
1740/// Memoization table mapping u64 keys to u64 values.
1741#[allow(dead_code)]
1742pub struct MemoTable {
1743    entries: Vec<(u64, u64)>,
1744}
1745#[allow(dead_code)]
1746impl MemoTable {
1747    /// Create a new empty memo table.
1748    pub fn new() -> Self {
1749        MemoTable {
1750            entries: Vec::new(),
1751        }
1752    }
1753    /// Insert a key→value pair; replaces if key already present.
1754    pub fn insert(&mut self, key: u64, val: u64) {
1755        if let Some(e) = self.entries.iter_mut().find(|(k, _)| *k == key) {
1756            e.1 = val;
1757        } else {
1758            self.entries.push((key, val));
1759        }
1760    }
1761    /// Look up a value by key.
1762    pub fn get(&self, key: u64) -> Option<u64> {
1763        self.entries
1764            .iter()
1765            .find(|(k, _)| *k == key)
1766            .map(|(_, v)| *v)
1767    }
1768    /// Remove a key; returns the old value if present.
1769    pub fn remove(&mut self, key: u64) -> Option<u64> {
1770        if let Some(pos) = self.entries.iter().position(|(k, _)| *k == key) {
1771            Some(self.entries.remove(pos).1)
1772        } else {
1773            None
1774        }
1775    }
1776    /// Number of entries.
1777    pub fn len(&self) -> usize {
1778        self.entries.len()
1779    }
1780    /// Return whether the table is empty.
1781    pub fn is_empty(&self) -> bool {
1782        self.entries.is_empty()
1783    }
1784    /// Clear all entries.
1785    pub fn clear(&mut self) {
1786        self.entries.clear();
1787    }
1788    /// Drain all entries into a vec.
1789    pub fn drain_all(&mut self) -> Vec<(u64, u64)> {
1790        let v = self.entries.clone();
1791        self.entries.clear();
1792        v
1793    }
1794}
1795/// Wrapper around `Expr` that implements `Hash` and `PartialEq` by structure.
1796///
1797/// This is used as the key in `ExprHashcons::expr_to_id` so that structurally
1798/// identical expressions map to the same slot.
1799#[derive(Clone, Debug)]
1800pub(crate) struct ExprKey(pub(crate) Expr);
1801/// Arena-like pool for `Expr` values backed by a hash-consing table.
1802///
1803/// `ExprPool` layers GC-root tracking on top of `ExprHashcons`.  Any
1804/// expression added via `add_root` (or subsequently marked with
1805/// `mark_root`) is considered live.
1806pub struct ExprPool {
1807    /// The underlying hash-consing table.
1808    hashcons: ExprHashcons,
1809    /// Root expression IDs (GC roots — considered permanently live).
1810    roots: Vec<ExprId>,
1811}
1812impl ExprPool {
1813    /// Create a new, empty pool.
1814    pub fn new() -> Self {
1815        ExprPool {
1816            hashcons: ExprHashcons::new(),
1817            roots: Vec::new(),
1818        }
1819    }
1820    /// Intern an expression without marking it as a root.
1821    pub fn add(&mut self, expr: Expr) -> ExprId {
1822        let (id, _) = self.hashcons.intern(expr);
1823        id
1824    }
1825    /// Intern an expression and mark it as a GC root.
1826    pub fn add_root(&mut self, expr: Expr) -> ExprId {
1827        let (id, _) = self.hashcons.intern(expr);
1828        self.roots.push(id);
1829        id
1830    }
1831    /// Look up an expression by ID.
1832    pub fn get(&self, id: ExprId) -> Option<&Expr> {
1833        self.hashcons.get(id)
1834    }
1835    /// Mark an existing interned expression as a GC root.
1836    ///
1837    /// If `id` is already a root it will appear twice in `roots`;
1838    /// `live_count` uses a deduplicated count so this is harmless.
1839    pub fn mark_root(&mut self, id: ExprId) {
1840        self.roots.push(id);
1841    }
1842    /// Return the number of distinct live (root) expressions.
1843    ///
1844    /// Duplicates in `roots` are ignored.
1845    pub fn live_count(&self) -> usize {
1846        let mut seen = std::collections::HashSet::new();
1847        for &id in &self.roots {
1848            seen.insert(id);
1849        }
1850        seen.len()
1851    }
1852    /// Return the total number of distinct expressions in the pool.
1853    pub fn total_count(&self) -> usize {
1854        self.hashcons.size()
1855    }
1856    /// Compute the deduplication ratio: hits / total intern calls.
1857    ///
1858    /// A value close to 1.0 means most `add`/`add_root` calls were
1859    /// satisfied from cache.
1860    pub fn dedup_ratio(&self) -> f64 {
1861        self.hashcons.hit_rate()
1862    }
1863    /// Look up the  for a structurally equal expression, if interned.
1864    pub fn get_id(&self, expr: &Expr) -> Option<ExprId> {
1865        self.hashcons.get_id(expr)
1866    }
1867}
1868/// A write-once cell.
1869#[allow(dead_code)]
1870pub struct WriteOnce<T> {
1871    value: std::cell::Cell<Option<T>>,
1872}
1873#[allow(dead_code)]
1874impl<T: Copy> WriteOnce<T> {
1875    /// Creates an empty write-once cell.
1876    pub fn new() -> Self {
1877        Self {
1878            value: std::cell::Cell::new(None),
1879        }
1880    }
1881    /// Writes a value.  Returns `false` if already written.
1882    pub fn write(&self, val: T) -> bool {
1883        if self.value.get().is_some() {
1884            return false;
1885        }
1886        self.value.set(Some(val));
1887        true
1888    }
1889    /// Returns the value if written.
1890    pub fn read(&self) -> Option<T> {
1891        self.value.get()
1892    }
1893    /// Returns `true` if the value has been written.
1894    pub fn is_written(&self) -> bool {
1895        self.value.get().is_some()
1896    }
1897}