Skip to main content

oxilean_kernel/cache/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use crate::expr_util::lift_loose_bvars;
7use std::collections::HashMap;
8
9/// A reusable scratch buffer for path computations.
10#[allow(dead_code)]
11pub struct PathBuf {
12    components: Vec<String>,
13}
14#[allow(dead_code)]
15impl PathBuf {
16    /// Creates a new empty path buffer.
17    pub fn new() -> Self {
18        Self {
19            components: Vec::new(),
20        }
21    }
22    /// Pushes a component.
23    pub fn push(&mut self, comp: impl Into<String>) {
24        self.components.push(comp.into());
25    }
26    /// Pops the last component.
27    pub fn pop(&mut self) {
28        self.components.pop();
29    }
30    /// Returns the current path as a `/`-separated string.
31    pub fn as_str(&self) -> String {
32        self.components.join("/")
33    }
34    /// Returns the depth of the path.
35    pub fn depth(&self) -> usize {
36        self.components.len()
37    }
38    /// Clears the path.
39    pub fn clear(&mut self) {
40        self.components.clear();
41    }
42}
43/// A type-erased function pointer with arity tracking.
44#[allow(dead_code)]
45pub struct RawFnPtr {
46    /// The raw function pointer (stored as usize for type erasure).
47    ptr: usize,
48    arity: usize,
49    name: String,
50}
51#[allow(dead_code)]
52impl RawFnPtr {
53    /// Creates a new raw function pointer descriptor.
54    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
55        Self {
56            ptr,
57            arity,
58            name: name.into(),
59        }
60    }
61    /// Returns the arity.
62    pub fn arity(&self) -> usize {
63        self.arity
64    }
65    /// Returns the name.
66    pub fn name(&self) -> &str {
67        &self.name
68    }
69    /// Returns the raw pointer value.
70    pub fn raw(&self) -> usize {
71        self.ptr
72    }
73}
74/// A pair of `StatSummary` values tracking before/after a transformation.
75#[allow(dead_code)]
76pub struct TransformStat {
77    before: StatSummary,
78    after: StatSummary,
79}
80#[allow(dead_code)]
81impl TransformStat {
82    /// Creates a new transform stat recorder.
83    pub fn new() -> Self {
84        Self {
85            before: StatSummary::new(),
86            after: StatSummary::new(),
87        }
88    }
89    /// Records a before value.
90    pub fn record_before(&mut self, v: f64) {
91        self.before.record(v);
92    }
93    /// Records an after value.
94    pub fn record_after(&mut self, v: f64) {
95        self.after.record(v);
96    }
97    /// Returns the mean reduction ratio (after/before).
98    pub fn mean_ratio(&self) -> Option<f64> {
99        let b = self.before.mean()?;
100        let a = self.after.mean()?;
101        if b.abs() < f64::EPSILON {
102            return None;
103        }
104        Some(a / b)
105    }
106}
107/// A token bucket rate limiter.
108#[allow(dead_code)]
109pub struct TokenBucket {
110    capacity: u64,
111    tokens: u64,
112    refill_per_ms: u64,
113    last_refill: crate::wall_clock::Instant,
114}
115#[allow(dead_code)]
116impl TokenBucket {
117    /// Creates a new token bucket.
118    pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
119        Self {
120            capacity,
121            tokens: capacity,
122            refill_per_ms,
123            last_refill: crate::wall_clock::Instant::now(),
124        }
125    }
126    /// Attempts to consume `n` tokens.  Returns `true` on success.
127    pub fn try_consume(&mut self, n: u64) -> bool {
128        self.refill();
129        if self.tokens >= n {
130            self.tokens -= n;
131            true
132        } else {
133            false
134        }
135    }
136    fn refill(&mut self) {
137        let now = crate::wall_clock::Instant::now();
138        let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
139        if elapsed_ms > 0 {
140            let new_tokens = elapsed_ms * self.refill_per_ms;
141            self.tokens = (self.tokens + new_tokens).min(self.capacity);
142            self.last_refill = now;
143        }
144    }
145    /// Returns the number of currently available tokens.
146    pub fn available(&self) -> u64 {
147        self.tokens
148    }
149    /// Returns the bucket capacity.
150    pub fn capacity(&self) -> u64 {
151        self.capacity
152    }
153}
154/// A fixed-size sliding window that computes a running sum.
155#[allow(dead_code)]
156pub struct SlidingSum {
157    window: Vec<f64>,
158    capacity: usize,
159    pos: usize,
160    sum: f64,
161    count: usize,
162}
163#[allow(dead_code)]
164impl SlidingSum {
165    /// Creates a sliding sum with the given window size.
166    pub fn new(capacity: usize) -> Self {
167        Self {
168            window: vec![0.0; capacity],
169            capacity,
170            pos: 0,
171            sum: 0.0,
172            count: 0,
173        }
174    }
175    /// Adds a value to the window, removing the oldest if full.
176    pub fn push(&mut self, val: f64) {
177        let oldest = self.window[self.pos];
178        self.sum -= oldest;
179        self.sum += val;
180        self.window[self.pos] = val;
181        self.pos = (self.pos + 1) % self.capacity;
182        if self.count < self.capacity {
183            self.count += 1;
184        }
185    }
186    /// Returns the current window sum.
187    pub fn sum(&self) -> f64 {
188        self.sum
189    }
190    /// Returns the window mean, or `None` if empty.
191    pub fn mean(&self) -> Option<f64> {
192        if self.count == 0 {
193            None
194        } else {
195            Some(self.sum / self.count as f64)
196        }
197    }
198    /// Returns the current window size (number of valid elements).
199    pub fn count(&self) -> usize {
200        self.count
201    }
202}
203/// A hierarchical configuration tree.
204#[allow(dead_code)]
205pub struct ConfigNode {
206    key: String,
207    value: Option<String>,
208    children: Vec<ConfigNode>,
209}
210#[allow(dead_code)]
211impl ConfigNode {
212    /// Creates a leaf config node with a value.
213    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
214        Self {
215            key: key.into(),
216            value: Some(value.into()),
217            children: Vec::new(),
218        }
219    }
220    /// Creates a section node with children.
221    pub fn section(key: impl Into<String>) -> Self {
222        Self {
223            key: key.into(),
224            value: None,
225            children: Vec::new(),
226        }
227    }
228    /// Adds a child node.
229    pub fn add_child(&mut self, child: ConfigNode) {
230        self.children.push(child);
231    }
232    /// Returns the key.
233    pub fn key(&self) -> &str {
234        &self.key
235    }
236    /// Returns the value, or `None` for section nodes.
237    pub fn value(&self) -> Option<&str> {
238        self.value.as_deref()
239    }
240    /// Returns the number of children.
241    pub fn num_children(&self) -> usize {
242        self.children.len()
243    }
244    /// Looks up a dot-separated path.
245    pub fn lookup(&self, path: &str) -> Option<&str> {
246        let mut parts = path.splitn(2, '.');
247        let head = parts.next()?;
248        let tail = parts.next();
249        if head != self.key {
250            return None;
251        }
252        match tail {
253            None => self.value.as_deref(),
254            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
255        }
256    }
257    fn lookup_relative(&self, path: &str) -> Option<&str> {
258        let mut parts = path.splitn(2, '.');
259        let head = parts.next()?;
260        let tail = parts.next();
261        if head != self.key {
262            return None;
263        }
264        match tail {
265            None => self.value.as_deref(),
266            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
267        }
268    }
269}
270/// A simple directed acyclic graph.
271#[allow(dead_code)]
272pub struct SimpleDag {
273    /// `edges[i]` is the list of direct successors of node `i`.
274    edges: Vec<Vec<usize>>,
275}
276#[allow(dead_code)]
277impl SimpleDag {
278    /// Creates a DAG with `n` nodes and no edges.
279    pub fn new(n: usize) -> Self {
280        Self {
281            edges: vec![Vec::new(); n],
282        }
283    }
284    /// Adds an edge from `from` to `to`.
285    pub fn add_edge(&mut self, from: usize, to: usize) {
286        if from < self.edges.len() {
287            self.edges[from].push(to);
288        }
289    }
290    /// Returns the successors of `node`.
291    pub fn successors(&self, node: usize) -> &[usize] {
292        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
293    }
294    /// Returns `true` if `from` can reach `to` via DFS.
295    pub fn can_reach(&self, from: usize, to: usize) -> bool {
296        let mut visited = vec![false; self.edges.len()];
297        self.dfs(from, to, &mut visited)
298    }
299    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
300        if cur == target {
301            return true;
302        }
303        if cur >= visited.len() || visited[cur] {
304            return false;
305        }
306        visited[cur] = true;
307        for &next in self.successors(cur) {
308            if self.dfs(next, target, visited) {
309                return true;
310            }
311        }
312        false
313    }
314    /// Returns the topological order of nodes, or `None` if a cycle is detected.
315    pub fn topological_sort(&self) -> Option<Vec<usize>> {
316        let n = self.edges.len();
317        let mut in_degree = vec![0usize; n];
318        for succs in &self.edges {
319            for &s in succs {
320                if s < n {
321                    in_degree[s] += 1;
322                }
323            }
324        }
325        let mut queue: std::collections::VecDeque<usize> =
326            (0..n).filter(|&i| in_degree[i] == 0).collect();
327        let mut order = Vec::new();
328        while let Some(node) = queue.pop_front() {
329            order.push(node);
330            for &s in self.successors(node) {
331                if s < n {
332                    in_degree[s] -= 1;
333                    if in_degree[s] == 0 {
334                        queue.push_back(s);
335                    }
336                }
337            }
338        }
339        if order.len() == n {
340            Some(order)
341        } else {
342            None
343        }
344    }
345    /// Returns the number of nodes.
346    pub fn num_nodes(&self) -> usize {
347        self.edges.len()
348    }
349}
350/// A tagged union for representing a simple two-case discriminated union.
351#[allow(dead_code)]
352pub enum Either2<A, B> {
353    /// The first alternative.
354    First(A),
355    /// The second alternative.
356    Second(B),
357}
358#[allow(dead_code)]
359impl<A, B> Either2<A, B> {
360    /// Returns `true` if this is the first alternative.
361    pub fn is_first(&self) -> bool {
362        matches!(self, Either2::First(_))
363    }
364    /// Returns `true` if this is the second alternative.
365    pub fn is_second(&self) -> bool {
366        matches!(self, Either2::Second(_))
367    }
368    /// Returns the first value if present.
369    pub fn first(self) -> Option<A> {
370        match self {
371            Either2::First(a) => Some(a),
372            _ => None,
373        }
374    }
375    /// Returns the second value if present.
376    pub fn second(self) -> Option<B> {
377        match self {
378            Either2::Second(b) => Some(b),
379            _ => None,
380        }
381    }
382    /// Maps over the first alternative.
383    pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
384        match self {
385            Either2::First(a) => Either2::First(f(a)),
386            Either2::Second(b) => Either2::Second(b),
387        }
388    }
389}
390/// A simple decision tree node for rule dispatching.
391#[allow(dead_code)]
392#[allow(missing_docs)]
393pub enum DecisionNode {
394    /// A leaf with an action string.
395    Leaf(String),
396    /// An interior node: check `key` equals `val` → `yes_branch`, else `no_branch`.
397    Branch {
398        key: String,
399        val: String,
400        yes_branch: Box<DecisionNode>,
401        no_branch: Box<DecisionNode>,
402    },
403}
404#[allow(dead_code)]
405impl DecisionNode {
406    /// Evaluates the decision tree with the given context.
407    pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
408        match self {
409            DecisionNode::Leaf(action) => action.as_str(),
410            DecisionNode::Branch {
411                key,
412                val,
413                yes_branch,
414                no_branch,
415            } => {
416                let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
417                if actual == val.as_str() {
418                    yes_branch.evaluate(ctx)
419                } else {
420                    no_branch.evaluate(ctx)
421                }
422            }
423        }
424    }
425    /// Returns the depth of the decision tree.
426    pub fn depth(&self) -> usize {
427        match self {
428            DecisionNode::Leaf(_) => 0,
429            DecisionNode::Branch {
430                yes_branch,
431                no_branch,
432                ..
433            } => 1 + yes_branch.depth().max(no_branch.depth()),
434        }
435    }
436}
437/// A simple stack-based calculator for arithmetic expressions.
438#[allow(dead_code)]
439pub struct StackCalc {
440    stack: Vec<i64>,
441}
442#[allow(dead_code)]
443impl StackCalc {
444    /// Creates a new empty calculator.
445    pub fn new() -> Self {
446        Self { stack: Vec::new() }
447    }
448    /// Pushes an integer literal.
449    pub fn push(&mut self, n: i64) {
450        self.stack.push(n);
451    }
452    /// Replaces the top two values, `b` on top of `a`, with `op(a, b)` and
453    /// returns it.  Returns `None`, leaving the stack unchanged, if it holds
454    /// fewer than two values.
455    fn apply_binary(&mut self, op: impl FnOnce(i64, i64) -> i64) -> Option<i64> {
456        let (rest_len, a, b) = match self.stack[..] {
457            [ref rest @ .., a, b] => (rest.len(), a, b),
458            _ => return None,
459        };
460        let value = op(a, b);
461        self.stack.truncate(rest_len);
462        self.stack.push(value);
463        Some(value)
464    }
465    /// Adds the top two values and returns the sum, which replaces them.
466    /// Returns `None`, leaving the stack unchanged, if it holds fewer than
467    /// two values.
468    pub fn add(&mut self) -> Option<i64> {
469        self.apply_binary(|a, b| a + b)
470    }
471    /// Subtracts top from second and returns the difference, which replaces
472    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
473    /// than two values.
474    pub fn sub(&mut self) -> Option<i64> {
475        self.apply_binary(|a, b| a - b)
476    }
477    /// Multiplies the top two values and returns the product, which replaces
478    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
479    /// than two values.
480    pub fn mul(&mut self) -> Option<i64> {
481        self.apply_binary(|a, b| a * b)
482    }
483    /// Peeks the top value.
484    pub fn peek(&self) -> Option<i64> {
485        self.stack.last().copied()
486    }
487    /// Returns the stack depth.
488    pub fn depth(&self) -> usize {
489        self.stack.len()
490    }
491}
492/// A counter that can measure elapsed time between snapshots.
493#[allow(dead_code)]
494pub struct Stopwatch {
495    start: crate::wall_clock::Instant,
496    splits: Vec<f64>,
497}
498#[allow(dead_code)]
499impl Stopwatch {
500    /// Creates and starts a new stopwatch.
501    pub fn start() -> Self {
502        Self {
503            start: crate::wall_clock::Instant::now(),
504            splits: Vec::new(),
505        }
506    }
507    /// Records a split time (elapsed since start).
508    pub fn split(&mut self) {
509        self.splits.push(self.elapsed_ms());
510    }
511    /// Returns total elapsed milliseconds since start.
512    pub fn elapsed_ms(&self) -> f64 {
513        self.start.elapsed().as_secs_f64() * 1000.0
514    }
515    /// Returns all recorded split times.
516    pub fn splits(&self) -> &[f64] {
517        &self.splits
518    }
519    /// Returns the number of splits.
520    pub fn num_splits(&self) -> usize {
521        self.splits.len()
522    }
523}
524/// Global cache manager for all kernel caches
525pub struct CacheManager {
526    pub(crate) whnf: WhnfCache,
527    pub(crate) defeq: DefEqCache,
528    pub(crate) infer: InferCache,
529}
530impl CacheManager {
531    /// Create a new cache manager with default capacities
532    pub fn new() -> Self {
533        CacheManager {
534            whnf: WhnfCache::new(1024, false),
535            defeq: DefEqCache::new(512),
536            infer: InferCache::new(256),
537        }
538    }
539    /// Create a cache manager with custom capacities
540    pub fn with_capacities(whnf_cap: usize, defeq_cap: usize, infer_cap: usize) -> Self {
541        CacheManager {
542            whnf: WhnfCache::new(whnf_cap, false),
543            defeq: DefEqCache::new(defeq_cap),
544            infer: InferCache::new(infer_cap),
545        }
546    }
547    /// Get mutable reference to WHNF cache
548    pub fn whnf_mut(&mut self) -> &mut WhnfCache {
549        &mut self.whnf
550    }
551    /// Get mutable reference to DefEq cache
552    pub fn defeq_mut(&mut self) -> &mut DefEqCache {
553        &mut self.defeq
554    }
555    /// Get mutable reference to Infer cache
556    pub fn infer_mut(&mut self) -> &mut InferCache {
557        &mut self.infer
558    }
559    /// Clear all caches
560    pub fn clear_all(&mut self) {
561        self.whnf.clear();
562        self.defeq.clear();
563        self.infer.clear();
564    }
565    /// Resize all caches to new capacities
566    pub fn resize_all(&mut self, whnf_cap: usize, defeq_cap: usize, infer_cap: usize) {
567        self.whnf = WhnfCache::new(whnf_cap, self.whnf.is_transparent());
568        self.defeq = DefEqCache::new(defeq_cap);
569        self.infer = InferCache::new(infer_cap);
570    }
571    /// Get comprehensive statistics for all caches
572    pub fn statistics(&self) -> CacheStatistics {
573        let (whnf_hits, whnf_misses) = self.whnf.stats();
574        let (defeq_hits, defeq_misses) = self.defeq.stats();
575        let (infer_hits, infer_misses) = self.infer.stats();
576        CacheStatistics {
577            whnf_hits,
578            whnf_misses,
579            whnf_hit_rate: self.whnf.hit_rate(),
580            defeq_hits,
581            defeq_misses,
582            defeq_hit_rate: self.defeq.hit_rate(),
583            infer_hits,
584            infer_misses,
585            infer_hit_rate: self.infer.hit_rate(),
586        }
587    }
588}
589/// Simplified Expr representation for hashing (mirrors actual Expr).
590///
591/// Used for caching purposes with a streamlined structure suitable for hashing.
592#[derive(Clone, Debug, PartialEq, Eq)]
593pub enum SimplifiedExpr {
594    /// A variable reference with the given name.
595    Var(String),
596    /// Function application: function and argument.
597    App(Box<SimplifiedExpr>, Box<SimplifiedExpr>),
598    /// Lambda abstraction: parameter name and body.
599    Lambda(String, Box<SimplifiedExpr>),
600    /// Pi type: parameter name, parameter type, and body type.
601    Pi(String, Box<SimplifiedExpr>, Box<SimplifiedExpr>),
602}
603impl SimplifiedExpr {
604    /// Compute FNV-1a hash for this expression
605    pub fn hash(&self) -> u64 {
606        match self {
607            SimplifiedExpr::Var(name) => {
608                let mut bytes = vec![0u8];
609                bytes.extend_from_slice(name.as_bytes());
610                fnv1a_hash(&bytes)
611            }
612            SimplifiedExpr::App(f, arg) => {
613                let f_hash = f.hash();
614                let arg_hash = arg.hash();
615                let mut bytes = vec![1u8];
616                bytes.extend_from_slice(&f_hash.to_le_bytes());
617                bytes.extend_from_slice(&arg_hash.to_le_bytes());
618                fnv1a_hash(&bytes)
619            }
620            SimplifiedExpr::Lambda(name, body) => {
621                let body_hash = body.hash();
622                let mut bytes = vec![2u8];
623                bytes.extend_from_slice(name.as_bytes());
624                bytes.extend_from_slice(&body_hash.to_le_bytes());
625                fnv1a_hash(&bytes)
626            }
627            SimplifiedExpr::Pi(name, typ, body) => {
628                let typ_hash = typ.hash();
629                let body_hash = body.hash();
630                let mut bytes = vec![3u8];
631                bytes.extend_from_slice(name.as_bytes());
632                bytes.extend_from_slice(&typ_hash.to_le_bytes());
633                bytes.extend_from_slice(&body_hash.to_le_bytes());
634                fnv1a_hash(&bytes)
635            }
636        }
637    }
638}
639/// A window iterator that yields overlapping windows of size `n`.
640#[allow(dead_code)]
641pub struct WindowIterator<'a, T> {
642    pub(super) data: &'a [T],
643    pub(super) pos: usize,
644    pub(super) window: usize,
645}
646#[allow(dead_code)]
647impl<'a, T> WindowIterator<'a, T> {
648    /// Creates a new window iterator.
649    pub fn new(data: &'a [T], window: usize) -> Self {
650        Self {
651            data,
652            pos: 0,
653            window,
654        }
655    }
656}
657/// A set of rewrite rules.
658#[allow(dead_code)]
659pub struct RewriteRuleSet {
660    rules: Vec<RewriteRule>,
661}
662#[allow(dead_code)]
663impl RewriteRuleSet {
664    /// Creates an empty rule set.
665    pub fn new() -> Self {
666        Self { rules: Vec::new() }
667    }
668    /// Adds a rule.
669    pub fn add(&mut self, rule: RewriteRule) {
670        self.rules.push(rule);
671    }
672    /// Returns the number of rules.
673    pub fn len(&self) -> usize {
674        self.rules.len()
675    }
676    /// Returns `true` if the set is empty.
677    pub fn is_empty(&self) -> bool {
678        self.rules.is_empty()
679    }
680    /// Returns all conditional rules.
681    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
682        self.rules.iter().filter(|r| r.conditional).collect()
683    }
684    /// Returns all unconditional rules.
685    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
686        self.rules.iter().filter(|r| !r.conditional).collect()
687    }
688    /// Looks up a rule by name.
689    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
690        self.rules.iter().find(|r| r.name == name)
691    }
692}
693/// Generic LRU Cache with HashMap + Vec-based intrusive list
694pub struct LruCache<K: Clone + Eq + std::hash::Hash, V: Clone> {
695    capacity: usize,
696    map: HashMap<K, usize>,
697    nodes: Vec<Node<K, V>>,
698    head: Option<usize>,
699    tail: Option<usize>,
700    hits: u64,
701    misses: u64,
702}
703impl<K: Clone + Eq + std::hash::Hash, V: Clone> LruCache<K, V> {
704    /// Create a new LRU cache with specified capacity
705    pub fn new(capacity: usize) -> Self {
706        assert!(capacity > 0, "LRU cache capacity must be > 0");
707        LruCache {
708            capacity,
709            map: HashMap::new(),
710            nodes: Vec::new(),
711            head: None,
712            tail: None,
713            hits: 0,
714            misses: 0,
715        }
716    }
717    /// Get a value by key and move it to the most recently used position
718    pub fn get(&mut self, key: &K) -> Option<V> {
719        if let Some(&idx) = self.map.get(key) {
720            self.hits += 1;
721            self.move_to_head(idx);
722            Some(self.nodes[idx].value.clone())
723        } else {
724            self.misses += 1;
725            None
726        }
727    }
728    /// Insert a key-value pair into the cache
729    pub fn insert(&mut self, key: K, value: V) {
730        if let Some(&idx) = self.map.get(&key) {
731            self.nodes[idx].value = value;
732            self.move_to_head(idx);
733        } else {
734            if self.nodes.len() >= self.capacity {
735                self.evict_lru();
736            }
737            let new_idx = self.nodes.len();
738            let node = Node {
739                key: key.clone(),
740                value,
741                prev: None,
742                next: self.head,
743            };
744            self.nodes.push(node);
745            self.map.insert(key, new_idx);
746            if let Some(old_head) = self.head {
747                self.nodes[old_head].prev = Some(new_idx);
748            }
749            self.head = Some(new_idx);
750            if self.tail.is_none() {
751                self.tail = Some(new_idx);
752            }
753        }
754    }
755    /// Remove a key from the cache
756    pub fn remove(&mut self, key: &K) -> Option<V> {
757        if let Some(&idx) = self.map.get(key) {
758            let node = &self.nodes[idx];
759            let prev = node.prev;
760            let next = node.next;
761            if let Some(p) = prev {
762                self.nodes[p].next = next;
763            } else {
764                self.tail = next;
765            }
766            if let Some(n) = next {
767                self.nodes[n].prev = prev;
768            } else {
769                self.head = prev;
770            }
771            self.map.remove(key);
772            Some(self.nodes[idx].value.clone())
773        } else {
774            None
775        }
776    }
777    /// Check if key exists in the cache
778    pub fn contains_key(&self, key: &K) -> bool {
779        self.map.contains_key(key)
780    }
781    /// Get the number of entries in the cache
782    pub fn len(&self) -> usize {
783        self.map.len()
784    }
785    /// Check if cache is empty
786    pub fn is_empty(&self) -> bool {
787        self.map.is_empty()
788    }
789    /// Get the capacity of the cache
790    pub fn capacity(&self) -> usize {
791        self.capacity
792    }
793    /// Clear all entries from the cache
794    pub fn clear(&mut self) {
795        self.map.clear();
796        self.nodes.clear();
797        self.head = None;
798        self.tail = None;
799        self.hits = 0;
800        self.misses = 0;
801    }
802    /// Get cache statistics
803    pub fn stats(&self) -> (u64, u64) {
804        (self.hits, self.misses)
805    }
806    /// Get hit rate as a percentage (0.0 to 100.0)
807    pub fn hit_rate(&self) -> f64 {
808        let total = self.hits + self.misses;
809        if total == 0 {
810            0.0
811        } else {
812            (self.hits as f64 / total as f64) * 100.0
813        }
814    }
815    fn move_to_head(&mut self, idx: usize) {
816        if self.head == Some(idx) {
817            return;
818        }
819        let prev = self.nodes[idx].prev;
820        let next = self.nodes[idx].next;
821        if let Some(p) = prev {
822            self.nodes[p].next = next;
823        }
824        if let Some(n) = next {
825            self.nodes[n].prev = prev;
826        } else {
827            self.tail = prev;
828        }
829        self.nodes[idx].prev = None;
830        self.nodes[idx].next = self.head;
831        if let Some(old_head) = self.head {
832            self.nodes[old_head].prev = Some(idx);
833        }
834        self.head = Some(idx);
835    }
836    fn evict_lru(&mut self) {
837        if let Some(tail_idx) = self.tail {
838            let key = self.nodes[tail_idx].key.clone();
839            let prev = self.nodes[tail_idx].prev;
840            if let Some(p) = prev {
841                self.nodes[p].next = None;
842                self.head = Some(p);
843            } else {
844                self.head = None;
845            }
846            self.tail = prev;
847            self.map.remove(&key);
848            self.nodes.remove(tail_idx);
849            self.nodes.iter().enumerate().for_each(|(i, node)| {
850                *self
851                    .map
852                    .get_mut(&node.key)
853                    .expect("node key must exist in map") = i;
854            });
855        }
856    }
857}
858/// A very simple approximate membership test using bit-hashing.
859///
860/// This is NOT a production-quality bloom filter; it uses only two hash
861/// functions and a small bit array for illustration.
862pub struct BloomFilterApprox {
863    bits: Vec<bool>,
864    size: usize,
865}
866impl BloomFilterApprox {
867    /// Create a bloom filter with `size` bits.
868    pub fn new(size: usize) -> Self {
869        Self {
870            bits: vec![false; size],
871            size,
872        }
873    }
874    fn hash1(data: &[u8], size: usize) -> usize {
875        fnv1a_hash(data) as usize % size
876    }
877    fn hash2(data: &[u8], size: usize) -> usize {
878        let h = fnv1a_hash(data).wrapping_mul(0x9e3779b9_7f4a7c15);
879        h as usize % size
880    }
881    /// Insert a key.
882    pub fn insert<T: AsRef<[u8]>>(&mut self, key: T) {
883        let bytes = key.as_ref();
884        let h1 = Self::hash1(bytes, self.size);
885        let h2 = Self::hash2(bytes, self.size);
886        self.bits[h1] = true;
887        self.bits[h2] = true;
888    }
889    /// Check if a key might be present (may have false positives).
890    pub fn might_contain<T: AsRef<[u8]>>(&self, key: T) -> bool {
891        let bytes = key.as_ref();
892        let h1 = Self::hash1(bytes, self.size);
893        let h2 = Self::hash2(bytes, self.size);
894        self.bits[h1] && self.bits[h2]
895    }
896    /// Clear all bits.
897    pub fn clear(&mut self) {
898        self.bits.iter_mut().for_each(|b| *b = false);
899    }
900    /// Number of set bits.
901    pub fn set_bit_count(&self) -> usize {
902        self.bits.iter().filter(|&&b| b).count()
903    }
904    /// Filter size in bits.
905    pub fn size(&self) -> usize {
906        self.size
907    }
908}
909/// A two-level cache hierarchy.
910///
911/// L1 is a small, fast LRU cache. L2 is a larger LRU cache. On a miss in L1,
912/// L2 is checked and the result is promoted to L1.
913pub struct MultiLevelCache<K: Clone + Eq + std::hash::Hash, V: Clone> {
914    l1: LruCache<K, V>,
915    l2: LruCache<K, V>,
916    l1_hits: u64,
917    l2_hits: u64,
918    misses: u64,
919}
920impl<K: Clone + Eq + std::hash::Hash, V: Clone> MultiLevelCache<K, V> {
921    /// Create a two-level cache.
922    pub fn new(l1_cap: usize, l2_cap: usize) -> Self {
923        Self {
924            l1: LruCache::new(l1_cap),
925            l2: LruCache::new(l2_cap),
926            l1_hits: 0,
927            l2_hits: 0,
928            misses: 0,
929        }
930    }
931    /// Get a value, checking L1 first, then L2.
932    pub fn get(&mut self, key: &K) -> Option<V> {
933        if let Some(v) = self.l1.get(key) {
934            self.l1_hits += 1;
935            return Some(v);
936        }
937        if let Some(v) = self.l2.get(key) {
938            self.l2_hits += 1;
939            self.l1.insert(key.clone(), v.clone());
940            return Some(v);
941        }
942        self.misses += 1;
943        None
944    }
945    /// Insert into both L1 and L2.
946    pub fn insert(&mut self, key: K, value: V) {
947        self.l1.insert(key.clone(), value.clone());
948        self.l2.insert(key, value);
949    }
950    /// Insert only into L2 (for pre-warming the L2 cache).
951    pub fn insert_l2_only(&mut self, key: K, value: V) {
952        self.l2.insert(key, value);
953    }
954    /// Clear L1 only.
955    pub fn clear_l1(&mut self) {
956        self.l1.clear();
957    }
958    /// Clear both levels.
959    pub fn clear_all(&mut self) {
960        self.l1.clear();
961        self.l2.clear();
962        self.l1_hits = 0;
963        self.l2_hits = 0;
964        self.misses = 0;
965    }
966    /// L1 hit count.
967    pub fn l1_hits(&self) -> u64 {
968        self.l1_hits
969    }
970    /// L2 hit count.
971    pub fn l2_hits(&self) -> u64 {
972        self.l2_hits
973    }
974    /// Miss count.
975    pub fn misses(&self) -> u64 {
976        self.misses
977    }
978    /// Total requests served.
979    pub fn total_requests(&self) -> u64 {
980        self.l1_hits + self.l2_hits + self.misses
981    }
982    /// Overall hit rate.
983    pub fn hit_rate(&self) -> f64 {
984        let total = self.total_requests();
985        if total == 0 {
986            0.0
987        } else {
988            ((self.l1_hits + self.l2_hits) as f64 / total as f64) * 100.0
989        }
990    }
991}
992/// Symmetry-aware cache for definitional equality checks
993pub struct DefEqCache {
994    pub(crate) cache: LruCache<(u64, u64), bool>,
995}
996impl DefEqCache {
997    /// Create a new definitional equality cache
998    pub fn new(capacity: usize) -> Self {
999        DefEqCache {
1000            cache: LruCache::new(capacity),
1001        }
1002    }
1003    /// Check if a definitional equality result is cached (symmetry-aware)
1004    pub fn check_cache(&mut self, expr1: &SimplifiedExpr, expr2: &SimplifiedExpr) -> Option<bool> {
1005        let hash1 = expr1.hash();
1006        let hash2 = expr2.hash();
1007        if let Some(result) = self.cache.get(&(hash1, hash2)) {
1008            return Some(result);
1009        }
1010        if let Some(result) = self.cache.get(&(hash2, hash1)) {
1011            return Some(result);
1012        }
1013        None
1014    }
1015    /// Store a definitional equality result (symmetry-aware)
1016    pub fn store_result(&mut self, expr1: &SimplifiedExpr, expr2: &SimplifiedExpr, result: bool) {
1017        let hash1 = expr1.hash();
1018        let hash2 = expr2.hash();
1019        self.cache.insert((hash1, hash2), result);
1020        self.cache.insert((hash2, hash1), result);
1021    }
1022    /// Clear the cache
1023    pub fn clear(&mut self) {
1024        self.cache.clear();
1025    }
1026    /// Get cache statistics
1027    pub fn stats(&self) -> (u64, u64) {
1028        self.cache.stats()
1029    }
1030    /// Get hit rate
1031    pub fn hit_rate(&self) -> f64 {
1032        self.cache.hit_rate()
1033    }
1034}
1035/// A mutable reference stack for tracking the current "focus" in a tree traversal.
1036#[allow(dead_code)]
1037pub struct FocusStack<T> {
1038    items: Vec<T>,
1039}
1040#[allow(dead_code)]
1041impl<T> FocusStack<T> {
1042    /// Creates an empty focus stack.
1043    pub fn new() -> Self {
1044        Self { items: Vec::new() }
1045    }
1046    /// Focuses on `item`.
1047    pub fn focus(&mut self, item: T) {
1048        self.items.push(item);
1049    }
1050    /// Blurs (pops) the current focus.
1051    pub fn blur(&mut self) -> Option<T> {
1052        self.items.pop()
1053    }
1054    /// Returns the current focus, or `None`.
1055    pub fn current(&self) -> Option<&T> {
1056        self.items.last()
1057    }
1058    /// Returns the focus depth.
1059    pub fn depth(&self) -> usize {
1060        self.items.len()
1061    }
1062    /// Returns `true` if there is no current focus.
1063    pub fn is_empty(&self) -> bool {
1064        self.items.is_empty()
1065    }
1066}
1067/// A generic counter that tracks min/max/sum for statistical summaries.
1068#[allow(dead_code)]
1069pub struct StatSummary {
1070    count: u64,
1071    sum: f64,
1072    min: f64,
1073    max: f64,
1074}
1075#[allow(dead_code)]
1076impl StatSummary {
1077    /// Creates an empty summary.
1078    pub fn new() -> Self {
1079        Self {
1080            count: 0,
1081            sum: 0.0,
1082            min: f64::INFINITY,
1083            max: f64::NEG_INFINITY,
1084        }
1085    }
1086    /// Records a sample.
1087    pub fn record(&mut self, val: f64) {
1088        self.count += 1;
1089        self.sum += val;
1090        if val < self.min {
1091            self.min = val;
1092        }
1093        if val > self.max {
1094            self.max = val;
1095        }
1096    }
1097    /// Returns the mean, or `None` if no samples.
1098    pub fn mean(&self) -> Option<f64> {
1099        if self.count == 0 {
1100            None
1101        } else {
1102            Some(self.sum / self.count as f64)
1103        }
1104    }
1105    /// Returns the minimum, or `None` if no samples.
1106    pub fn min(&self) -> Option<f64> {
1107        if self.count == 0 {
1108            None
1109        } else {
1110            Some(self.min)
1111        }
1112    }
1113    /// Returns the maximum, or `None` if no samples.
1114    pub fn max(&self) -> Option<f64> {
1115        if self.count == 0 {
1116            None
1117        } else {
1118            Some(self.max)
1119        }
1120    }
1121    /// Returns the count of recorded samples.
1122    pub fn count(&self) -> u64 {
1123        self.count
1124    }
1125}
1126#[derive(Clone, Debug)]
1127struct TtlEntry<V> {
1128    value: V,
1129    expires_at: u64,
1130}
1131/// A sparse vector: stores only non-default elements.
1132#[allow(dead_code)]
1133pub struct SparseVec<T: Default + Clone + PartialEq> {
1134    entries: std::collections::HashMap<usize, T>,
1135    default_: T,
1136    logical_len: usize,
1137}
1138#[allow(dead_code)]
1139impl<T: Default + Clone + PartialEq> SparseVec<T> {
1140    /// Creates a new sparse vector with logical length `len`.
1141    pub fn new(len: usize) -> Self {
1142        Self {
1143            entries: std::collections::HashMap::new(),
1144            default_: T::default(),
1145            logical_len: len,
1146        }
1147    }
1148    /// Sets element at `idx`.
1149    pub fn set(&mut self, idx: usize, val: T) {
1150        if val == self.default_ {
1151            self.entries.remove(&idx);
1152        } else {
1153            self.entries.insert(idx, val);
1154        }
1155    }
1156    /// Gets element at `idx`.
1157    pub fn get(&self, idx: usize) -> &T {
1158        self.entries.get(&idx).unwrap_or(&self.default_)
1159    }
1160    /// Returns the logical length.
1161    pub fn len(&self) -> usize {
1162        self.logical_len
1163    }
1164    /// Returns whether the collection is empty.
1165    pub fn is_empty(&self) -> bool {
1166        self.len() == 0
1167    }
1168    /// Returns the number of non-default elements.
1169    pub fn nnz(&self) -> usize {
1170        self.entries.len()
1171    }
1172}
1173/// Represents a rewrite rule `lhs → rhs`.
1174#[allow(dead_code)]
1175#[allow(missing_docs)]
1176pub struct RewriteRule {
1177    /// The name of the rule.
1178    pub name: String,
1179    /// A string representation of the LHS pattern.
1180    pub lhs: String,
1181    /// A string representation of the RHS.
1182    pub rhs: String,
1183    /// Whether this is a conditional rule (has side conditions).
1184    pub conditional: bool,
1185}
1186#[allow(dead_code)]
1187impl RewriteRule {
1188    /// Creates an unconditional rewrite rule.
1189    pub fn unconditional(
1190        name: impl Into<String>,
1191        lhs: impl Into<String>,
1192        rhs: impl Into<String>,
1193    ) -> Self {
1194        Self {
1195            name: name.into(),
1196            lhs: lhs.into(),
1197            rhs: rhs.into(),
1198            conditional: false,
1199        }
1200    }
1201    /// Creates a conditional rewrite rule.
1202    pub fn conditional(
1203        name: impl Into<String>,
1204        lhs: impl Into<String>,
1205        rhs: impl Into<String>,
1206    ) -> Self {
1207        Self {
1208            name: name.into(),
1209            lhs: lhs.into(),
1210            rhs: rhs.into(),
1211            conditional: true,
1212        }
1213    }
1214    /// Returns a textual representation.
1215    pub fn display(&self) -> String {
1216        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
1217    }
1218}
1219/// Statistics for all caches.
1220///
1221/// Provides comprehensive metrics for monitoring cache performance across all cache types.
1222#[derive(Clone, Debug)]
1223pub struct CacheStatistics {
1224    /// Number of hits in the WHNF cache.
1225    pub whnf_hits: u64,
1226    /// Number of misses in the WHNF cache.
1227    pub whnf_misses: u64,
1228    /// Hit rate percentage for the WHNF cache (0.0-100.0).
1229    pub whnf_hit_rate: f64,
1230    /// Number of hits in the DefEq cache.
1231    pub defeq_hits: u64,
1232    /// Number of misses in the DefEq cache.
1233    pub defeq_misses: u64,
1234    /// Hit rate percentage for the DefEq cache (0.0-100.0).
1235    pub defeq_hit_rate: f64,
1236    /// Number of hits in the Infer cache.
1237    pub infer_hits: u64,
1238    /// Number of misses in the Infer cache.
1239    pub infer_misses: u64,
1240    /// Hit rate percentage for the Infer cache (0.0-100.0).
1241    pub infer_hit_rate: f64,
1242}
1243impl CacheStatistics {
1244    /// Get total hits across all caches
1245    pub fn total_hits(&self) -> u64 {
1246        self.whnf_hits + self.defeq_hits + self.infer_hits
1247    }
1248    /// Get total misses across all caches
1249    pub fn total_misses(&self) -> u64 {
1250        self.whnf_misses + self.defeq_misses + self.infer_misses
1251    }
1252    /// Get overall hit rate across all caches
1253    pub fn overall_hit_rate(&self) -> f64 {
1254        let total = self.total_hits() + self.total_misses();
1255        if total == 0 {
1256            0.0
1257        } else {
1258            (self.total_hits() as f64 / total as f64) * 100.0
1259        }
1260    }
1261}
1262/// A flat list of substitution pairs `(from, to)`.
1263#[allow(dead_code)]
1264pub struct FlatSubstitution {
1265    pairs: Vec<(String, String)>,
1266}
1267#[allow(dead_code)]
1268impl FlatSubstitution {
1269    /// Creates an empty substitution.
1270    pub fn new() -> Self {
1271        Self { pairs: Vec::new() }
1272    }
1273    /// Adds a pair.
1274    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
1275        self.pairs.push((from.into(), to.into()));
1276    }
1277    /// Applies all substitutions to `s` (leftmost-first order).
1278    pub fn apply(&self, s: &str) -> String {
1279        let mut result = s.to_string();
1280        for (from, to) in &self.pairs {
1281            result = result.replace(from.as_str(), to.as_str());
1282        }
1283        result
1284    }
1285    /// Returns the number of pairs.
1286    pub fn len(&self) -> usize {
1287        self.pairs.len()
1288    }
1289    /// Returns `true` if empty.
1290    pub fn is_empty(&self) -> bool {
1291        self.pairs.is_empty()
1292    }
1293}
1294/// A non-empty list (at least one element guaranteed).
1295#[allow(dead_code)]
1296pub struct NonEmptyVec<T> {
1297    head: T,
1298    tail: Vec<T>,
1299}
1300#[allow(dead_code)]
1301impl<T> NonEmptyVec<T> {
1302    /// Creates a non-empty vec with a single element.
1303    pub fn singleton(val: T) -> Self {
1304        Self {
1305            head: val,
1306            tail: Vec::new(),
1307        }
1308    }
1309    /// Pushes an element.
1310    pub fn push(&mut self, val: T) {
1311        self.tail.push(val);
1312    }
1313    /// Returns a reference to the first element.
1314    pub fn first(&self) -> &T {
1315        &self.head
1316    }
1317    /// Returns a reference to the last element.
1318    pub fn last(&self) -> &T {
1319        self.tail.last().unwrap_or(&self.head)
1320    }
1321    /// Returns the number of elements.
1322    pub fn len(&self) -> usize {
1323        1 + self.tail.len()
1324    }
1325    /// Returns whether the collection is empty.
1326    pub fn is_empty(&self) -> bool {
1327        self.len() == 0
1328    }
1329    /// Returns all elements as a Vec.
1330    pub fn to_vec(&self) -> Vec<&T> {
1331        let mut v = vec![&self.head];
1332        v.extend(self.tail.iter());
1333        v
1334    }
1335}
1336/// Cache for type inference results
1337pub struct InferCache {
1338    pub(crate) cache: LruCache<u64, SimplifiedExpr>,
1339}
1340impl InferCache {
1341    /// Create a new type inference cache
1342    pub fn new(capacity: usize) -> Self {
1343        InferCache {
1344            cache: LruCache::new(capacity),
1345        }
1346    }
1347    /// Look up inferred type for an expression
1348    pub fn lookup(&mut self, expr: &SimplifiedExpr) -> Option<SimplifiedExpr> {
1349        let hash = expr.hash();
1350        self.cache.get(&hash)
1351    }
1352    /// Store inferred type for an expression
1353    pub fn store(&mut self, expr: &SimplifiedExpr, inferred_type: SimplifiedExpr) {
1354        let hash = expr.hash();
1355        self.cache.insert(hash, inferred_type);
1356    }
1357    /// Clear the cache
1358    pub fn clear(&mut self) {
1359        self.cache.clear();
1360    }
1361    /// Get cache statistics
1362    pub fn stats(&self) -> (u64, u64) {
1363        self.cache.stats()
1364    }
1365    /// Get hit rate
1366    pub fn hit_rate(&self) -> f64 {
1367        self.cache.hit_rate()
1368    }
1369    /// Get cache size
1370    pub fn len(&self) -> usize {
1371        self.cache.len()
1372    }
1373    /// Check if empty
1374    pub fn is_empty(&self) -> bool {
1375        self.cache.is_empty()
1376    }
1377}
1378/// A pool of reusable string buffers.
1379#[allow(dead_code)]
1380pub struct StringPool {
1381    free: Vec<String>,
1382}
1383#[allow(dead_code)]
1384impl StringPool {
1385    /// Creates a new empty string pool.
1386    pub fn new() -> Self {
1387        Self { free: Vec::new() }
1388    }
1389    /// Takes a string from the pool (may be empty).
1390    pub fn take(&mut self) -> String {
1391        self.free.pop().unwrap_or_default()
1392    }
1393    /// Returns a string to the pool.
1394    pub fn give(&mut self, mut s: String) {
1395        s.clear();
1396        self.free.push(s);
1397    }
1398    /// Returns the number of free strings in the pool.
1399    pub fn free_count(&self) -> usize {
1400        self.free.len()
1401    }
1402}
1403/// Cache for weak head normal form (WHNF) reduction results
1404pub struct WhnfCache {
1405    pub(crate) cache: LruCache<u64, SimplifiedExpr>,
1406    transparency_mode: bool,
1407}
1408impl WhnfCache {
1409    /// Create a new WHNF cache
1410    pub fn new(capacity: usize, transparency_mode: bool) -> Self {
1411        WhnfCache {
1412            cache: LruCache::new(capacity),
1413            transparency_mode,
1414        }
1415    }
1416    /// Look up an expression's WHNF in the cache
1417    pub fn lookup(&mut self, expr: &SimplifiedExpr) -> Option<SimplifiedExpr> {
1418        if !self.transparency_mode {
1419            let hash = expr.hash();
1420            self.cache.get(&hash)
1421        } else {
1422            None
1423        }
1424    }
1425    /// Store an expression and its WHNF in the cache
1426    pub fn store(&mut self, expr: &SimplifiedExpr, whnf: SimplifiedExpr) {
1427        if !self.transparency_mode {
1428            let hash = expr.hash();
1429            self.cache.insert(hash, whnf);
1430        }
1431    }
1432    /// Clear the WHNF cache
1433    pub fn clear(&mut self) {
1434        self.cache.clear();
1435    }
1436    /// Get cache statistics
1437    pub fn stats(&self) -> (u64, u64) {
1438        self.cache.stats()
1439    }
1440    /// Get hit rate
1441    pub fn hit_rate(&self) -> f64 {
1442        self.cache.hit_rate()
1443    }
1444    /// Set transparency mode
1445    pub fn set_transparency(&mut self, mode: bool) {
1446        self.transparency_mode = mode;
1447        if mode {
1448            self.cache.clear();
1449        }
1450    }
1451    /// Get current transparency mode
1452    pub fn is_transparent(&self) -> bool {
1453        self.transparency_mode
1454    }
1455}
1456/// A versioned record that stores a history of values.
1457#[allow(dead_code)]
1458pub struct VersionedRecord<T: Clone> {
1459    /// Every earlier value, oldest first: version `n` is `earlier[n]`.
1460    earlier: Vec<T>,
1461    /// The latest value (version `earlier.len()`), declared last so it drops last.
1462    current: T,
1463}
1464#[allow(dead_code)]
1465impl<T: Clone> VersionedRecord<T> {
1466    /// Creates a new record with an initial value.
1467    pub fn new(initial: T) -> Self {
1468        Self {
1469            earlier: Vec::new(),
1470            current: initial,
1471        }
1472    }
1473    /// Updates the record with a new version.
1474    pub fn update(&mut self, val: T) {
1475        let previous = std::mem::replace(&mut self.current, val);
1476        self.earlier.push(previous);
1477    }
1478    /// Returns the current (latest) value.
1479    pub fn current(&self) -> &T {
1480        &self.current
1481    }
1482    /// Returns the value at version `n` (0-indexed), or `None`.
1483    pub fn at_version(&self, n: usize) -> Option<&T> {
1484        match self.earlier.get(n) {
1485            Some(value) => Some(value),
1486            None if n == self.earlier.len() => Some(&self.current),
1487            None => None,
1488        }
1489    }
1490    /// Returns the version number of the current value.
1491    pub fn version(&self) -> usize {
1492        self.earlier.len()
1493    }
1494    /// Returns `true` if more than one version exists.
1495    pub fn has_history(&self) -> bool {
1496        !self.earlier.is_empty()
1497    }
1498}
1499/// A simple TTL-based cache using a step counter as a clock.
1500///
1501/// Each entry has a `ttl` expressed in "steps". The cache is checked on every
1502/// `get`; stale entries are lazily removed.
1503pub struct TtlCache<K: Clone + Eq + std::hash::Hash, V: Clone> {
1504    entries: HashMap<K, TtlEntry<V>>,
1505    /// Monotonic step counter (incremented on every `tick()`).
1506    step: u64,
1507    /// Default TTL for new entries.
1508    default_ttl: u64,
1509}
1510impl<K: Clone + Eq + std::hash::Hash, V: Clone> TtlCache<K, V> {
1511    /// Create a new TTL cache.
1512    pub fn new(default_ttl: u64) -> Self {
1513        Self {
1514            entries: HashMap::new(),
1515            step: 0,
1516            default_ttl,
1517        }
1518    }
1519    /// Advance the clock by one step.
1520    pub fn tick(&mut self) {
1521        self.step += 1;
1522    }
1523    /// Advance the clock by `n` steps.
1524    pub fn tick_n(&mut self, n: u64) {
1525        self.step += n;
1526    }
1527    /// Insert an entry with the default TTL.
1528    pub fn insert(&mut self, key: K, value: V) {
1529        self.entries.insert(
1530            key,
1531            TtlEntry {
1532                value,
1533                expires_at: self.step + self.default_ttl,
1534            },
1535        );
1536    }
1537    /// Insert an entry with a custom TTL.
1538    pub fn insert_with_ttl(&mut self, key: K, value: V, ttl: u64) {
1539        self.entries.insert(
1540            key,
1541            TtlEntry {
1542                value,
1543                expires_at: self.step + ttl,
1544            },
1545        );
1546    }
1547    /// Get an entry if it hasn't expired.
1548    pub fn get(&self, key: &K) -> Option<V> {
1549        self.entries.get(key).and_then(|e| {
1550            if self.step < e.expires_at {
1551                Some(e.value.clone())
1552            } else {
1553                None
1554            }
1555        })
1556    }
1557    /// Remove expired entries.
1558    pub fn purge_expired(&mut self) {
1559        let step = self.step;
1560        self.entries.retain(|_, e| step < e.expires_at);
1561    }
1562    /// Number of entries (including possibly stale).
1563    pub fn len(&self) -> usize {
1564        self.entries.len()
1565    }
1566    /// Whether the cache is empty.
1567    pub fn is_empty(&self) -> bool {
1568        self.entries.is_empty()
1569    }
1570    /// Current step counter.
1571    pub fn current_step(&self) -> u64 {
1572        self.step
1573    }
1574    /// Clear all entries.
1575    pub fn clear(&mut self) {
1576        self.entries.clear();
1577    }
1578}
1579/// A write-once cell.
1580#[allow(dead_code)]
1581pub struct WriteOnce<T> {
1582    value: std::cell::Cell<Option<T>>,
1583}
1584#[allow(dead_code)]
1585impl<T: Copy> WriteOnce<T> {
1586    /// Creates an empty write-once cell.
1587    pub fn new() -> Self {
1588        Self {
1589            value: std::cell::Cell::new(None),
1590        }
1591    }
1592    /// Writes a value.  Returns `false` if already written.
1593    pub fn write(&self, val: T) -> bool {
1594        if self.value.get().is_some() {
1595            return false;
1596        }
1597        self.value.set(Some(val));
1598        true
1599    }
1600    /// Returns the value if written.
1601    pub fn read(&self) -> Option<T> {
1602        self.value.get()
1603    }
1604    /// Returns `true` if the value has been written.
1605    pub fn is_written(&self) -> bool {
1606        self.value.get().is_some()
1607    }
1608}
1609/// Node in the intrusive doubly-linked list for LRU tracking
1610#[derive(Clone, Debug)]
1611struct Node<K, V> {
1612    key: K,
1613    value: V,
1614    prev: Option<usize>,
1615    next: Option<usize>,
1616}
1617/// A label set for a graph node.
1618#[allow(dead_code)]
1619pub struct LabelSet {
1620    labels: Vec<String>,
1621}
1622#[allow(dead_code)]
1623impl LabelSet {
1624    /// Creates a new empty label set.
1625    pub fn new() -> Self {
1626        Self { labels: Vec::new() }
1627    }
1628    /// Adds a label (deduplicates).
1629    pub fn add(&mut self, label: impl Into<String>) {
1630        let s = label.into();
1631        if !self.labels.contains(&s) {
1632            self.labels.push(s);
1633        }
1634    }
1635    /// Returns `true` if `label` is present.
1636    pub fn has(&self, label: &str) -> bool {
1637        self.labels.iter().any(|l| l == label)
1638    }
1639    /// Returns the count of labels.
1640    pub fn count(&self) -> usize {
1641        self.labels.len()
1642    }
1643    /// Returns all labels.
1644    pub fn all(&self) -> &[String] {
1645        &self.labels
1646    }
1647}
1648/// A dependency closure builder (transitive closure via BFS).
1649#[allow(dead_code)]
1650pub struct TransitiveClosure {
1651    adj: Vec<Vec<usize>>,
1652    n: usize,
1653}
1654#[allow(dead_code)]
1655impl TransitiveClosure {
1656    /// Creates a transitive closure builder for `n` nodes.
1657    pub fn new(n: usize) -> Self {
1658        Self {
1659            adj: vec![Vec::new(); n],
1660            n,
1661        }
1662    }
1663    /// Adds a direct edge.
1664    pub fn add_edge(&mut self, from: usize, to: usize) {
1665        if from < self.n {
1666            self.adj[from].push(to);
1667        }
1668    }
1669    /// Computes all nodes reachable from `start` (including `start`).
1670    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
1671        let mut visited = vec![false; self.n];
1672        let mut queue = std::collections::VecDeque::new();
1673        queue.push_back(start);
1674        while let Some(node) = queue.pop_front() {
1675            if node >= self.n || visited[node] {
1676                continue;
1677            }
1678            visited[node] = true;
1679            for &next in &self.adj[node] {
1680                queue.push_back(next);
1681            }
1682        }
1683        (0..self.n).filter(|&i| visited[i]).collect()
1684    }
1685    /// Returns `true` if `from` can transitively reach `to`.
1686    pub fn can_reach(&self, from: usize, to: usize) -> bool {
1687        self.reachable_from(from).contains(&to)
1688    }
1689}
1690/// A simple key-value store backed by a sorted Vec for small maps.
1691#[allow(dead_code)]
1692pub struct SmallMap<K: Ord + Clone, V: Clone> {
1693    entries: Vec<(K, V)>,
1694}
1695#[allow(dead_code)]
1696impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
1697    /// Creates a new empty small map.
1698    pub fn new() -> Self {
1699        Self {
1700            entries: Vec::new(),
1701        }
1702    }
1703    /// Inserts or replaces the value for `key`.
1704    pub fn insert(&mut self, key: K, val: V) {
1705        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
1706            Ok(i) => self.entries[i].1 = val,
1707            Err(i) => self.entries.insert(i, (key, val)),
1708        }
1709    }
1710    /// Returns the value for `key`, or `None`.
1711    pub fn get(&self, key: &K) -> Option<&V> {
1712        self.entries
1713            .binary_search_by_key(&key, |(k, _)| k)
1714            .ok()
1715            .map(|i| &self.entries[i].1)
1716    }
1717    /// Returns the number of entries.
1718    pub fn len(&self) -> usize {
1719        self.entries.len()
1720    }
1721    /// Returns `true` if empty.
1722    pub fn is_empty(&self) -> bool {
1723        self.entries.is_empty()
1724    }
1725    /// Returns all keys.
1726    pub fn keys(&self) -> Vec<&K> {
1727        self.entries.iter().map(|(k, _)| k).collect()
1728    }
1729    /// Returns all values.
1730    pub fn values(&self) -> Vec<&V> {
1731        self.entries.iter().map(|(_, v)| v).collect()
1732    }
1733}