Skip to main content

oxilean_kernel/equiv_manager/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use crate::Expr;
7use std::collections::{HashMap, HashSet, VecDeque};
8
9/// Maps expression hashes to equivalence class IDs.
10#[allow(dead_code)]
11pub struct EquivalenceTable {
12    hash_to_id: std::collections::HashMap<u64, usize>,
13    uf: UnionFind,
14    next_id: usize,
15}
16#[allow(dead_code)]
17impl EquivalenceTable {
18    /// Creates an empty table with pre-allocated capacity.
19    pub fn new(capacity: usize) -> Self {
20        Self {
21            hash_to_id: std::collections::HashMap::with_capacity(capacity),
22            uf: UnionFind::new(capacity),
23            next_id: 0,
24        }
25    }
26    /// Registers a hash and returns its class ID.
27    pub fn register(&mut self, hash: u64) -> usize {
28        if let Some(&id) = self.hash_to_id.get(&hash) {
29            return id;
30        }
31        let id = self.next_id;
32        self.next_id += 1;
33        self.hash_to_id.insert(hash, id);
34        id
35    }
36    /// Merges the classes of `h1` and `h2`.
37    pub fn merge(&mut self, h1: u64, h2: u64) {
38        let id1 = self.register(h1);
39        let id2 = self.register(h2);
40        self.uf.union(id1, id2);
41    }
42    /// Returns `true` if `h1` and `h2` are in the same class.
43    pub fn equiv(&mut self, h1: u64, h2: u64) -> bool {
44        let id1 = self.register(h1);
45        let id2 = self.register(h2);
46        self.uf.same(id1, id2)
47    }
48}
49/// A monotonic timestamp in microseconds.
50#[allow(dead_code)]
51#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
52pub struct Timestamp(u64);
53#[allow(dead_code)]
54impl Timestamp {
55    /// Creates a timestamp from microseconds.
56    pub const fn from_us(us: u64) -> Self {
57        Self(us)
58    }
59    /// Returns the timestamp in microseconds.
60    pub fn as_us(self) -> u64 {
61        self.0
62    }
63    /// Returns the duration between two timestamps.
64    pub fn elapsed_since(self, earlier: Timestamp) -> u64 {
65        self.0.saturating_sub(earlier.0)
66    }
67}
68/// A generation counter for validity tracking.
69#[allow(dead_code)]
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub struct Generation(u32);
72#[allow(dead_code)]
73impl Generation {
74    /// The initial generation.
75    pub const INITIAL: Generation = Generation(0);
76    /// Advances to the next generation.
77    pub fn advance(self) -> Generation {
78        Generation(self.0 + 1)
79    }
80    /// Returns the raw generation number.
81    pub fn number(self) -> u32 {
82        self.0
83    }
84}
85/// A clock that measures elapsed time in a loop.
86#[allow(dead_code)]
87pub struct LoopClock {
88    start: crate::wall_clock::Instant,
89    iters: u64,
90}
91#[allow(dead_code)]
92impl LoopClock {
93    /// Starts the clock.
94    pub fn start() -> Self {
95        Self {
96            start: crate::wall_clock::Instant::now(),
97            iters: 0,
98        }
99    }
100    /// Records one iteration.
101    pub fn tick(&mut self) {
102        self.iters += 1;
103    }
104    /// Returns the elapsed time in microseconds.
105    pub fn elapsed_us(&self) -> f64 {
106        self.start.elapsed().as_secs_f64() * 1e6
107    }
108    /// Returns the average microseconds per iteration.
109    pub fn avg_us_per_iter(&self) -> f64 {
110        if self.iters == 0 {
111            return 0.0;
112        }
113        self.elapsed_us() / self.iters as f64
114    }
115    /// Returns the number of iterations.
116    pub fn iters(&self) -> u64 {
117        self.iters
118    }
119}
120/// A bidirectional map between two types.
121#[allow(dead_code)]
122pub struct BiMap<A: std::hash::Hash + Eq + Clone, B: std::hash::Hash + Eq + Clone> {
123    forward: std::collections::HashMap<A, B>,
124    backward: std::collections::HashMap<B, A>,
125}
126#[allow(dead_code)]
127impl<A: std::hash::Hash + Eq + Clone, B: std::hash::Hash + Eq + Clone> BiMap<A, B> {
128    /// Creates an empty bidirectional map.
129    pub fn new() -> Self {
130        Self {
131            forward: std::collections::HashMap::new(),
132            backward: std::collections::HashMap::new(),
133        }
134    }
135    /// Inserts a pair `(a, b)`.
136    pub fn insert(&mut self, a: A, b: B) {
137        self.forward.insert(a.clone(), b.clone());
138        self.backward.insert(b, a);
139    }
140    /// Looks up `b` for a given `a`.
141    pub fn get_b(&self, a: &A) -> Option<&B> {
142        self.forward.get(a)
143    }
144    /// Looks up `a` for a given `b`.
145    pub fn get_a(&self, b: &B) -> Option<&A> {
146        self.backward.get(b)
147    }
148    /// Returns the number of pairs.
149    pub fn len(&self) -> usize {
150        self.forward.len()
151    }
152    /// Returns `true` if empty.
153    pub fn is_empty(&self) -> bool {
154        self.forward.is_empty()
155    }
156}
157/// A pair of values useful for before/after comparisons.
158#[allow(dead_code)]
159#[allow(missing_docs)]
160pub struct BeforeAfter<T> {
161    /// Value before the transformation.
162    pub before: T,
163    /// Value after the transformation.
164    pub after: T,
165}
166#[allow(dead_code)]
167impl<T: PartialEq> BeforeAfter<T> {
168    /// Creates a new before/after pair.
169    pub fn new(before: T, after: T) -> Self {
170        Self { before, after }
171    }
172    /// Returns `true` if before equals after (no change).
173    pub fn unchanged(&self) -> bool {
174        self.before == self.after
175    }
176}
177/// A simple LRU cache backed by a linked list + hash map.
178#[allow(dead_code)]
179pub struct SimpleLruCache<K: std::hash::Hash + Eq + Clone, V: Clone> {
180    capacity: usize,
181    map: std::collections::HashMap<K, usize>,
182    keys: Vec<K>,
183    vals: Vec<V>,
184    order: Vec<usize>,
185}
186#[allow(dead_code)]
187impl<K: std::hash::Hash + Eq + Clone, V: Clone> SimpleLruCache<K, V> {
188    /// Creates a new LRU cache with the given capacity.
189    pub fn new(capacity: usize) -> Self {
190        assert!(capacity > 0);
191        Self {
192            capacity,
193            map: std::collections::HashMap::new(),
194            keys: Vec::new(),
195            vals: Vec::new(),
196            order: Vec::new(),
197        }
198    }
199    /// Inserts or updates a key-value pair.
200    pub fn put(&mut self, key: K, val: V) {
201        // `keys`, `vals` and `order` keep one length, with `order` holding
202        // each slot index once (most recently used first) and the length at
203        // most `capacity`, also when a call unwinds out of user code (`Clone`,
204        // `Hash` or `Eq` of `K`, the drop of a `K` or a `V`): `new` starts
205        // them empty, and every change below to a length or to `order` is a
206        // step that runs no user code and cannot fail, with its branch's user
207        // code (and the `reserve`s, which may panic) before the first such
208        // step or after the last. Every index in `map` is a slot index.
209        if let Some(&idx) = self.map.get(&key) {
210            // `map.get` ran `Hash` / `Eq` before any change. The old value's
211            // drop runs in this assignment and changes no length.
212            self.vals[idx] = val;
213            // `idx` is in `order` once, so `retain` removes one index and
214            // `insert` puts it back without growing the buffer.
215            self.order.retain(|&x| x != idx);
216            self.order.insert(0, idx);
217            return;
218        }
219        match self.order.last() {
220            // A full cache has `order.len() == keys.len() >= capacity > 0`
221            // (`new` asserts `capacity > 0`), so its least recently used slot
222            // is `order.last()`.
223            Some(&evict_idx) if self.keys.len() >= self.capacity => {
224                // The user code that comes before any change: the clone that
225                // goes into the map, and `Hash` / `Eq` in removing the evicted
226                // key from the map (a panic there leaves the map as it was).
227                // The removed key is held and dropped at the end.
228                let map_key = key.clone();
229                let removed = self.map.remove_entry(&self.keys[evict_idx]);
230                // Moves that run no user code: the slot takes the new key and
231                // value, and `rotate_right` takes the evicted index from the
232                // back of `order` to the front. No length changes.
233                let old_key = std::mem::replace(&mut self.keys[evict_idx], key);
234                let old_val = std::mem::replace(&mut self.vals[evict_idx], val);
235                self.order.rotate_right(1);
236                // The user code that comes after: `Hash` / `Eq` in the map's
237                // insertion, then the drops of the evicted key and value.
238                self.map.insert(map_key, evict_idx);
239                drop((removed, old_key, old_val));
240            }
241            // Not full: a new slot. (With the lengths equal, `order` is never
242            // empty while `keys.len() >= capacity`.)
243            _ => {
244                let map_key = key.clone();
245                let idx = self.keys.len();
246                // Reserving first, the three appends below allocate nothing
247                // and cannot fail, so the lists grow together; `Hash` / `Eq`
248                // in the map's insertion run after them.
249                self.keys.reserve(1);
250                self.vals.reserve(1);
251                self.order.reserve(1);
252                self.keys.push(key);
253                self.vals.push(val);
254                self.order.insert(0, idx);
255                self.map.insert(map_key, idx);
256            }
257        }
258    }
259    /// Returns a reference to the value for `key`, promoting it.
260    pub fn get(&mut self, key: &K) -> Option<&V> {
261        // `map.get` runs `Hash` / `Eq` before any change; `idx` is in `order`
262        // once (see `put`), so `retain` and `insert` change no length.
263        let idx = *self.map.get(key)?;
264        self.order.retain(|&x| x != idx);
265        self.order.insert(0, idx);
266        Some(&self.vals[idx])
267    }
268    /// Returns the number of entries.
269    pub fn len(&self) -> usize {
270        self.keys.len()
271    }
272    /// Returns `true` if empty.
273    pub fn is_empty(&self) -> bool {
274        self.keys.is_empty()
275    }
276}
277/// A simple sparse bit set.
278#[allow(dead_code)]
279pub struct SparseBitSet {
280    words: Vec<u64>,
281}
282#[allow(dead_code)]
283impl SparseBitSet {
284    /// Creates a new bit set that can hold at least `capacity` bits.
285    pub fn new(capacity: usize) -> Self {
286        let words = capacity.div_ceil(64);
287        Self {
288            words: vec![0u64; words],
289        }
290    }
291    /// Sets bit `i`.
292    pub fn set(&mut self, i: usize) {
293        let word = i / 64;
294        let bit = i % 64;
295        if word < self.words.len() {
296            self.words[word] |= 1u64 << bit;
297        }
298    }
299    /// Clears bit `i`.
300    pub fn clear(&mut self, i: usize) {
301        let word = i / 64;
302        let bit = i % 64;
303        if word < self.words.len() {
304            self.words[word] &= !(1u64 << bit);
305        }
306    }
307    /// Returns `true` if bit `i` is set.
308    pub fn get(&self, i: usize) -> bool {
309        let word = i / 64;
310        let bit = i % 64;
311        self.words.get(word).is_some_and(|w| w & (1u64 << bit) != 0)
312    }
313    /// Returns the number of set bits.
314    pub fn count_ones(&self) -> u32 {
315        self.words.iter().map(|w| w.count_ones()).sum()
316    }
317    /// Returns the union with another bit set.
318    pub fn union(&self, other: &SparseBitSet) -> SparseBitSet {
319        let len = self.words.len().max(other.words.len());
320        let mut result = SparseBitSet {
321            words: vec![0u64; len],
322        };
323        for i in 0..self.words.len() {
324            result.words[i] |= self.words[i];
325        }
326        for i in 0..other.words.len() {
327            result.words[i] |= other.words[i];
328        }
329        result
330    }
331}
332/// Tracks the frequency of items.
333#[allow(dead_code)]
334pub struct FrequencyTable<T: std::hash::Hash + Eq + Clone> {
335    counts: std::collections::HashMap<T, u64>,
336}
337#[allow(dead_code)]
338impl<T: std::hash::Hash + Eq + Clone> FrequencyTable<T> {
339    /// Creates a new empty frequency table.
340    pub fn new() -> Self {
341        Self {
342            counts: std::collections::HashMap::new(),
343        }
344    }
345    /// Records one occurrence of `item`.
346    pub fn record(&mut self, item: T) {
347        *self.counts.entry(item).or_insert(0) += 1;
348    }
349    /// Returns the frequency of `item`.
350    pub fn freq(&self, item: &T) -> u64 {
351        self.counts.get(item).copied().unwrap_or(0)
352    }
353    /// Returns the item with the highest frequency.
354    pub fn most_frequent(&self) -> Option<(&T, u64)> {
355        self.counts
356            .iter()
357            .max_by_key(|(_, &v)| v)
358            .map(|(k, &v)| (k, v))
359    }
360    /// Returns the total number of recordings.
361    pub fn total(&self) -> u64 {
362        self.counts.values().sum()
363    }
364    /// Returns the number of distinct items.
365    pub fn distinct(&self) -> usize {
366        self.counts.len()
367    }
368}
369/// A simple LIFO work queue.
370#[allow(dead_code)]
371pub struct WorkStack<T> {
372    items: Vec<T>,
373}
374#[allow(dead_code)]
375impl<T> WorkStack<T> {
376    /// Creates a new empty stack.
377    pub fn new() -> Self {
378        Self { items: Vec::new() }
379    }
380    /// Pushes a work item.
381    pub fn push(&mut self, item: T) {
382        self.items.push(item);
383    }
384    /// Pops the next work item.
385    pub fn pop(&mut self) -> Option<T> {
386        self.items.pop()
387    }
388    /// Returns `true` if empty.
389    pub fn is_empty(&self) -> bool {
390        self.items.is_empty()
391    }
392    /// Returns the number of pending work items.
393    pub fn len(&self) -> usize {
394        self.items.len()
395    }
396}
397/// A symmetric binary relation stored as a set of unordered pairs.
398#[allow(dead_code)]
399pub struct SymmetricRelation {
400    pairs: std::collections::HashSet<(u64, u64)>,
401}
402#[allow(dead_code)]
403impl SymmetricRelation {
404    /// Creates an empty relation.
405    pub fn new() -> Self {
406        Self {
407            pairs: std::collections::HashSet::new(),
408        }
409    }
410    fn key(a: u64, b: u64) -> (u64, u64) {
411        if a <= b {
412            (a, b)
413        } else {
414            (b, a)
415        }
416    }
417    /// Adds the pair `(a, b)`.
418    pub fn add(&mut self, a: u64, b: u64) {
419        self.pairs.insert(Self::key(a, b));
420    }
421    /// Returns `true` if `(a, b)` is in the relation.
422    pub fn contains(&self, a: u64, b: u64) -> bool {
423        self.pairs.contains(&Self::key(a, b))
424    }
425    /// Returns the number of pairs.
426    pub fn len(&self) -> usize {
427        self.pairs.len()
428    }
429    /// Returns `true` if empty.
430    pub fn is_empty(&self) -> bool {
431        self.pairs.is_empty()
432    }
433}
434/// A memoized computation slot that stores a cached value.
435#[allow(dead_code)]
436pub struct MemoSlot<T: Clone> {
437    cached: Option<T>,
438}
439#[allow(dead_code)]
440impl<T: Clone> MemoSlot<T> {
441    /// Creates an uncomputed memo slot.
442    pub fn new() -> Self {
443        Self { cached: None }
444    }
445    /// Returns the cached value, computing it with `f` if absent.
446    pub fn get_or_compute(&mut self, f: impl FnOnce() -> T) -> &T {
447        self.cached.get_or_insert_with(f)
448    }
449    /// Invalidates the cached value.
450    pub fn invalidate(&mut self) {
451        self.cached = None;
452    }
453    /// Returns `true` if the value has been computed.
454    pub fn is_cached(&self) -> bool {
455        self.cached.is_some()
456    }
457}
458/// A checked equivalence with its witness proof.
459#[allow(dead_code)]
460pub struct CheckedEquiv {
461    lhs: u64,
462    rhs: u64,
463    proof: EquivProofTerm,
464}
465#[allow(dead_code)]
466impl CheckedEquiv {
467    /// Creates a reflexive equivalence.
468    pub fn refl(id: u64) -> Self {
469        Self {
470            lhs: id,
471            rhs: id,
472            proof: EquivProofTerm::Refl(id),
473        }
474    }
475    /// Creates a named-axiom equivalence.
476    pub fn by_axiom(lhs: u64, rhs: u64, name: impl Into<String>) -> Self {
477        Self {
478            lhs,
479            rhs,
480            proof: EquivProofTerm::Axiom(name.into()),
481        }
482    }
483    /// Returns the LHS hash.
484    pub fn lhs(&self) -> u64 {
485        self.lhs
486    }
487    /// Returns the RHS hash.
488    pub fn rhs(&self) -> u64 {
489        self.rhs
490    }
491    /// Returns a reference to the proof term.
492    pub fn proof(&self) -> &EquivProofTerm {
493        &self.proof
494    }
495    /// Returns `true` if both sides are identical.
496    pub fn is_trivial(&self) -> bool {
497        self.lhs == self.rhs
498    }
499}
500/// A simple event counter with named events.
501#[allow(dead_code)]
502pub struct EventCounter {
503    counts: std::collections::HashMap<String, u64>,
504}
505#[allow(dead_code)]
506impl EventCounter {
507    /// Creates a new empty counter.
508    pub fn new() -> Self {
509        Self {
510            counts: std::collections::HashMap::new(),
511        }
512    }
513    /// Increments the counter for `event`.
514    pub fn inc(&mut self, event: &str) {
515        *self.counts.entry(event.to_string()).or_insert(0) += 1;
516    }
517    /// Adds `n` to the counter for `event`.
518    pub fn add(&mut self, event: &str, n: u64) {
519        *self.counts.entry(event.to_string()).or_insert(0) += n;
520    }
521    /// Returns the count for `event`.
522    pub fn get(&self, event: &str) -> u64 {
523        self.counts.get(event).copied().unwrap_or(0)
524    }
525    /// Returns the total count across all events.
526    pub fn total(&self) -> u64 {
527        self.counts.values().sum()
528    }
529    /// Resets all counters.
530    pub fn reset(&mut self) {
531        self.counts.clear();
532    }
533    /// Returns all event names.
534    pub fn event_names(&self) -> Vec<&str> {
535        self.counts.keys().map(|s| s.as_str()).collect()
536    }
537}
538/// A key-value annotation table for arbitrary metadata.
539#[allow(dead_code)]
540pub struct AnnotationTable {
541    map: std::collections::HashMap<String, Vec<String>>,
542}
543#[allow(dead_code)]
544impl AnnotationTable {
545    /// Creates an empty annotation table.
546    pub fn new() -> Self {
547        Self {
548            map: std::collections::HashMap::new(),
549        }
550    }
551    /// Adds an annotation value for the given key.
552    pub fn annotate(&mut self, key: impl Into<String>, val: impl Into<String>) {
553        self.map.entry(key.into()).or_default().push(val.into());
554    }
555    /// Returns all annotations for `key`.
556    pub fn get_all(&self, key: &str) -> &[String] {
557        self.map.get(key).map(|v| v.as_slice()).unwrap_or(&[])
558    }
559    /// Returns the number of distinct annotation keys.
560    pub fn num_keys(&self) -> usize {
561        self.map.len()
562    }
563    /// Returns `true` if the table has any annotations for `key`.
564    pub fn has(&self, key: &str) -> bool {
565        self.map.contains_key(key)
566    }
567}
568/// A FIFO work queue.
569#[allow(dead_code)]
570pub struct WorkQueue<T> {
571    items: std::collections::VecDeque<T>,
572}
573#[allow(dead_code)]
574impl<T> WorkQueue<T> {
575    /// Creates a new empty queue.
576    pub fn new() -> Self {
577        Self {
578            items: std::collections::VecDeque::new(),
579        }
580    }
581    /// Enqueues a work item.
582    pub fn enqueue(&mut self, item: T) {
583        self.items.push_back(item);
584    }
585    /// Dequeues the next work item.
586    pub fn dequeue(&mut self) -> Option<T> {
587        self.items.pop_front()
588    }
589    /// Returns `true` if empty.
590    pub fn is_empty(&self) -> bool {
591        self.items.is_empty()
592    }
593    /// Returns the number of pending items.
594    pub fn len(&self) -> usize {
595        self.items.len()
596    }
597}
598/// A counter that dispenses monotonically increasing `TypedId` values.
599#[allow(dead_code)]
600pub struct IdDispenser<T> {
601    next: u32,
602    _phantom: std::marker::PhantomData<T>,
603}
604#[allow(dead_code)]
605impl<T> IdDispenser<T> {
606    /// Creates a new dispenser starting from zero.
607    pub fn new() -> Self {
608        Self {
609            next: 0,
610            _phantom: std::marker::PhantomData,
611        }
612    }
613    /// Dispenses the next ID.
614    #[allow(clippy::should_implement_trait)]
615    pub fn next(&mut self) -> TypedId<T> {
616        let id = TypedId::new(self.next);
617        self.next += 1;
618        id
619    }
620    /// Returns the number of IDs dispensed.
621    pub fn count(&self) -> u32 {
622        self.next
623    }
624}
625/// A slot that can hold a value, with lazy initialization.
626#[allow(dead_code)]
627pub struct Slot<T> {
628    inner: Option<T>,
629}
630#[allow(dead_code)]
631impl<T> Slot<T> {
632    /// Creates an empty slot.
633    pub fn empty() -> Self {
634        Self { inner: None }
635    }
636    /// Fills the slot with `val`.  Panics if already filled.
637    pub fn fill(&mut self, val: T) {
638        assert!(self.inner.is_none(), "Slot: already filled");
639        self.inner = Some(val);
640    }
641    /// Returns the slot's value, or `None`.
642    pub fn get(&self) -> Option<&T> {
643        self.inner.as_ref()
644    }
645    /// Returns `true` if the slot is filled.
646    pub fn is_filled(&self) -> bool {
647        self.inner.is_some()
648    }
649    /// Takes the value out of the slot.
650    pub fn take(&mut self) -> Option<T> {
651        self.inner.take()
652    }
653    /// Fills the slot if empty, returning a reference to the value.
654    pub fn get_or_fill_with(&mut self, f: impl FnOnce() -> T) -> &T {
655        self.inner.get_or_insert_with(f)
656    }
657}
658/// A single equivalence class with a canonical representative.
659#[allow(dead_code)]
660#[allow(missing_docs)]
661pub struct EquivClass {
662    /// The canonical (representative) element.
663    pub repr: usize,
664    /// All elements in this class (including the representative).
665    pub members: Vec<usize>,
666}
667#[allow(dead_code)]
668impl EquivClass {
669    /// Creates a new singleton class.
670    pub fn singleton(id: usize) -> Self {
671        Self {
672            repr: id,
673            members: vec![id],
674        }
675    }
676    /// Returns `true` if `id` is a member of this class.
677    pub fn contains(&self, id: usize) -> bool {
678        self.members.contains(&id)
679    }
680    /// Returns the size of the class.
681    pub fn size(&self) -> usize {
682        self.members.len()
683    }
684}
685/// A proof term witnessing an equivalence.
686#[allow(dead_code)]
687#[derive(Debug, Clone)]
688pub enum EquivProofTerm {
689    /// Reflexivity: `a ≡ a`.
690    Refl(u64),
691    /// Symmetry: if `a ≡ b` then `b ≡ a`.
692    Symm(Box<EquivProofTerm>),
693    /// Transitivity: if `a ≡ b` and `b ≡ c` then `a ≡ c`.
694    Trans(Box<EquivProofTerm>, Box<EquivProofTerm>),
695    /// A named axiom.
696    Axiom(String),
697}
698#[allow(dead_code)]
699impl EquivProofTerm {
700    /// Returns the depth of the proof term.
701    pub fn depth(&self) -> usize {
702        match self {
703            EquivProofTerm::Refl(_) => 0,
704            EquivProofTerm::Axiom(_) => 0,
705            EquivProofTerm::Symm(p) => 1 + p.depth(),
706            EquivProofTerm::Trans(p, q) => 1 + p.depth().max(q.depth()),
707        }
708    }
709    /// Returns `true` if the proof is trivially reflexive.
710    pub fn is_refl(&self) -> bool {
711        matches!(self, EquivProofTerm::Refl(_))
712    }
713}
714/// Union-find node for expression equivalence classes.
715#[derive(Debug)]
716struct UnionFindEntry {
717    parent: usize,
718    rank: u32,
719}
720/// Statistics about the equivalence manager usage.
721#[derive(Clone, Debug, Default)]
722pub struct EquivStats {
723    /// Total queries performed.
724    pub total_queries: u64,
725    /// Queries that hit the equiv cache.
726    pub equiv_hits: u64,
727    /// Queries that hit the failure cache.
728    pub failure_hits: u64,
729    /// Queries that required computation.
730    pub cache_misses: u64,
731    /// Number of times add_equiv was called.
732    pub equiv_additions: u64,
733    /// Number of times add_failure was called.
734    pub failure_additions: u64,
735}
736impl EquivStats {
737    /// Create new zeroed statistics.
738    pub fn new() -> Self {
739        Self::default()
740    }
741    /// Record a cache hit on the equiv set.
742    pub fn record_equiv_hit(&mut self) {
743        self.total_queries += 1;
744        self.equiv_hits += 1;
745    }
746    /// Record a cache hit on the failure set.
747    pub fn record_failure_hit(&mut self) {
748        self.total_queries += 1;
749        self.failure_hits += 1;
750    }
751    /// Record a cache miss.
752    pub fn record_miss(&mut self) {
753        self.total_queries += 1;
754        self.cache_misses += 1;
755    }
756    /// Record an addition.
757    pub fn record_equiv_addition(&mut self) {
758        self.equiv_additions += 1;
759    }
760    /// Record a failure addition.
761    pub fn record_failure_addition(&mut self) {
762        self.failure_additions += 1;
763    }
764    /// Compute the cache hit rate.
765    pub fn hit_rate(&self) -> f64 {
766        if self.total_queries == 0 {
767            return 1.0;
768        }
769        (self.equiv_hits + self.failure_hits) as f64 / self.total_queries as f64
770    }
771}
772/// A set of non-overlapping integer intervals.
773#[allow(dead_code)]
774pub struct IntervalSet {
775    intervals: Vec<(i64, i64)>,
776}
777#[allow(dead_code)]
778impl IntervalSet {
779    /// Creates an empty interval set.
780    pub fn new() -> Self {
781        Self {
782            intervals: Vec::new(),
783        }
784    }
785    /// Adds the interval `[lo, hi]` to the set.
786    pub fn add(&mut self, lo: i64, hi: i64) {
787        if lo > hi {
788            return;
789        }
790        let mut new_lo = lo;
791        let mut new_hi = hi;
792        let mut i = 0;
793        while i < self.intervals.len() {
794            let (il, ih) = self.intervals[i];
795            if ih < new_lo - 1 {
796                i += 1;
797                continue;
798            }
799            if il > new_hi + 1 {
800                break;
801            }
802            new_lo = new_lo.min(il);
803            new_hi = new_hi.max(ih);
804            self.intervals.remove(i);
805        }
806        self.intervals.insert(i, (new_lo, new_hi));
807    }
808    /// Returns `true` if `x` is in the set.
809    pub fn contains(&self, x: i64) -> bool {
810        self.intervals.iter().any(|&(lo, hi)| x >= lo && x <= hi)
811    }
812    /// Returns the number of intervals.
813    pub fn num_intervals(&self) -> usize {
814        self.intervals.len()
815    }
816    /// Returns the total count of integers covered.
817    pub fn cardinality(&self) -> i64 {
818        self.intervals.iter().map(|&(lo, hi)| hi - lo + 1).sum()
819    }
820}
821/// Equivalence manager for caching def_eq results during type checking.
822///
823/// Provides two layers of caching:
824/// 1. A set of expression pairs known to be definitionally equal.
825/// 2. A set of expression pairs known to NOT be definitionally equal (failure cache).
826///
827/// The failure cache is critical for performance: when lazy delta reduction
828/// tries multiple unfolding strategies, caching failures avoids redundant work.
829#[derive(Debug)]
830pub struct EquivManager {
831    /// Known equal pairs.
832    equiv_set: HashSet<(Expr, Expr)>,
833    /// Known unequal pairs (failure cache).
834    failure_set: HashSet<(Expr, Expr)>,
835    /// Union-find for transitive closure.
836    /// Maps expression to its representative index.
837    expr_to_idx: HashMap<Expr, usize>,
838    /// Union-find entries.
839    entries: Vec<UnionFindEntry>,
840}
841impl EquivManager {
842    /// Create a new empty equivalence manager.
843    pub fn new() -> Self {
844        EquivManager {
845            equiv_set: HashSet::new(),
846            failure_set: HashSet::new(),
847            expr_to_idx: HashMap::new(),
848            entries: Vec::new(),
849        }
850    }
851    /// Record that two expressions are definitionally equal.
852    pub fn add_equiv(&mut self, a: &Expr, b: &Expr) {
853        if a == b {
854            return;
855        }
856        let pair = canonicalize_pair(a.clone(), b.clone());
857        self.equiv_set.insert(pair);
858        let idx_a = self.get_or_create_idx(a);
859        let idx_b = self.get_or_create_idx(b);
860        self.union(idx_a, idx_b);
861    }
862    /// Check if two expressions are known to be definitionally equal.
863    pub fn is_equiv(&mut self, a: &Expr, b: &Expr) -> bool {
864        if a == b {
865            return true;
866        }
867        let pair = canonicalize_pair(a.clone(), b.clone());
868        if self.equiv_set.contains(&pair) {
869            return true;
870        }
871        if let (Some(&idx_a), Some(&idx_b)) = (self.expr_to_idx.get(a), self.expr_to_idx.get(b)) {
872            return self.find(idx_a) == self.find(idx_b);
873        }
874        false
875    }
876    /// Record that two expressions are NOT definitionally equal.
877    pub fn add_failure(&mut self, a: &Expr, b: &Expr) {
878        let pair = canonicalize_pair(a.clone(), b.clone());
879        self.failure_set.insert(pair);
880    }
881    /// Check if two expressions are known to NOT be definitionally equal.
882    pub fn is_failure(&self, a: &Expr, b: &Expr) -> bool {
883        let pair = canonicalize_pair(a.clone(), b.clone());
884        self.failure_set.contains(&pair)
885    }
886    /// Clear all cached results.
887    pub fn clear(&mut self) {
888        self.equiv_set.clear();
889        self.failure_set.clear();
890        self.expr_to_idx.clear();
891        self.entries.clear();
892    }
893    /// Get the number of cached equivalences.
894    pub fn num_equiv(&self) -> usize {
895        self.equiv_set.len()
896    }
897    /// Get the number of cached failures.
898    pub fn num_failures(&self) -> usize {
899        self.failure_set.len()
900    }
901    fn get_or_create_idx(&mut self, e: &Expr) -> usize {
902        if let Some(&idx) = self.expr_to_idx.get(e) {
903            return idx;
904        }
905        let idx = self.entries.len();
906        self.entries.push(UnionFindEntry {
907            parent: idx,
908            rank: 0,
909        });
910        self.expr_to_idx.insert(e.clone(), idx);
911        idx
912    }
913    fn find(&mut self, mut x: usize) -> usize {
914        while self.entries[x].parent != x {
915            let grandparent = self.entries[self.entries[x].parent].parent;
916            self.entries[x].parent = grandparent;
917            x = grandparent;
918        }
919        x
920    }
921    fn union(&mut self, x: usize, y: usize) {
922        let rx = self.find(x);
923        let ry = self.find(y);
924        if rx == ry {
925            return;
926        }
927        match self.entries[rx].rank.cmp(&self.entries[ry].rank) {
928            std::cmp::Ordering::Less => {
929                self.entries[rx].parent = ry;
930            }
931            std::cmp::Ordering::Greater => {
932                self.entries[ry].parent = rx;
933            }
934            std::cmp::Ordering::Equal => {
935                self.entries[ry].parent = rx;
936                self.entries[rx].rank += 1;
937            }
938        }
939    }
940}
941/// A simple equivalence relation over integer indices.
942#[derive(Clone, Debug, Default)]
943pub struct IndexEquivManager {
944    parent: Vec<usize>,
945    rank: Vec<u32>,
946}
947impl IndexEquivManager {
948    /// Create a new manager for n elements.
949    pub fn new(n: usize) -> Self {
950        Self {
951            parent: (0..n).collect(),
952            rank: vec![0; n],
953        }
954    }
955    /// Find the representative of element `x`.
956    pub fn find(&mut self, x: usize) -> usize {
957        if self.parent[x] != x {
958            self.parent[x] = self.find(self.parent[x]);
959        }
960        self.parent[x]
961    }
962    /// Merge the classes of `x` and `y`.
963    pub fn union(&mut self, x: usize, y: usize) {
964        let rx = self.find(x);
965        let ry = self.find(y);
966        if rx == ry {
967            return;
968        }
969        match self.rank[rx].cmp(&self.rank[ry]) {
970            std::cmp::Ordering::Less => self.parent[rx] = ry,
971            std::cmp::Ordering::Greater => self.parent[ry] = rx,
972            std::cmp::Ordering::Equal => {
973                self.parent[ry] = rx;
974                self.rank[rx] += 1;
975            }
976        }
977    }
978    /// Check if two elements are in the same class.
979    pub fn same_class(&mut self, x: usize, y: usize) -> bool {
980        self.find(x) == self.find(y)
981    }
982    /// Count the number of distinct equivalence classes.
983    pub fn num_classes(&mut self) -> usize {
984        let n = self.parent.len();
985        let roots: std::collections::HashSet<usize> = (0..n).map(|i| self.find(i)).collect();
986        roots.len()
987    }
988}
989/// A key-value store for diagnostic metadata.
990#[allow(dead_code)]
991pub struct DiagMeta {
992    pub(super) entries: Vec<(String, String)>,
993}
994#[allow(dead_code)]
995impl DiagMeta {
996    /// Creates an empty metadata store.
997    pub fn new() -> Self {
998        Self {
999            entries: Vec::new(),
1000        }
1001    }
1002    /// Adds a key-value pair.
1003    pub fn add(&mut self, key: impl Into<String>, val: impl Into<String>) {
1004        self.entries.push((key.into(), val.into()));
1005    }
1006    /// Returns the value for `key`, or `None`.
1007    pub fn get(&self, key: &str) -> Option<&str> {
1008        self.entries
1009            .iter()
1010            .find(|(k, _)| k == key)
1011            .map(|(_, v)| v.as_str())
1012    }
1013    /// Returns the number of entries.
1014    pub fn len(&self) -> usize {
1015        self.entries.len()
1016    }
1017    /// Returns `true` if empty.
1018    pub fn is_empty(&self) -> bool {
1019        self.entries.is_empty()
1020    }
1021}
1022/// A type-safe wrapper around a `u32` identifier.
1023#[allow(dead_code)]
1024#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1025pub struct TypedId<T> {
1026    pub(super) id: u32,
1027    _phantom: std::marker::PhantomData<T>,
1028}
1029#[allow(dead_code)]
1030impl<T> TypedId<T> {
1031    /// Creates a new typed ID.
1032    pub const fn new(id: u32) -> Self {
1033        Self {
1034            id,
1035            _phantom: std::marker::PhantomData,
1036        }
1037    }
1038    /// Returns the raw `u32` ID.
1039    pub fn raw(&self) -> u32 {
1040        self.id
1041    }
1042}
1043/// A counted-access cache that tracks hit and miss statistics.
1044#[allow(dead_code)]
1045#[allow(missing_docs)]
1046pub struct StatCache<K: std::hash::Hash + Eq + Clone, V: Clone> {
1047    /// The inner LRU cache.
1048    pub inner: SimpleLruCache<K, V>,
1049    /// Number of cache hits.
1050    pub hits: u64,
1051    /// Number of cache misses.
1052    pub misses: u64,
1053}
1054#[allow(dead_code)]
1055impl<K: std::hash::Hash + Eq + Clone, V: Clone> StatCache<K, V> {
1056    /// Creates a new stat cache with the given capacity.
1057    pub fn new(capacity: usize) -> Self {
1058        Self {
1059            inner: SimpleLruCache::new(capacity),
1060            hits: 0,
1061            misses: 0,
1062        }
1063    }
1064    /// Performs a lookup, tracking hit/miss.
1065    pub fn get(&mut self, key: &K) -> Option<&V> {
1066        let result = self.inner.get(key);
1067        if result.is_some() {
1068            self.hits += 1;
1069        } else {
1070            self.misses += 1;
1071        }
1072        None
1073    }
1074    /// Inserts a key-value pair.
1075    pub fn put(&mut self, key: K, val: V) {
1076        self.inner.put(key, val);
1077    }
1078    /// Returns the hit rate.
1079    pub fn hit_rate(&self) -> f64 {
1080        let total = self.hits + self.misses;
1081        if total == 0 {
1082            return 0.0;
1083        }
1084        self.hits as f64 / total as f64
1085    }
1086}
1087/// A simple stack-based scope tracker.
1088#[allow(dead_code)]
1089pub struct ScopeStack {
1090    names: Vec<String>,
1091}
1092#[allow(dead_code)]
1093impl ScopeStack {
1094    /// Creates a new empty scope stack.
1095    pub fn new() -> Self {
1096        Self { names: Vec::new() }
1097    }
1098    /// Pushes a scope name.
1099    pub fn push(&mut self, name: impl Into<String>) {
1100        self.names.push(name.into());
1101    }
1102    /// Pops the current scope.
1103    pub fn pop(&mut self) -> Option<String> {
1104        self.names.pop()
1105    }
1106    /// Returns the current (innermost) scope name, or `None`.
1107    pub fn current(&self) -> Option<&str> {
1108        self.names.last().map(|s| s.as_str())
1109    }
1110    /// Returns the depth of the scope stack.
1111    pub fn depth(&self) -> usize {
1112        self.names.len()
1113    }
1114    /// Returns `true` if the stack is empty.
1115    pub fn is_empty(&self) -> bool {
1116        self.names.is_empty()
1117    }
1118    /// Returns the full path as a dot-separated string.
1119    pub fn path(&self) -> String {
1120        self.names.join(".")
1121    }
1122}
1123/// A sequence number that can be compared for ordering.
1124#[allow(dead_code)]
1125#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1126pub struct SeqNum(u64);
1127#[allow(dead_code)]
1128impl SeqNum {
1129    /// Creates sequence number zero.
1130    pub const ZERO: SeqNum = SeqNum(0);
1131    /// Advances the sequence number by one.
1132    pub fn next(self) -> SeqNum {
1133        SeqNum(self.0 + 1)
1134    }
1135    /// Returns the raw value.
1136    pub fn value(self) -> u64 {
1137        self.0
1138    }
1139}
1140/// An instrumented equivalence manager that collects statistics.
1141#[derive(Debug, Default)]
1142pub struct InstrumentedEquivManager {
1143    /// Underlying manager.
1144    inner: EquivManager,
1145    /// Usage statistics.
1146    stats: EquivStats,
1147}
1148impl InstrumentedEquivManager {
1149    /// Create a new instrumented manager.
1150    pub fn new() -> Self {
1151        Self::default()
1152    }
1153    /// Add an equivalence.
1154    pub fn add_equiv(&mut self, a: &Expr, b: &Expr) {
1155        self.stats.record_equiv_addition();
1156        self.inner.add_equiv(a, b);
1157    }
1158    /// Add a failure.
1159    pub fn add_failure(&mut self, a: &Expr, b: &Expr) {
1160        self.stats.record_failure_addition();
1161        self.inner.add_failure(a, b);
1162    }
1163    /// Check if two expressions are equivalent.
1164    pub fn is_equiv(&mut self, a: &Expr, b: &Expr) -> bool {
1165        let result = self.inner.is_equiv(a, b);
1166        if result {
1167            self.stats.record_equiv_hit();
1168        } else {
1169            self.stats.record_miss();
1170        }
1171        result
1172    }
1173    /// Check if a failure is cached.
1174    pub fn is_failure(&self, a: &Expr, b: &Expr) -> bool {
1175        self.inner.is_failure(a, b)
1176    }
1177    /// Get the statistics.
1178    pub fn stats(&self) -> &EquivStats {
1179        &self.stats
1180    }
1181    /// Clear both the manager and statistics.
1182    pub fn clear(&mut self) {
1183        self.inner.clear();
1184        self.stats = EquivStats::new();
1185    }
1186}
1187/// Statistics for the equivalence manager.
1188#[allow(dead_code)]
1189#[allow(missing_docs)]
1190pub struct EquivManagerStats {
1191    /// Number of equivalence checks performed.
1192    pub checks: u64,
1193    /// Number of cache hits.
1194    pub hits: u64,
1195    /// Number of merges performed.
1196    pub merges: u64,
1197    /// Number of distinct equivalence classes remaining.
1198    pub classes: usize,
1199}
1200#[allow(dead_code)]
1201impl EquivManagerStats {
1202    /// Creates zeroed stats.
1203    pub fn new() -> Self {
1204        Self {
1205            checks: 0,
1206            hits: 0,
1207            merges: 0,
1208            classes: 0,
1209        }
1210    }
1211    /// Returns the cache hit rate (0.0–1.0).
1212    pub fn hit_rate(&self) -> f64 {
1213        if self.checks == 0 {
1214            return 0.0;
1215        }
1216        self.hits as f64 / self.checks as f64
1217    }
1218}
1219/// A read-only view into an `EquivManager` for query-only access.
1220pub struct EquivQuery<'a> {
1221    manager: &'a EquivManager,
1222}
1223impl<'a> EquivQuery<'a> {
1224    /// Create a new query handle.
1225    pub fn new(manager: &'a EquivManager) -> Self {
1226        Self { manager }
1227    }
1228    /// Check if two expressions are in the failure cache.
1229    pub fn is_known_failure(&self, a: &Expr, b: &Expr) -> bool {
1230        self.manager.is_failure(a, b)
1231    }
1232    /// Return the number of cached equivalences.
1233    pub fn num_equiv(&self) -> usize {
1234        self.manager.num_equiv()
1235    }
1236    /// Return the number of cached failures.
1237    pub fn num_failures(&self) -> usize {
1238        self.manager.num_failures()
1239    }
1240}
1241/// A persistent equivalence manager that supports efficient batch updates.
1242///
1243/// Unlike `EquivManager`, this stores equivalences as a sorted list for
1244/// predictable serialization.
1245#[derive(Clone, Debug, Default)]
1246pub struct PersistentEquivManager {
1247    pairs: Vec<(String, String)>,
1248}
1249impl PersistentEquivManager {
1250    /// Create a new empty persistent manager.
1251    pub fn new() -> Self {
1252        Self::default()
1253    }
1254    /// Add an equivalence.
1255    pub fn add_equiv(&mut self, a: &Expr, b: &Expr) {
1256        let ka = format!("{:?}", a);
1257        let kb = format!("{:?}", b);
1258        let pair = if ka <= kb { (ka, kb) } else { (kb, ka) };
1259        if !self.pairs.contains(&pair) {
1260            self.pairs.push(pair);
1261            self.pairs.sort();
1262        }
1263    }
1264    /// Check if two expressions are known equivalent.
1265    pub fn is_equiv(&self, a: &Expr, b: &Expr) -> bool {
1266        if a == b {
1267            return true;
1268        }
1269        let ka = format!("{:?}", a);
1270        let kb = format!("{:?}", b);
1271        let pair = if ka <= kb {
1272            (ka.clone(), kb.clone())
1273        } else {
1274            (kb.clone(), ka.clone())
1275        };
1276        self.pairs.contains(&pair)
1277    }
1278    /// Return the number of stored equivalences.
1279    pub fn len(&self) -> usize {
1280        self.pairs.len()
1281    }
1282    /// Check if the manager is empty.
1283    pub fn is_empty(&self) -> bool {
1284        self.pairs.is_empty()
1285    }
1286    /// Serialize the equivalences to a list of string pairs.
1287    pub fn serialize(&self) -> Vec<(String, String)> {
1288        self.pairs.clone()
1289    }
1290}
1291/// A cache that records proven expression equalities.
1292#[allow(dead_code)]
1293pub struct ExprEquivCache {
1294    proven: std::collections::HashSet<(u64, u64)>,
1295    disproven: std::collections::HashSet<(u64, u64)>,
1296}
1297#[allow(dead_code)]
1298impl ExprEquivCache {
1299    /// Creates an empty cache.
1300    pub fn new() -> Self {
1301        Self {
1302            proven: std::collections::HashSet::new(),
1303            disproven: std::collections::HashSet::new(),
1304        }
1305    }
1306    /// Records that `a` and `b` are equal.
1307    pub fn mark_equal(&mut self, a: u64, b: u64) {
1308        let key = if a <= b { (a, b) } else { (b, a) };
1309        self.proven.insert(key);
1310    }
1311    /// Records that `a` and `b` are unequal.
1312    pub fn mark_unequal(&mut self, a: u64, b: u64) {
1313        let key = if a <= b { (a, b) } else { (b, a) };
1314        self.disproven.insert(key);
1315    }
1316    /// Returns `Some(true)` if equal, `Some(false)` if unequal, `None` if unknown.
1317    pub fn query(&self, a: u64, b: u64) -> Option<bool> {
1318        let key = if a <= b { (a, b) } else { (b, a) };
1319        if self.proven.contains(&key) {
1320            return Some(true);
1321        }
1322        if self.disproven.contains(&key) {
1323            return Some(false);
1324        }
1325        None
1326    }
1327    /// Returns the number of proven equalities.
1328    pub fn proven_count(&self) -> usize {
1329        self.proven.len()
1330    }
1331}
1332/// Interns strings to save memory (each unique string stored once).
1333#[allow(dead_code)]
1334pub struct StringInterner {
1335    strings: Vec<String>,
1336    map: std::collections::HashMap<String, u32>,
1337}
1338#[allow(dead_code)]
1339impl StringInterner {
1340    /// Creates a new string interner.
1341    pub fn new() -> Self {
1342        Self {
1343            strings: Vec::new(),
1344            map: std::collections::HashMap::new(),
1345        }
1346    }
1347    /// Interns `s` and returns its ID.
1348    pub fn intern(&mut self, s: &str) -> u32 {
1349        if let Some(&id) = self.map.get(s) {
1350            return id;
1351        }
1352        let id = self.strings.len() as u32;
1353        self.strings.push(s.to_string());
1354        self.map.insert(s.to_string(), id);
1355        id
1356    }
1357    /// Returns the string for `id`.
1358    pub fn get(&self, id: u32) -> Option<&str> {
1359        self.strings.get(id as usize).map(|s| s.as_str())
1360    }
1361    /// Returns the total number of interned strings.
1362    pub fn len(&self) -> usize {
1363        self.strings.len()
1364    }
1365    /// Returns `true` if empty.
1366    pub fn is_empty(&self) -> bool {
1367        self.strings.is_empty()
1368    }
1369}
1370/// A union-find (disjoint-set) data structure with path compression.
1371#[allow(dead_code)]
1372pub struct UnionFind {
1373    parent: Vec<usize>,
1374    rank: Vec<usize>,
1375    count: usize,
1376}
1377#[allow(dead_code)]
1378impl UnionFind {
1379    /// Creates a new union-find with `n` elements (each its own class).
1380    pub fn new(n: usize) -> Self {
1381        Self {
1382            parent: (0..n).collect(),
1383            rank: vec![0; n],
1384            count: n,
1385        }
1386    }
1387    /// Finds the representative of `x` with path compression.
1388    pub fn find(&mut self, x: usize) -> usize {
1389        if self.parent[x] != x {
1390            self.parent[x] = self.find(self.parent[x]);
1391        }
1392        self.parent[x]
1393    }
1394    /// Unions the classes of `x` and `y`.  Returns `true` if they were distinct.
1395    pub fn union(&mut self, x: usize, y: usize) -> bool {
1396        let rx = self.find(x);
1397        let ry = self.find(y);
1398        if rx == ry {
1399            return false;
1400        }
1401        if self.rank[rx] < self.rank[ry] {
1402            self.parent[rx] = ry;
1403        } else if self.rank[rx] > self.rank[ry] {
1404            self.parent[ry] = rx;
1405        } else {
1406            self.parent[ry] = rx;
1407            self.rank[rx] += 1;
1408        }
1409        self.count -= 1;
1410        true
1411    }
1412    /// Returns `true` if `x` and `y` are in the same class.
1413    pub fn same(&mut self, x: usize, y: usize) -> bool {
1414        self.find(x) == self.find(y)
1415    }
1416    /// Returns the number of distinct classes.
1417    pub fn num_classes(&self) -> usize {
1418        self.count
1419    }
1420    /// Returns the total number of elements.
1421    pub fn num_elements(&self) -> usize {
1422        self.parent.len()
1423    }
1424}
1425/// A scoped equivalence manager that supports push/pop of equivalence frames.
1426///
1427/// This allows backtracking during type checking — when a unification attempt
1428/// fails, all equivalences added in that scope are rolled back.
1429#[derive(Debug, Default)]
1430pub struct ScopedEquivManager {
1431    /// Current equivalences, grouped by scope.
1432    scopes: Vec<Vec<(Expr, Expr)>>,
1433    /// The underlying equivalence manager.
1434    inner: EquivManager,
1435}
1436impl ScopedEquivManager {
1437    /// Create a new scoped manager with one initial scope.
1438    pub fn new() -> Self {
1439        Self {
1440            scopes: vec![Vec::new()],
1441            inner: EquivManager::new(),
1442        }
1443    }
1444    /// Push a new scope.
1445    pub fn push_scope(&mut self) {
1446        self.scopes.push(Vec::new());
1447    }
1448    /// Pop the current scope, rolling back all equivalences added in it.
1449    pub fn pop_scope(&mut self) {
1450        if self.scopes.len() > 1 {
1451            self.scopes.pop();
1452            self.inner.clear();
1453            for scope in &self.scopes {
1454                for (a, b) in scope {
1455                    self.inner.add_equiv(a, b);
1456                }
1457            }
1458        }
1459    }
1460    /// Add an equivalence in the current scope.
1461    pub fn add_equiv(&mut self, a: &Expr, b: &Expr) {
1462        if let Some(scope) = self.scopes.last_mut() {
1463            scope.push((a.clone(), b.clone()));
1464        }
1465        self.inner.add_equiv(a, b);
1466    }
1467    /// Check if two expressions are equivalent.
1468    pub fn is_equiv(&mut self, a: &Expr, b: &Expr) -> bool {
1469        self.inner.is_equiv(a, b)
1470    }
1471    /// Return the current scope depth.
1472    pub fn scope_depth(&self) -> usize {
1473        self.scopes.len()
1474    }
1475    /// Return the total number of equivalences across all scopes.
1476    pub fn total_equivs(&self) -> usize {
1477        self.scopes.iter().map(|s| s.len()).sum()
1478    }
1479}
1480/// A growable ring buffer with fixed maximum capacity.
1481#[allow(dead_code)]
1482pub struct RingBuffer<T> {
1483    data: Vec<Option<T>>,
1484    head: usize,
1485    tail: usize,
1486    count: usize,
1487    capacity: usize,
1488}
1489#[allow(dead_code)]
1490impl<T> RingBuffer<T> {
1491    /// Creates a new ring buffer with the given capacity.
1492    pub fn new(capacity: usize) -> Self {
1493        let mut data = Vec::with_capacity(capacity);
1494        for _ in 0..capacity {
1495            data.push(None);
1496        }
1497        Self {
1498            data,
1499            head: 0,
1500            tail: 0,
1501            count: 0,
1502            capacity,
1503        }
1504    }
1505    /// Pushes a value, overwriting the oldest if full.
1506    pub fn push(&mut self, val: T) {
1507        if self.count == self.capacity {
1508            self.data[self.head] = Some(val);
1509            self.head = (self.head + 1) % self.capacity;
1510            self.tail = (self.tail + 1) % self.capacity;
1511        } else {
1512            self.data[self.tail] = Some(val);
1513            self.tail = (self.tail + 1) % self.capacity;
1514            self.count += 1;
1515        }
1516    }
1517    /// Pops the oldest value.
1518    pub fn pop(&mut self) -> Option<T> {
1519        if self.count == 0 {
1520            return None;
1521        }
1522        let val = self.data[self.head].take();
1523        self.head = (self.head + 1) % self.capacity;
1524        self.count -= 1;
1525        val
1526    }
1527    /// Returns the number of elements.
1528    pub fn len(&self) -> usize {
1529        self.count
1530    }
1531    /// Returns `true` if empty.
1532    pub fn is_empty(&self) -> bool {
1533        self.count == 0
1534    }
1535    /// Returns `true` if at capacity.
1536    pub fn is_full(&self) -> bool {
1537        self.count == self.capacity
1538    }
1539}