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: std::time::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: std::time::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 = std::time::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    /// Adds the top two values.  Panics if fewer than two values.
453    pub fn add(&mut self) {
454        let b = self
455            .stack
456            .pop()
457            .expect("stack must have at least two values for add");
458        let a = self
459            .stack
460            .pop()
461            .expect("stack must have at least two values for add");
462        self.stack.push(a + b);
463    }
464    /// Subtracts top from second.
465    pub fn sub(&mut self) {
466        let b = self
467            .stack
468            .pop()
469            .expect("stack must have at least two values for sub");
470        let a = self
471            .stack
472            .pop()
473            .expect("stack must have at least two values for sub");
474        self.stack.push(a - b);
475    }
476    /// Multiplies the top two values.
477    pub fn mul(&mut self) {
478        let b = self
479            .stack
480            .pop()
481            .expect("stack must have at least two values for mul");
482        let a = self
483            .stack
484            .pop()
485            .expect("stack must have at least two values for mul");
486        self.stack.push(a * b);
487    }
488    /// Peeks the top value.
489    pub fn peek(&self) -> Option<i64> {
490        self.stack.last().copied()
491    }
492    /// Returns the stack depth.
493    pub fn depth(&self) -> usize {
494        self.stack.len()
495    }
496}
497/// A counter that can measure elapsed time between snapshots.
498#[allow(dead_code)]
499pub struct Stopwatch {
500    start: std::time::Instant,
501    splits: Vec<f64>,
502}
503#[allow(dead_code)]
504impl Stopwatch {
505    /// Creates and starts a new stopwatch.
506    pub fn start() -> Self {
507        Self {
508            start: std::time::Instant::now(),
509            splits: Vec::new(),
510        }
511    }
512    /// Records a split time (elapsed since start).
513    pub fn split(&mut self) {
514        self.splits.push(self.elapsed_ms());
515    }
516    /// Returns total elapsed milliseconds since start.
517    pub fn elapsed_ms(&self) -> f64 {
518        self.start.elapsed().as_secs_f64() * 1000.0
519    }
520    /// Returns all recorded split times.
521    pub fn splits(&self) -> &[f64] {
522        &self.splits
523    }
524    /// Returns the number of splits.
525    pub fn num_splits(&self) -> usize {
526        self.splits.len()
527    }
528}
529/// Global cache manager for all kernel caches
530pub struct CacheManager {
531    pub(crate) whnf: WhnfCache,
532    pub(crate) defeq: DefEqCache,
533    pub(crate) infer: InferCache,
534}
535impl CacheManager {
536    /// Create a new cache manager with default capacities
537    pub fn new() -> Self {
538        CacheManager {
539            whnf: WhnfCache::new(1024, false),
540            defeq: DefEqCache::new(512),
541            infer: InferCache::new(256),
542        }
543    }
544    /// Create a cache manager with custom capacities
545    pub fn with_capacities(whnf_cap: usize, defeq_cap: usize, infer_cap: usize) -> Self {
546        CacheManager {
547            whnf: WhnfCache::new(whnf_cap, false),
548            defeq: DefEqCache::new(defeq_cap),
549            infer: InferCache::new(infer_cap),
550        }
551    }
552    /// Get mutable reference to WHNF cache
553    pub fn whnf_mut(&mut self) -> &mut WhnfCache {
554        &mut self.whnf
555    }
556    /// Get mutable reference to DefEq cache
557    pub fn defeq_mut(&mut self) -> &mut DefEqCache {
558        &mut self.defeq
559    }
560    /// Get mutable reference to Infer cache
561    pub fn infer_mut(&mut self) -> &mut InferCache {
562        &mut self.infer
563    }
564    /// Clear all caches
565    pub fn clear_all(&mut self) {
566        self.whnf.clear();
567        self.defeq.clear();
568        self.infer.clear();
569    }
570    /// Resize all caches to new capacities
571    pub fn resize_all(&mut self, whnf_cap: usize, defeq_cap: usize, infer_cap: usize) {
572        self.whnf = WhnfCache::new(whnf_cap, self.whnf.is_transparent());
573        self.defeq = DefEqCache::new(defeq_cap);
574        self.infer = InferCache::new(infer_cap);
575    }
576    /// Get comprehensive statistics for all caches
577    pub fn statistics(&self) -> CacheStatistics {
578        let (whnf_hits, whnf_misses) = self.whnf.stats();
579        let (defeq_hits, defeq_misses) = self.defeq.stats();
580        let (infer_hits, infer_misses) = self.infer.stats();
581        CacheStatistics {
582            whnf_hits,
583            whnf_misses,
584            whnf_hit_rate: self.whnf.hit_rate(),
585            defeq_hits,
586            defeq_misses,
587            defeq_hit_rate: self.defeq.hit_rate(),
588            infer_hits,
589            infer_misses,
590            infer_hit_rate: self.infer.hit_rate(),
591        }
592    }
593}
594/// Simplified Expr representation for hashing (mirrors actual Expr).
595///
596/// Used for caching purposes with a streamlined structure suitable for hashing.
597#[derive(Clone, Debug, PartialEq, Eq)]
598pub enum SimplifiedExpr {
599    /// A variable reference with the given name.
600    Var(String),
601    /// Function application: function and argument.
602    App(Box<SimplifiedExpr>, Box<SimplifiedExpr>),
603    /// Lambda abstraction: parameter name and body.
604    Lambda(String, Box<SimplifiedExpr>),
605    /// Pi type: parameter name, parameter type, and body type.
606    Pi(String, Box<SimplifiedExpr>, Box<SimplifiedExpr>),
607}
608impl SimplifiedExpr {
609    /// Compute FNV-1a hash for this expression
610    pub fn hash(&self) -> u64 {
611        match self {
612            SimplifiedExpr::Var(name) => {
613                let mut bytes = vec![0u8];
614                bytes.extend_from_slice(name.as_bytes());
615                fnv1a_hash(&bytes)
616            }
617            SimplifiedExpr::App(f, arg) => {
618                let f_hash = f.hash();
619                let arg_hash = arg.hash();
620                let mut bytes = vec![1u8];
621                bytes.extend_from_slice(&f_hash.to_le_bytes());
622                bytes.extend_from_slice(&arg_hash.to_le_bytes());
623                fnv1a_hash(&bytes)
624            }
625            SimplifiedExpr::Lambda(name, body) => {
626                let body_hash = body.hash();
627                let mut bytes = vec![2u8];
628                bytes.extend_from_slice(name.as_bytes());
629                bytes.extend_from_slice(&body_hash.to_le_bytes());
630                fnv1a_hash(&bytes)
631            }
632            SimplifiedExpr::Pi(name, typ, body) => {
633                let typ_hash = typ.hash();
634                let body_hash = body.hash();
635                let mut bytes = vec![3u8];
636                bytes.extend_from_slice(name.as_bytes());
637                bytes.extend_from_slice(&typ_hash.to_le_bytes());
638                bytes.extend_from_slice(&body_hash.to_le_bytes());
639                fnv1a_hash(&bytes)
640            }
641        }
642    }
643}
644/// A window iterator that yields overlapping windows of size `n`.
645#[allow(dead_code)]
646pub struct WindowIterator<'a, T> {
647    pub(super) data: &'a [T],
648    pub(super) pos: usize,
649    pub(super) window: usize,
650}
651#[allow(dead_code)]
652impl<'a, T> WindowIterator<'a, T> {
653    /// Creates a new window iterator.
654    pub fn new(data: &'a [T], window: usize) -> Self {
655        Self {
656            data,
657            pos: 0,
658            window,
659        }
660    }
661}
662/// A set of rewrite rules.
663#[allow(dead_code)]
664pub struct RewriteRuleSet {
665    rules: Vec<RewriteRule>,
666}
667#[allow(dead_code)]
668impl RewriteRuleSet {
669    /// Creates an empty rule set.
670    pub fn new() -> Self {
671        Self { rules: Vec::new() }
672    }
673    /// Adds a rule.
674    pub fn add(&mut self, rule: RewriteRule) {
675        self.rules.push(rule);
676    }
677    /// Returns the number of rules.
678    pub fn len(&self) -> usize {
679        self.rules.len()
680    }
681    /// Returns `true` if the set is empty.
682    pub fn is_empty(&self) -> bool {
683        self.rules.is_empty()
684    }
685    /// Returns all conditional rules.
686    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
687        self.rules.iter().filter(|r| r.conditional).collect()
688    }
689    /// Returns all unconditional rules.
690    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
691        self.rules.iter().filter(|r| !r.conditional).collect()
692    }
693    /// Looks up a rule by name.
694    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
695        self.rules.iter().find(|r| r.name == name)
696    }
697}
698/// Generic LRU Cache with HashMap + Vec-based intrusive list
699pub struct LruCache<K: Clone + Eq + std::hash::Hash, V: Clone> {
700    capacity: usize,
701    map: HashMap<K, usize>,
702    nodes: Vec<Node<K, V>>,
703    head: Option<usize>,
704    tail: Option<usize>,
705    hits: u64,
706    misses: u64,
707}
708impl<K: Clone + Eq + std::hash::Hash, V: Clone> LruCache<K, V> {
709    /// Create a new LRU cache with specified capacity
710    pub fn new(capacity: usize) -> Self {
711        assert!(capacity > 0, "LRU cache capacity must be > 0");
712        LruCache {
713            capacity,
714            map: HashMap::new(),
715            nodes: Vec::new(),
716            head: None,
717            tail: None,
718            hits: 0,
719            misses: 0,
720        }
721    }
722    /// Get a value by key and move it to the most recently used position
723    pub fn get(&mut self, key: &K) -> Option<V> {
724        if let Some(&idx) = self.map.get(key) {
725            self.hits += 1;
726            self.move_to_head(idx);
727            Some(self.nodes[idx].value.clone())
728        } else {
729            self.misses += 1;
730            None
731        }
732    }
733    /// Insert a key-value pair into the cache
734    pub fn insert(&mut self, key: K, value: V) {
735        if let Some(&idx) = self.map.get(&key) {
736            self.nodes[idx].value = value;
737            self.move_to_head(idx);
738        } else {
739            if self.nodes.len() >= self.capacity {
740                self.evict_lru();
741            }
742            let new_idx = self.nodes.len();
743            let node = Node {
744                key: key.clone(),
745                value,
746                prev: None,
747                next: self.head,
748            };
749            self.nodes.push(node);
750            self.map.insert(key, new_idx);
751            if let Some(old_head) = self.head {
752                self.nodes[old_head].prev = Some(new_idx);
753            }
754            self.head = Some(new_idx);
755            if self.tail.is_none() {
756                self.tail = Some(new_idx);
757            }
758        }
759    }
760    /// Remove a key from the cache
761    pub fn remove(&mut self, key: &K) -> Option<V> {
762        if let Some(&idx) = self.map.get(key) {
763            let node = &self.nodes[idx];
764            let prev = node.prev;
765            let next = node.next;
766            if let Some(p) = prev {
767                self.nodes[p].next = next;
768            } else {
769                self.tail = next;
770            }
771            if let Some(n) = next {
772                self.nodes[n].prev = prev;
773            } else {
774                self.head = prev;
775            }
776            self.map.remove(key);
777            Some(self.nodes[idx].value.clone())
778        } else {
779            None
780        }
781    }
782    /// Check if key exists in the cache
783    pub fn contains_key(&self, key: &K) -> bool {
784        self.map.contains_key(key)
785    }
786    /// Get the number of entries in the cache
787    pub fn len(&self) -> usize {
788        self.map.len()
789    }
790    /// Check if cache is empty
791    pub fn is_empty(&self) -> bool {
792        self.map.is_empty()
793    }
794    /// Get the capacity of the cache
795    pub fn capacity(&self) -> usize {
796        self.capacity
797    }
798    /// Clear all entries from the cache
799    pub fn clear(&mut self) {
800        self.map.clear();
801        self.nodes.clear();
802        self.head = None;
803        self.tail = None;
804        self.hits = 0;
805        self.misses = 0;
806    }
807    /// Get cache statistics
808    pub fn stats(&self) -> (u64, u64) {
809        (self.hits, self.misses)
810    }
811    /// Get hit rate as a percentage (0.0 to 100.0)
812    pub fn hit_rate(&self) -> f64 {
813        let total = self.hits + self.misses;
814        if total == 0 {
815            0.0
816        } else {
817            (self.hits as f64 / total as f64) * 100.0
818        }
819    }
820    fn move_to_head(&mut self, idx: usize) {
821        if self.head == Some(idx) {
822            return;
823        }
824        let prev = self.nodes[idx].prev;
825        let next = self.nodes[idx].next;
826        if let Some(p) = prev {
827            self.nodes[p].next = next;
828        }
829        if let Some(n) = next {
830            self.nodes[n].prev = prev;
831        } else {
832            self.tail = prev;
833        }
834        self.nodes[idx].prev = None;
835        self.nodes[idx].next = self.head;
836        if let Some(old_head) = self.head {
837            self.nodes[old_head].prev = Some(idx);
838        }
839        self.head = Some(idx);
840    }
841    fn evict_lru(&mut self) {
842        if let Some(tail_idx) = self.tail {
843            let key = self.nodes[tail_idx].key.clone();
844            let prev = self.nodes[tail_idx].prev;
845            if let Some(p) = prev {
846                self.nodes[p].next = None;
847                self.head = Some(p);
848            } else {
849                self.head = None;
850            }
851            self.tail = prev;
852            self.map.remove(&key);
853            self.nodes.remove(tail_idx);
854            self.nodes.iter().enumerate().for_each(|(i, node)| {
855                *self
856                    .map
857                    .get_mut(&node.key)
858                    .expect("node key must exist in map") = i;
859            });
860        }
861    }
862}
863/// A very simple approximate membership test using bit-hashing.
864///
865/// This is NOT a production-quality bloom filter; it uses only two hash
866/// functions and a small bit array for illustration.
867pub struct BloomFilterApprox {
868    bits: Vec<bool>,
869    size: usize,
870}
871impl BloomFilterApprox {
872    /// Create a bloom filter with `size` bits.
873    pub fn new(size: usize) -> Self {
874        Self {
875            bits: vec![false; size],
876            size,
877        }
878    }
879    fn hash1(data: &[u8], size: usize) -> usize {
880        fnv1a_hash(data) as usize % size
881    }
882    fn hash2(data: &[u8], size: usize) -> usize {
883        let h = fnv1a_hash(data).wrapping_mul(0x9e3779b9_7f4a7c15);
884        h as usize % size
885    }
886    /// Insert a key.
887    pub fn insert<T: AsRef<[u8]>>(&mut self, key: T) {
888        let bytes = key.as_ref();
889        let h1 = Self::hash1(bytes, self.size);
890        let h2 = Self::hash2(bytes, self.size);
891        self.bits[h1] = true;
892        self.bits[h2] = true;
893    }
894    /// Check if a key might be present (may have false positives).
895    pub fn might_contain<T: AsRef<[u8]>>(&self, key: T) -> bool {
896        let bytes = key.as_ref();
897        let h1 = Self::hash1(bytes, self.size);
898        let h2 = Self::hash2(bytes, self.size);
899        self.bits[h1] && self.bits[h2]
900    }
901    /// Clear all bits.
902    pub fn clear(&mut self) {
903        self.bits.iter_mut().for_each(|b| *b = false);
904    }
905    /// Number of set bits.
906    pub fn set_bit_count(&self) -> usize {
907        self.bits.iter().filter(|&&b| b).count()
908    }
909    /// Filter size in bits.
910    pub fn size(&self) -> usize {
911        self.size
912    }
913}
914/// A two-level cache hierarchy.
915///
916/// L1 is a small, fast LRU cache. L2 is a larger LRU cache. On a miss in L1,
917/// L2 is checked and the result is promoted to L1.
918pub struct MultiLevelCache<K: Clone + Eq + std::hash::Hash, V: Clone> {
919    l1: LruCache<K, V>,
920    l2: LruCache<K, V>,
921    l1_hits: u64,
922    l2_hits: u64,
923    misses: u64,
924}
925impl<K: Clone + Eq + std::hash::Hash, V: Clone> MultiLevelCache<K, V> {
926    /// Create a two-level cache.
927    pub fn new(l1_cap: usize, l2_cap: usize) -> Self {
928        Self {
929            l1: LruCache::new(l1_cap),
930            l2: LruCache::new(l2_cap),
931            l1_hits: 0,
932            l2_hits: 0,
933            misses: 0,
934        }
935    }
936    /// Get a value, checking L1 first, then L2.
937    pub fn get(&mut self, key: &K) -> Option<V> {
938        if let Some(v) = self.l1.get(key) {
939            self.l1_hits += 1;
940            return Some(v);
941        }
942        if let Some(v) = self.l2.get(key) {
943            self.l2_hits += 1;
944            self.l1.insert(key.clone(), v.clone());
945            return Some(v);
946        }
947        self.misses += 1;
948        None
949    }
950    /// Insert into both L1 and L2.
951    pub fn insert(&mut self, key: K, value: V) {
952        self.l1.insert(key.clone(), value.clone());
953        self.l2.insert(key, value);
954    }
955    /// Insert only into L2 (for pre-warming the L2 cache).
956    pub fn insert_l2_only(&mut self, key: K, value: V) {
957        self.l2.insert(key, value);
958    }
959    /// Clear L1 only.
960    pub fn clear_l1(&mut self) {
961        self.l1.clear();
962    }
963    /// Clear both levels.
964    pub fn clear_all(&mut self) {
965        self.l1.clear();
966        self.l2.clear();
967        self.l1_hits = 0;
968        self.l2_hits = 0;
969        self.misses = 0;
970    }
971    /// L1 hit count.
972    pub fn l1_hits(&self) -> u64 {
973        self.l1_hits
974    }
975    /// L2 hit count.
976    pub fn l2_hits(&self) -> u64 {
977        self.l2_hits
978    }
979    /// Miss count.
980    pub fn misses(&self) -> u64 {
981        self.misses
982    }
983    /// Total requests served.
984    pub fn total_requests(&self) -> u64 {
985        self.l1_hits + self.l2_hits + self.misses
986    }
987    /// Overall hit rate.
988    pub fn hit_rate(&self) -> f64 {
989        let total = self.total_requests();
990        if total == 0 {
991            0.0
992        } else {
993            ((self.l1_hits + self.l2_hits) as f64 / total as f64) * 100.0
994        }
995    }
996}
997/// Symmetry-aware cache for definitional equality checks
998pub struct DefEqCache {
999    pub(crate) cache: LruCache<(u64, u64), bool>,
1000}
1001impl DefEqCache {
1002    /// Create a new definitional equality cache
1003    pub fn new(capacity: usize) -> Self {
1004        DefEqCache {
1005            cache: LruCache::new(capacity),
1006        }
1007    }
1008    /// Check if a definitional equality result is cached (symmetry-aware)
1009    pub fn check_cache(&mut self, expr1: &SimplifiedExpr, expr2: &SimplifiedExpr) -> Option<bool> {
1010        let hash1 = expr1.hash();
1011        let hash2 = expr2.hash();
1012        if let Some(result) = self.cache.get(&(hash1, hash2)) {
1013            return Some(result);
1014        }
1015        if let Some(result) = self.cache.get(&(hash2, hash1)) {
1016            return Some(result);
1017        }
1018        None
1019    }
1020    /// Store a definitional equality result (symmetry-aware)
1021    pub fn store_result(&mut self, expr1: &SimplifiedExpr, expr2: &SimplifiedExpr, result: bool) {
1022        let hash1 = expr1.hash();
1023        let hash2 = expr2.hash();
1024        self.cache.insert((hash1, hash2), result);
1025        self.cache.insert((hash2, hash1), result);
1026    }
1027    /// Clear the cache
1028    pub fn clear(&mut self) {
1029        self.cache.clear();
1030    }
1031    /// Get cache statistics
1032    pub fn stats(&self) -> (u64, u64) {
1033        self.cache.stats()
1034    }
1035    /// Get hit rate
1036    pub fn hit_rate(&self) -> f64 {
1037        self.cache.hit_rate()
1038    }
1039}
1040/// A mutable reference stack for tracking the current "focus" in a tree traversal.
1041#[allow(dead_code)]
1042pub struct FocusStack<T> {
1043    items: Vec<T>,
1044}
1045#[allow(dead_code)]
1046impl<T> FocusStack<T> {
1047    /// Creates an empty focus stack.
1048    pub fn new() -> Self {
1049        Self { items: Vec::new() }
1050    }
1051    /// Focuses on `item`.
1052    pub fn focus(&mut self, item: T) {
1053        self.items.push(item);
1054    }
1055    /// Blurs (pops) the current focus.
1056    pub fn blur(&mut self) -> Option<T> {
1057        self.items.pop()
1058    }
1059    /// Returns the current focus, or `None`.
1060    pub fn current(&self) -> Option<&T> {
1061        self.items.last()
1062    }
1063    /// Returns the focus depth.
1064    pub fn depth(&self) -> usize {
1065        self.items.len()
1066    }
1067    /// Returns `true` if there is no current focus.
1068    pub fn is_empty(&self) -> bool {
1069        self.items.is_empty()
1070    }
1071}
1072/// A generic counter that tracks min/max/sum for statistical summaries.
1073#[allow(dead_code)]
1074pub struct StatSummary {
1075    count: u64,
1076    sum: f64,
1077    min: f64,
1078    max: f64,
1079}
1080#[allow(dead_code)]
1081impl StatSummary {
1082    /// Creates an empty summary.
1083    pub fn new() -> Self {
1084        Self {
1085            count: 0,
1086            sum: 0.0,
1087            min: f64::INFINITY,
1088            max: f64::NEG_INFINITY,
1089        }
1090    }
1091    /// Records a sample.
1092    pub fn record(&mut self, val: f64) {
1093        self.count += 1;
1094        self.sum += val;
1095        if val < self.min {
1096            self.min = val;
1097        }
1098        if val > self.max {
1099            self.max = val;
1100        }
1101    }
1102    /// Returns the mean, or `None` if no samples.
1103    pub fn mean(&self) -> Option<f64> {
1104        if self.count == 0 {
1105            None
1106        } else {
1107            Some(self.sum / self.count as f64)
1108        }
1109    }
1110    /// Returns the minimum, or `None` if no samples.
1111    pub fn min(&self) -> Option<f64> {
1112        if self.count == 0 {
1113            None
1114        } else {
1115            Some(self.min)
1116        }
1117    }
1118    /// Returns the maximum, or `None` if no samples.
1119    pub fn max(&self) -> Option<f64> {
1120        if self.count == 0 {
1121            None
1122        } else {
1123            Some(self.max)
1124        }
1125    }
1126    /// Returns the count of recorded samples.
1127    pub fn count(&self) -> u64 {
1128        self.count
1129    }
1130}
1131#[derive(Clone, Debug)]
1132struct TtlEntry<V> {
1133    value: V,
1134    expires_at: u64,
1135}
1136/// A sparse vector: stores only non-default elements.
1137#[allow(dead_code)]
1138pub struct SparseVec<T: Default + Clone + PartialEq> {
1139    entries: std::collections::HashMap<usize, T>,
1140    default_: T,
1141    logical_len: usize,
1142}
1143#[allow(dead_code)]
1144impl<T: Default + Clone + PartialEq> SparseVec<T> {
1145    /// Creates a new sparse vector with logical length `len`.
1146    pub fn new(len: usize) -> Self {
1147        Self {
1148            entries: std::collections::HashMap::new(),
1149            default_: T::default(),
1150            logical_len: len,
1151        }
1152    }
1153    /// Sets element at `idx`.
1154    pub fn set(&mut self, idx: usize, val: T) {
1155        if val == self.default_ {
1156            self.entries.remove(&idx);
1157        } else {
1158            self.entries.insert(idx, val);
1159        }
1160    }
1161    /// Gets element at `idx`.
1162    pub fn get(&self, idx: usize) -> &T {
1163        self.entries.get(&idx).unwrap_or(&self.default_)
1164    }
1165    /// Returns the logical length.
1166    pub fn len(&self) -> usize {
1167        self.logical_len
1168    }
1169    /// Returns whether the collection is empty.
1170    pub fn is_empty(&self) -> bool {
1171        self.len() == 0
1172    }
1173    /// Returns the number of non-default elements.
1174    pub fn nnz(&self) -> usize {
1175        self.entries.len()
1176    }
1177}
1178/// Represents a rewrite rule `lhs → rhs`.
1179#[allow(dead_code)]
1180#[allow(missing_docs)]
1181pub struct RewriteRule {
1182    /// The name of the rule.
1183    pub name: String,
1184    /// A string representation of the LHS pattern.
1185    pub lhs: String,
1186    /// A string representation of the RHS.
1187    pub rhs: String,
1188    /// Whether this is a conditional rule (has side conditions).
1189    pub conditional: bool,
1190}
1191#[allow(dead_code)]
1192impl RewriteRule {
1193    /// Creates an unconditional rewrite rule.
1194    pub fn unconditional(
1195        name: impl Into<String>,
1196        lhs: impl Into<String>,
1197        rhs: impl Into<String>,
1198    ) -> Self {
1199        Self {
1200            name: name.into(),
1201            lhs: lhs.into(),
1202            rhs: rhs.into(),
1203            conditional: false,
1204        }
1205    }
1206    /// Creates a conditional rewrite rule.
1207    pub fn conditional(
1208        name: impl Into<String>,
1209        lhs: impl Into<String>,
1210        rhs: impl Into<String>,
1211    ) -> Self {
1212        Self {
1213            name: name.into(),
1214            lhs: lhs.into(),
1215            rhs: rhs.into(),
1216            conditional: true,
1217        }
1218    }
1219    /// Returns a textual representation.
1220    pub fn display(&self) -> String {
1221        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
1222    }
1223}
1224/// Statistics for all caches.
1225///
1226/// Provides comprehensive metrics for monitoring cache performance across all cache types.
1227#[derive(Clone, Debug)]
1228pub struct CacheStatistics {
1229    /// Number of hits in the WHNF cache.
1230    pub whnf_hits: u64,
1231    /// Number of misses in the WHNF cache.
1232    pub whnf_misses: u64,
1233    /// Hit rate percentage for the WHNF cache (0.0-100.0).
1234    pub whnf_hit_rate: f64,
1235    /// Number of hits in the DefEq cache.
1236    pub defeq_hits: u64,
1237    /// Number of misses in the DefEq cache.
1238    pub defeq_misses: u64,
1239    /// Hit rate percentage for the DefEq cache (0.0-100.0).
1240    pub defeq_hit_rate: f64,
1241    /// Number of hits in the Infer cache.
1242    pub infer_hits: u64,
1243    /// Number of misses in the Infer cache.
1244    pub infer_misses: u64,
1245    /// Hit rate percentage for the Infer cache (0.0-100.0).
1246    pub infer_hit_rate: f64,
1247}
1248impl CacheStatistics {
1249    /// Get total hits across all caches
1250    pub fn total_hits(&self) -> u64 {
1251        self.whnf_hits + self.defeq_hits + self.infer_hits
1252    }
1253    /// Get total misses across all caches
1254    pub fn total_misses(&self) -> u64 {
1255        self.whnf_misses + self.defeq_misses + self.infer_misses
1256    }
1257    /// Get overall hit rate across all caches
1258    pub fn overall_hit_rate(&self) -> f64 {
1259        let total = self.total_hits() + self.total_misses();
1260        if total == 0 {
1261            0.0
1262        } else {
1263            (self.total_hits() as f64 / total as f64) * 100.0
1264        }
1265    }
1266}
1267/// A flat list of substitution pairs `(from, to)`.
1268#[allow(dead_code)]
1269pub struct FlatSubstitution {
1270    pairs: Vec<(String, String)>,
1271}
1272#[allow(dead_code)]
1273impl FlatSubstitution {
1274    /// Creates an empty substitution.
1275    pub fn new() -> Self {
1276        Self { pairs: Vec::new() }
1277    }
1278    /// Adds a pair.
1279    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
1280        self.pairs.push((from.into(), to.into()));
1281    }
1282    /// Applies all substitutions to `s` (leftmost-first order).
1283    pub fn apply(&self, s: &str) -> String {
1284        let mut result = s.to_string();
1285        for (from, to) in &self.pairs {
1286            result = result.replace(from.as_str(), to.as_str());
1287        }
1288        result
1289    }
1290    /// Returns the number of pairs.
1291    pub fn len(&self) -> usize {
1292        self.pairs.len()
1293    }
1294    /// Returns `true` if empty.
1295    pub fn is_empty(&self) -> bool {
1296        self.pairs.is_empty()
1297    }
1298}
1299/// A non-empty list (at least one element guaranteed).
1300#[allow(dead_code)]
1301pub struct NonEmptyVec<T> {
1302    head: T,
1303    tail: Vec<T>,
1304}
1305#[allow(dead_code)]
1306impl<T> NonEmptyVec<T> {
1307    /// Creates a non-empty vec with a single element.
1308    pub fn singleton(val: T) -> Self {
1309        Self {
1310            head: val,
1311            tail: Vec::new(),
1312        }
1313    }
1314    /// Pushes an element.
1315    pub fn push(&mut self, val: T) {
1316        self.tail.push(val);
1317    }
1318    /// Returns a reference to the first element.
1319    pub fn first(&self) -> &T {
1320        &self.head
1321    }
1322    /// Returns a reference to the last element.
1323    pub fn last(&self) -> &T {
1324        self.tail.last().unwrap_or(&self.head)
1325    }
1326    /// Returns the number of elements.
1327    pub fn len(&self) -> usize {
1328        1 + self.tail.len()
1329    }
1330    /// Returns whether the collection is empty.
1331    pub fn is_empty(&self) -> bool {
1332        self.len() == 0
1333    }
1334    /// Returns all elements as a Vec.
1335    pub fn to_vec(&self) -> Vec<&T> {
1336        let mut v = vec![&self.head];
1337        v.extend(self.tail.iter());
1338        v
1339    }
1340}
1341/// Cache for type inference results
1342pub struct InferCache {
1343    pub(crate) cache: LruCache<u64, SimplifiedExpr>,
1344}
1345impl InferCache {
1346    /// Create a new type inference cache
1347    pub fn new(capacity: usize) -> Self {
1348        InferCache {
1349            cache: LruCache::new(capacity),
1350        }
1351    }
1352    /// Look up inferred type for an expression
1353    pub fn lookup(&mut self, expr: &SimplifiedExpr) -> Option<SimplifiedExpr> {
1354        let hash = expr.hash();
1355        self.cache.get(&hash)
1356    }
1357    /// Store inferred type for an expression
1358    pub fn store(&mut self, expr: &SimplifiedExpr, inferred_type: SimplifiedExpr) {
1359        let hash = expr.hash();
1360        self.cache.insert(hash, inferred_type);
1361    }
1362    /// Clear the cache
1363    pub fn clear(&mut self) {
1364        self.cache.clear();
1365    }
1366    /// Get cache statistics
1367    pub fn stats(&self) -> (u64, u64) {
1368        self.cache.stats()
1369    }
1370    /// Get hit rate
1371    pub fn hit_rate(&self) -> f64 {
1372        self.cache.hit_rate()
1373    }
1374    /// Get cache size
1375    pub fn len(&self) -> usize {
1376        self.cache.len()
1377    }
1378    /// Check if empty
1379    pub fn is_empty(&self) -> bool {
1380        self.cache.is_empty()
1381    }
1382}
1383/// A pool of reusable string buffers.
1384#[allow(dead_code)]
1385pub struct StringPool {
1386    free: Vec<String>,
1387}
1388#[allow(dead_code)]
1389impl StringPool {
1390    /// Creates a new empty string pool.
1391    pub fn new() -> Self {
1392        Self { free: Vec::new() }
1393    }
1394    /// Takes a string from the pool (may be empty).
1395    pub fn take(&mut self) -> String {
1396        self.free.pop().unwrap_or_default()
1397    }
1398    /// Returns a string to the pool.
1399    pub fn give(&mut self, mut s: String) {
1400        s.clear();
1401        self.free.push(s);
1402    }
1403    /// Returns the number of free strings in the pool.
1404    pub fn free_count(&self) -> usize {
1405        self.free.len()
1406    }
1407}
1408/// Cache for weak head normal form (WHNF) reduction results
1409pub struct WhnfCache {
1410    pub(crate) cache: LruCache<u64, SimplifiedExpr>,
1411    transparency_mode: bool,
1412}
1413impl WhnfCache {
1414    /// Create a new WHNF cache
1415    pub fn new(capacity: usize, transparency_mode: bool) -> Self {
1416        WhnfCache {
1417            cache: LruCache::new(capacity),
1418            transparency_mode,
1419        }
1420    }
1421    /// Look up an expression's WHNF in the cache
1422    pub fn lookup(&mut self, expr: &SimplifiedExpr) -> Option<SimplifiedExpr> {
1423        if !self.transparency_mode {
1424            let hash = expr.hash();
1425            self.cache.get(&hash)
1426        } else {
1427            None
1428        }
1429    }
1430    /// Store an expression and its WHNF in the cache
1431    pub fn store(&mut self, expr: &SimplifiedExpr, whnf: SimplifiedExpr) {
1432        if !self.transparency_mode {
1433            let hash = expr.hash();
1434            self.cache.insert(hash, whnf);
1435        }
1436    }
1437    /// Clear the WHNF cache
1438    pub fn clear(&mut self) {
1439        self.cache.clear();
1440    }
1441    /// Get cache statistics
1442    pub fn stats(&self) -> (u64, u64) {
1443        self.cache.stats()
1444    }
1445    /// Get hit rate
1446    pub fn hit_rate(&self) -> f64 {
1447        self.cache.hit_rate()
1448    }
1449    /// Set transparency mode
1450    pub fn set_transparency(&mut self, mode: bool) {
1451        self.transparency_mode = mode;
1452        if mode {
1453            self.cache.clear();
1454        }
1455    }
1456    /// Get current transparency mode
1457    pub fn is_transparent(&self) -> bool {
1458        self.transparency_mode
1459    }
1460}
1461/// A versioned record that stores a history of values.
1462#[allow(dead_code)]
1463pub struct VersionedRecord<T: Clone> {
1464    history: Vec<T>,
1465}
1466#[allow(dead_code)]
1467impl<T: Clone> VersionedRecord<T> {
1468    /// Creates a new record with an initial value.
1469    pub fn new(initial: T) -> Self {
1470        Self {
1471            history: vec![initial],
1472        }
1473    }
1474    /// Updates the record with a new version.
1475    pub fn update(&mut self, val: T) {
1476        self.history.push(val);
1477    }
1478    /// Returns the current (latest) value.
1479    pub fn current(&self) -> &T {
1480        self.history
1481            .last()
1482            .expect("VersionedRecord history is always non-empty after construction")
1483    }
1484    /// Returns the value at version `n` (0-indexed), or `None`.
1485    pub fn at_version(&self, n: usize) -> Option<&T> {
1486        self.history.get(n)
1487    }
1488    /// Returns the version number of the current value.
1489    pub fn version(&self) -> usize {
1490        self.history.len() - 1
1491    }
1492    /// Returns `true` if more than one version exists.
1493    pub fn has_history(&self) -> bool {
1494        self.history.len() > 1
1495    }
1496}
1497/// A simple TTL-based cache using a step counter as a clock.
1498///
1499/// Each entry has a `ttl` expressed in "steps". The cache is checked on every
1500/// `get`; stale entries are lazily removed.
1501pub struct TtlCache<K: Clone + Eq + std::hash::Hash, V: Clone> {
1502    entries: HashMap<K, TtlEntry<V>>,
1503    /// Monotonic step counter (incremented on every `tick()`).
1504    step: u64,
1505    /// Default TTL for new entries.
1506    default_ttl: u64,
1507}
1508impl<K: Clone + Eq + std::hash::Hash, V: Clone> TtlCache<K, V> {
1509    /// Create a new TTL cache.
1510    pub fn new(default_ttl: u64) -> Self {
1511        Self {
1512            entries: HashMap::new(),
1513            step: 0,
1514            default_ttl,
1515        }
1516    }
1517    /// Advance the clock by one step.
1518    pub fn tick(&mut self) {
1519        self.step += 1;
1520    }
1521    /// Advance the clock by `n` steps.
1522    pub fn tick_n(&mut self, n: u64) {
1523        self.step += n;
1524    }
1525    /// Insert an entry with the default TTL.
1526    pub fn insert(&mut self, key: K, value: V) {
1527        self.entries.insert(
1528            key,
1529            TtlEntry {
1530                value,
1531                expires_at: self.step + self.default_ttl,
1532            },
1533        );
1534    }
1535    /// Insert an entry with a custom TTL.
1536    pub fn insert_with_ttl(&mut self, key: K, value: V, ttl: u64) {
1537        self.entries.insert(
1538            key,
1539            TtlEntry {
1540                value,
1541                expires_at: self.step + ttl,
1542            },
1543        );
1544    }
1545    /// Get an entry if it hasn't expired.
1546    pub fn get(&self, key: &K) -> Option<V> {
1547        self.entries.get(key).and_then(|e| {
1548            if self.step < e.expires_at {
1549                Some(e.value.clone())
1550            } else {
1551                None
1552            }
1553        })
1554    }
1555    /// Remove expired entries.
1556    pub fn purge_expired(&mut self) {
1557        let step = self.step;
1558        self.entries.retain(|_, e| step < e.expires_at);
1559    }
1560    /// Number of entries (including possibly stale).
1561    pub fn len(&self) -> usize {
1562        self.entries.len()
1563    }
1564    /// Whether the cache is empty.
1565    pub fn is_empty(&self) -> bool {
1566        self.entries.is_empty()
1567    }
1568    /// Current step counter.
1569    pub fn current_step(&self) -> u64 {
1570        self.step
1571    }
1572    /// Clear all entries.
1573    pub fn clear(&mut self) {
1574        self.entries.clear();
1575    }
1576}
1577/// A write-once cell.
1578#[allow(dead_code)]
1579pub struct WriteOnce<T> {
1580    value: std::cell::Cell<Option<T>>,
1581}
1582#[allow(dead_code)]
1583impl<T: Copy> WriteOnce<T> {
1584    /// Creates an empty write-once cell.
1585    pub fn new() -> Self {
1586        Self {
1587            value: std::cell::Cell::new(None),
1588        }
1589    }
1590    /// Writes a value.  Returns `false` if already written.
1591    pub fn write(&self, val: T) -> bool {
1592        if self.value.get().is_some() {
1593            return false;
1594        }
1595        self.value.set(Some(val));
1596        true
1597    }
1598    /// Returns the value if written.
1599    pub fn read(&self) -> Option<T> {
1600        self.value.get()
1601    }
1602    /// Returns `true` if the value has been written.
1603    pub fn is_written(&self) -> bool {
1604        self.value.get().is_some()
1605    }
1606}
1607/// Node in the intrusive doubly-linked list for LRU tracking
1608#[derive(Clone, Debug)]
1609struct Node<K, V> {
1610    key: K,
1611    value: V,
1612    prev: Option<usize>,
1613    next: Option<usize>,
1614}
1615/// A label set for a graph node.
1616#[allow(dead_code)]
1617pub struct LabelSet {
1618    labels: Vec<String>,
1619}
1620#[allow(dead_code)]
1621impl LabelSet {
1622    /// Creates a new empty label set.
1623    pub fn new() -> Self {
1624        Self { labels: Vec::new() }
1625    }
1626    /// Adds a label (deduplicates).
1627    pub fn add(&mut self, label: impl Into<String>) {
1628        let s = label.into();
1629        if !self.labels.contains(&s) {
1630            self.labels.push(s);
1631        }
1632    }
1633    /// Returns `true` if `label` is present.
1634    pub fn has(&self, label: &str) -> bool {
1635        self.labels.iter().any(|l| l == label)
1636    }
1637    /// Returns the count of labels.
1638    pub fn count(&self) -> usize {
1639        self.labels.len()
1640    }
1641    /// Returns all labels.
1642    pub fn all(&self) -> &[String] {
1643        &self.labels
1644    }
1645}
1646/// A dependency closure builder (transitive closure via BFS).
1647#[allow(dead_code)]
1648pub struct TransitiveClosure {
1649    adj: Vec<Vec<usize>>,
1650    n: usize,
1651}
1652#[allow(dead_code)]
1653impl TransitiveClosure {
1654    /// Creates a transitive closure builder for `n` nodes.
1655    pub fn new(n: usize) -> Self {
1656        Self {
1657            adj: vec![Vec::new(); n],
1658            n,
1659        }
1660    }
1661    /// Adds a direct edge.
1662    pub fn add_edge(&mut self, from: usize, to: usize) {
1663        if from < self.n {
1664            self.adj[from].push(to);
1665        }
1666    }
1667    /// Computes all nodes reachable from `start` (including `start`).
1668    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
1669        let mut visited = vec![false; self.n];
1670        let mut queue = std::collections::VecDeque::new();
1671        queue.push_back(start);
1672        while let Some(node) = queue.pop_front() {
1673            if node >= self.n || visited[node] {
1674                continue;
1675            }
1676            visited[node] = true;
1677            for &next in &self.adj[node] {
1678                queue.push_back(next);
1679            }
1680        }
1681        (0..self.n).filter(|&i| visited[i]).collect()
1682    }
1683    /// Returns `true` if `from` can transitively reach `to`.
1684    pub fn can_reach(&self, from: usize, to: usize) -> bool {
1685        self.reachable_from(from).contains(&to)
1686    }
1687}
1688/// A simple key-value store backed by a sorted Vec for small maps.
1689#[allow(dead_code)]
1690pub struct SmallMap<K: Ord + Clone, V: Clone> {
1691    entries: Vec<(K, V)>,
1692}
1693#[allow(dead_code)]
1694impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
1695    /// Creates a new empty small map.
1696    pub fn new() -> Self {
1697        Self {
1698            entries: Vec::new(),
1699        }
1700    }
1701    /// Inserts or replaces the value for `key`.
1702    pub fn insert(&mut self, key: K, val: V) {
1703        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
1704            Ok(i) => self.entries[i].1 = val,
1705            Err(i) => self.entries.insert(i, (key, val)),
1706        }
1707    }
1708    /// Returns the value for `key`, or `None`.
1709    pub fn get(&self, key: &K) -> Option<&V> {
1710        self.entries
1711            .binary_search_by_key(&key, |(k, _)| k)
1712            .ok()
1713            .map(|i| &self.entries[i].1)
1714    }
1715    /// Returns the number of entries.
1716    pub fn len(&self) -> usize {
1717        self.entries.len()
1718    }
1719    /// Returns `true` if empty.
1720    pub fn is_empty(&self) -> bool {
1721        self.entries.is_empty()
1722    }
1723    /// Returns all keys.
1724    pub fn keys(&self) -> Vec<&K> {
1725        self.entries.iter().map(|(k, _)| k).collect()
1726    }
1727    /// Returns all values.
1728    pub fn values(&self) -> Vec<&V> {
1729        self.entries.iter().map(|(_, v)| v).collect()
1730    }
1731}