Skip to main content

oxilean_kernel/simp/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use crate::{Expr, Literal, Name};
7use std::collections::HashMap;
8
9/// A generic counter that tracks min/max/sum for statistical summaries.
10#[allow(dead_code)]
11pub struct StatSummary {
12    count: u64,
13    sum: f64,
14    min: f64,
15    max: f64,
16}
17#[allow(dead_code)]
18impl StatSummary {
19    /// Creates an empty summary.
20    pub fn new() -> Self {
21        Self {
22            count: 0,
23            sum: 0.0,
24            min: f64::INFINITY,
25            max: f64::NEG_INFINITY,
26        }
27    }
28    /// Records a sample.
29    pub fn record(&mut self, val: f64) {
30        self.count += 1;
31        self.sum += val;
32        if val < self.min {
33            self.min = val;
34        }
35        if val > self.max {
36            self.max = val;
37        }
38    }
39    /// Returns the mean, or `None` if no samples.
40    pub fn mean(&self) -> Option<f64> {
41        if self.count == 0 {
42            None
43        } else {
44            Some(self.sum / self.count as f64)
45        }
46    }
47    /// Returns the minimum, or `None` if no samples.
48    pub fn min(&self) -> Option<f64> {
49        if self.count == 0 {
50            None
51        } else {
52            Some(self.min)
53        }
54    }
55    /// Returns the maximum, or `None` if no samples.
56    pub fn max(&self) -> Option<f64> {
57        if self.count == 0 {
58            None
59        } else {
60            Some(self.max)
61        }
62    }
63    /// Returns the count of recorded samples.
64    pub fn count(&self) -> u64 {
65        self.count
66    }
67}
68/// A set of rewrite rules.
69#[allow(dead_code)]
70pub struct RewriteRuleSet {
71    rules: Vec<RewriteRule>,
72}
73#[allow(dead_code)]
74impl RewriteRuleSet {
75    /// Creates an empty rule set.
76    pub fn new() -> Self {
77        Self { rules: Vec::new() }
78    }
79    /// Adds a rule.
80    pub fn add(&mut self, rule: RewriteRule) {
81        self.rules.push(rule);
82    }
83    /// Returns the number of rules.
84    pub fn len(&self) -> usize {
85        self.rules.len()
86    }
87    /// Returns `true` if the set is empty.
88    pub fn is_empty(&self) -> bool {
89        self.rules.is_empty()
90    }
91    /// Returns all conditional rules.
92    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
93        self.rules.iter().filter(|r| r.conditional).collect()
94    }
95    /// Returns all unconditional rules.
96    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
97        self.rules.iter().filter(|r| !r.conditional).collect()
98    }
99    /// Looks up a rule by name.
100    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
101        self.rules.iter().find(|r| r.name == name)
102    }
103}
104/// Direction of a simp rewrite.
105#[derive(Clone, Copy, Debug, PartialEq, Eq)]
106pub enum SimpDirection {
107    /// Rewrite left-to-right (standard).
108    Forward,
109    /// Rewrite right-to-left.
110    Backward,
111}
112/// A dependency closure builder (transitive closure via BFS).
113#[allow(dead_code)]
114pub struct TransitiveClosure {
115    adj: Vec<Vec<usize>>,
116    n: usize,
117}
118#[allow(dead_code)]
119impl TransitiveClosure {
120    /// Creates a transitive closure builder for `n` nodes.
121    pub fn new(n: usize) -> Self {
122        Self {
123            adj: vec![Vec::new(); n],
124            n,
125        }
126    }
127    /// Adds a direct edge.
128    pub fn add_edge(&mut self, from: usize, to: usize) {
129        if from < self.n {
130            self.adj[from].push(to);
131        }
132    }
133    /// Computes all nodes reachable from `start` (including `start`).
134    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
135        let mut visited = vec![false; self.n];
136        let mut queue = std::collections::VecDeque::new();
137        queue.push_back(start);
138        while let Some(node) = queue.pop_front() {
139            if node >= self.n || visited[node] {
140                continue;
141            }
142            visited[node] = true;
143            for &next in &self.adj[node] {
144                queue.push_back(next);
145            }
146        }
147        (0..self.n).filter(|&i| visited[i]).collect()
148    }
149    /// Returns `true` if `from` can transitively reach `to`.
150    pub fn can_reach(&self, from: usize, to: usize) -> bool {
151        self.reachable_from(from).contains(&to)
152    }
153}
154/// A pair of `StatSummary` values tracking before/after a transformation.
155#[allow(dead_code)]
156pub struct TransformStat {
157    before: StatSummary,
158    after: StatSummary,
159}
160#[allow(dead_code)]
161impl TransformStat {
162    /// Creates a new transform stat recorder.
163    pub fn new() -> Self {
164        Self {
165            before: StatSummary::new(),
166            after: StatSummary::new(),
167        }
168    }
169    /// Records a before value.
170    pub fn record_before(&mut self, v: f64) {
171        self.before.record(v);
172    }
173    /// Records an after value.
174    pub fn record_after(&mut self, v: f64) {
175        self.after.record(v);
176    }
177    /// Returns the mean reduction ratio (after/before).
178    pub fn mean_ratio(&self) -> Option<f64> {
179        let b = self.before.mean()?;
180        let a = self.after.mean()?;
181        if b.abs() < f64::EPSILON {
182            return None;
183        }
184        Some(a / b)
185    }
186}
187/// A versioned record that stores a history of values.
188#[allow(dead_code)]
189pub struct VersionedRecord<T: Clone> {
190    /// Every earlier value, oldest first: version `n` is `earlier[n]`.
191    earlier: Vec<T>,
192    /// The latest value (version `earlier.len()`), declared last so it drops last.
193    current: T,
194}
195#[allow(dead_code)]
196impl<T: Clone> VersionedRecord<T> {
197    /// Creates a new record with an initial value.
198    pub fn new(initial: T) -> Self {
199        Self {
200            earlier: Vec::new(),
201            current: initial,
202        }
203    }
204    /// Updates the record with a new version.
205    pub fn update(&mut self, val: T) {
206        let previous = std::mem::replace(&mut self.current, val);
207        self.earlier.push(previous);
208    }
209    /// Returns the current (latest) value.
210    pub fn current(&self) -> &T {
211        &self.current
212    }
213    /// Returns the value at version `n` (0-indexed), or `None`.
214    pub fn at_version(&self, n: usize) -> Option<&T> {
215        match self.earlier.get(n) {
216            Some(value) => Some(value),
217            None if n == self.earlier.len() => Some(&self.current),
218            None => None,
219        }
220    }
221    /// Returns the version number of the current value.
222    pub fn version(&self) -> usize {
223        self.earlier.len()
224    }
225    /// Returns `true` if more than one version exists.
226    pub fn has_history(&self) -> bool {
227        !self.earlier.is_empty()
228    }
229}
230/// A simple decision tree node for rule dispatching.
231#[allow(dead_code)]
232#[allow(missing_docs)]
233pub enum DecisionNode {
234    /// A leaf with an action string.
235    Leaf(String),
236    /// An interior node: check `key` equals `val` → `yes_branch`, else `no_branch`.
237    Branch {
238        key: String,
239        val: String,
240        yes_branch: Box<DecisionNode>,
241        no_branch: Box<DecisionNode>,
242    },
243}
244#[allow(dead_code)]
245impl DecisionNode {
246    /// Evaluates the decision tree with the given context.
247    pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
248        match self {
249            DecisionNode::Leaf(action) => action.as_str(),
250            DecisionNode::Branch {
251                key,
252                val,
253                yes_branch,
254                no_branch,
255            } => {
256                let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
257                if actual == val.as_str() {
258                    yes_branch.evaluate(ctx)
259                } else {
260                    no_branch.evaluate(ctx)
261                }
262            }
263        }
264    }
265    /// Returns the depth of the decision tree.
266    pub fn depth(&self) -> usize {
267        match self {
268            DecisionNode::Leaf(_) => 0,
269            DecisionNode::Branch {
270                yes_branch,
271                no_branch,
272                ..
273            } => 1 + yes_branch.depth().max(no_branch.depth()),
274        }
275    }
276}
277/// A simple stack-based calculator for arithmetic expressions.
278#[allow(dead_code)]
279pub struct StackCalc {
280    stack: Vec<i64>,
281}
282#[allow(dead_code)]
283impl StackCalc {
284    /// Creates a new empty calculator.
285    pub fn new() -> Self {
286        Self { stack: Vec::new() }
287    }
288    /// Pushes an integer literal.
289    pub fn push(&mut self, n: i64) {
290        self.stack.push(n);
291    }
292    /// Replaces the top two values, `b` on top of `a`, with `op(a, b)` and
293    /// returns it.  Returns `None`, leaving the stack unchanged, if it holds
294    /// fewer than two values.
295    fn apply_binary(&mut self, op: impl FnOnce(i64, i64) -> i64) -> Option<i64> {
296        let (rest_len, a, b) = match self.stack[..] {
297            [ref rest @ .., a, b] => (rest.len(), a, b),
298            _ => return None,
299        };
300        let value = op(a, b);
301        self.stack.truncate(rest_len);
302        self.stack.push(value);
303        Some(value)
304    }
305    /// Adds the top two values and returns the sum, which replaces them.
306    /// Returns `None`, leaving the stack unchanged, if it holds fewer than
307    /// two values.
308    pub fn add(&mut self) -> Option<i64> {
309        self.apply_binary(|a, b| a + b)
310    }
311    /// Subtracts top from second and returns the difference, which replaces
312    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
313    /// than two values.
314    pub fn sub(&mut self) -> Option<i64> {
315        self.apply_binary(|a, b| a - b)
316    }
317    /// Multiplies the top two values and returns the product, which replaces
318    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
319    /// than two values.
320    pub fn mul(&mut self) -> Option<i64> {
321        self.apply_binary(|a, b| a * b)
322    }
323    /// Peeks the top value.
324    pub fn peek(&self) -> Option<i64> {
325        self.stack.last().copied()
326    }
327    /// Returns the stack depth.
328    pub fn depth(&self) -> usize {
329        self.stack.len()
330    }
331}
332/// A trie-based prefix counter.
333#[allow(dead_code)]
334pub struct PrefixCounter {
335    children: std::collections::HashMap<char, PrefixCounter>,
336    count: usize,
337}
338#[allow(dead_code)]
339impl PrefixCounter {
340    /// Creates an empty prefix counter.
341    pub fn new() -> Self {
342        Self {
343            children: std::collections::HashMap::new(),
344            count: 0,
345        }
346    }
347    /// Records a string.
348    pub fn record(&mut self, s: &str) {
349        self.count += 1;
350        let mut node = self;
351        for c in s.chars() {
352            node = node.children.entry(c).or_default();
353            node.count += 1;
354        }
355    }
356    /// Returns how many strings have been recorded that start with `prefix`.
357    pub fn count_with_prefix(&self, prefix: &str) -> usize {
358        let mut node = self;
359        for c in prefix.chars() {
360            match node.children.get(&c) {
361                Some(n) => node = n,
362                None => return 0,
363            }
364        }
365        node.count
366    }
367}
368/// A simple key-value store backed by a sorted Vec for small maps.
369#[allow(dead_code)]
370pub struct SmallMap<K: Ord + Clone, V: Clone> {
371    entries: Vec<(K, V)>,
372}
373#[allow(dead_code)]
374impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
375    /// Creates a new empty small map.
376    pub fn new() -> Self {
377        Self {
378            entries: Vec::new(),
379        }
380    }
381    /// Inserts or replaces the value for `key`.
382    pub fn insert(&mut self, key: K, val: V) {
383        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
384            Ok(i) => self.entries[i].1 = val,
385            Err(i) => self.entries.insert(i, (key, val)),
386        }
387    }
388    /// Returns the value for `key`, or `None`.
389    pub fn get(&self, key: &K) -> Option<&V> {
390        self.entries
391            .binary_search_by_key(&key, |(k, _)| k)
392            .ok()
393            .map(|i| &self.entries[i].1)
394    }
395    /// Returns the number of entries.
396    pub fn len(&self) -> usize {
397        self.entries.len()
398    }
399    /// Returns `true` if empty.
400    pub fn is_empty(&self) -> bool {
401        self.entries.is_empty()
402    }
403    /// Returns all keys.
404    pub fn keys(&self) -> Vec<&K> {
405        self.entries.iter().map(|(k, _)| k).collect()
406    }
407    /// Returns all values.
408    pub fn values(&self) -> Vec<&V> {
409        self.entries.iter().map(|(_, v)| v).collect()
410    }
411}
412/// A write-once cell.
413#[allow(dead_code)]
414pub struct WriteOnce<T> {
415    value: std::cell::Cell<Option<T>>,
416}
417#[allow(dead_code)]
418impl<T: Copy> WriteOnce<T> {
419    /// Creates an empty write-once cell.
420    pub fn new() -> Self {
421        Self {
422            value: std::cell::Cell::new(None),
423        }
424    }
425    /// Writes a value.  Returns `false` if already written.
426    pub fn write(&self, val: T) -> bool {
427        if self.value.get().is_some() {
428            return false;
429        }
430        self.value.set(Some(val));
431        true
432    }
433    /// Returns the value if written.
434    pub fn read(&self) -> Option<T> {
435        self.value.get()
436    }
437    /// Returns `true` if the value has been written.
438    pub fn is_written(&self) -> bool {
439        self.value.get().is_some()
440    }
441}
442/// A type-erased function pointer with arity tracking.
443#[allow(dead_code)]
444pub struct RawFnPtr {
445    /// The raw function pointer (stored as usize for type erasure).
446    ptr: usize,
447    arity: usize,
448    name: String,
449}
450#[allow(dead_code)]
451impl RawFnPtr {
452    /// Creates a new raw function pointer descriptor.
453    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
454        Self {
455            ptr,
456            arity,
457            name: name.into(),
458        }
459    }
460    /// Returns the arity.
461    pub fn arity(&self) -> usize {
462        self.arity
463    }
464    /// Returns the name.
465    pub fn name(&self) -> &str {
466        &self.name
467    }
468    /// Returns the raw pointer value.
469    pub fn raw(&self) -> usize {
470        self.ptr
471    }
472}
473/// A simplified expression with a trace of applied lemma names.
474#[derive(Debug, Clone)]
475pub struct SimpResult {
476    /// The simplified expression.
477    pub expr: Expr,
478    /// Names of lemmas that were applied (in order).
479    pub applied: Vec<Name>,
480    /// Whether any change was made.
481    pub changed: bool,
482}
483impl SimpResult {
484    /// Simplification had no effect.
485    pub fn unchanged(expr: Expr) -> Self {
486        Self {
487            expr,
488            applied: Vec::new(),
489            changed: false,
490        }
491    }
492    /// Simplification succeeded.
493    pub fn simplified(expr: Expr, applied: Vec<Name>) -> Self {
494        let changed = !applied.is_empty();
495        Self {
496            expr,
497            applied,
498            changed,
499        }
500    }
501}
502/// A non-empty list (at least one element guaranteed).
503#[allow(dead_code)]
504pub struct NonEmptyVec<T> {
505    head: T,
506    tail: Vec<T>,
507}
508#[allow(dead_code)]
509impl<T> NonEmptyVec<T> {
510    /// Creates a non-empty vec with a single element.
511    pub fn singleton(val: T) -> Self {
512        Self {
513            head: val,
514            tail: Vec::new(),
515        }
516    }
517    /// Pushes an element.
518    pub fn push(&mut self, val: T) {
519        self.tail.push(val);
520    }
521    /// Returns a reference to the first element.
522    pub fn first(&self) -> &T {
523        &self.head
524    }
525    /// Returns a reference to the last element.
526    pub fn last(&self) -> &T {
527        self.tail.last().unwrap_or(&self.head)
528    }
529    /// Returns the number of elements.
530    pub fn len(&self) -> usize {
531        1 + self.tail.len()
532    }
533    /// Returns whether the collection is empty.
534    pub fn is_empty(&self) -> bool {
535        self.len() == 0
536    }
537    /// Returns all elements as a Vec.
538    pub fn to_vec(&self) -> Vec<&T> {
539        let mut v = vec![&self.head];
540        v.extend(self.tail.iter());
541        v
542    }
543}
544/// A simple mutable key-value store for test fixtures.
545#[allow(dead_code)]
546pub struct Fixture {
547    data: std::collections::HashMap<String, String>,
548}
549#[allow(dead_code)]
550impl Fixture {
551    /// Creates an empty fixture.
552    pub fn new() -> Self {
553        Self {
554            data: std::collections::HashMap::new(),
555        }
556    }
557    /// Sets a key.
558    pub fn set(&mut self, key: impl Into<String>, val: impl Into<String>) {
559        self.data.insert(key.into(), val.into());
560    }
561    /// Gets a value.
562    pub fn get(&self, key: &str) -> Option<&str> {
563        self.data.get(key).map(|s| s.as_str())
564    }
565    /// Returns the number of entries.
566    pub fn len(&self) -> usize {
567        self.data.len()
568    }
569    /// Returns whether the collection is empty.
570    pub fn is_empty(&self) -> bool {
571        self.len() == 0
572    }
573}
574/// A reusable scratch buffer for path computations.
575#[allow(dead_code)]
576pub struct PathBuf {
577    components: Vec<String>,
578}
579#[allow(dead_code)]
580impl PathBuf {
581    /// Creates a new empty path buffer.
582    pub fn new() -> Self {
583        Self {
584            components: Vec::new(),
585        }
586    }
587    /// Pushes a component.
588    pub fn push(&mut self, comp: impl Into<String>) {
589        self.components.push(comp.into());
590    }
591    /// Pops the last component.
592    pub fn pop(&mut self) {
593        self.components.pop();
594    }
595    /// Returns the current path as a `/`-separated string.
596    pub fn as_str(&self) -> String {
597        self.components.join("/")
598    }
599    /// Returns the depth of the path.
600    pub fn depth(&self) -> usize {
601        self.components.len()
602    }
603    /// Clears the path.
604    pub fn clear(&mut self) {
605        self.components.clear();
606    }
607}
608/// A token bucket rate limiter.
609#[allow(dead_code)]
610pub struct TokenBucket {
611    capacity: u64,
612    tokens: u64,
613    refill_per_ms: u64,
614    last_refill: crate::wall_clock::Instant,
615}
616#[allow(dead_code)]
617impl TokenBucket {
618    /// Creates a new token bucket.
619    pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
620        Self {
621            capacity,
622            tokens: capacity,
623            refill_per_ms,
624            last_refill: crate::wall_clock::Instant::now(),
625        }
626    }
627    /// Attempts to consume `n` tokens.  Returns `true` on success.
628    pub fn try_consume(&mut self, n: u64) -> bool {
629        self.refill();
630        if self.tokens >= n {
631            self.tokens -= n;
632            true
633        } else {
634            false
635        }
636    }
637    fn refill(&mut self) {
638        let now = crate::wall_clock::Instant::now();
639        let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
640        if elapsed_ms > 0 {
641            let new_tokens = elapsed_ms * self.refill_per_ms;
642            self.tokens = (self.tokens + new_tokens).min(self.capacity);
643            self.last_refill = now;
644        }
645    }
646    /// Returns the number of currently available tokens.
647    pub fn available(&self) -> u64 {
648        self.tokens
649    }
650    /// Returns the bucket capacity.
651    pub fn capacity(&self) -> u64 {
652        self.capacity
653    }
654}
655/// A collection of simp lemmas that can be applied in sequence.
656pub struct SimpLemmaSet {
657    lemmas: Vec<SimpLemma>,
658}
659impl SimpLemmaSet {
660    /// Create an empty lemma set.
661    pub fn new() -> Self {
662        Self { lemmas: Vec::new() }
663    }
664    /// Add a lemma.
665    pub fn add(&mut self, lemma: SimpLemma) {
666        self.lemmas.push(lemma);
667    }
668    /// Number of lemmas.
669    pub fn len(&self) -> usize {
670        self.lemmas.len()
671    }
672    /// Check if empty.
673    pub fn is_empty(&self) -> bool {
674        self.lemmas.is_empty()
675    }
676    /// Apply all lemmas in order to an expression (one pass).
677    pub fn apply_once(&self, expr: &Expr) -> (Expr, bool) {
678        let mut current = expr.clone();
679        let mut changed = false;
680        for lemma in &self.lemmas {
681            let (new_expr, c) =
682                rewrite_once(&current, lemma.effective_lhs(), lemma.effective_rhs());
683            if c {
684                current = new_expr;
685                changed = true;
686            }
687        }
688        (current, changed)
689    }
690    /// Apply all lemmas exhaustively (until no changes).
691    pub fn apply_all(&self, expr: &Expr) -> Expr {
692        let mut current = expr.clone();
693        loop {
694            let (new_expr, changed) = self.apply_once(&current);
695            if !changed {
696                return current;
697            }
698            current = new_expr;
699        }
700    }
701    /// Iter over lemmas.
702    pub fn iter(&self) -> impl Iterator<Item = &SimpLemma> {
703        self.lemmas.iter()
704    }
705    /// Look up a lemma by name.
706    pub fn find(&self, name: &Name) -> Option<&SimpLemma> {
707        self.lemmas.iter().find(|l| &l.name == name)
708    }
709}
710/// A window iterator that yields overlapping windows of size `n`.
711#[allow(dead_code)]
712pub struct WindowIterator<'a, T> {
713    pub(super) data: &'a [T],
714    pub(super) pos: usize,
715    pub(super) window: usize,
716}
717#[allow(dead_code)]
718impl<'a, T> WindowIterator<'a, T> {
719    /// Creates a new window iterator.
720    pub fn new(data: &'a [T], window: usize) -> Self {
721        Self {
722            data,
723            pos: 0,
724            window,
725        }
726    }
727}
728/// A counter that can measure elapsed time between snapshots.
729#[allow(dead_code)]
730pub struct Stopwatch {
731    start: crate::wall_clock::Instant,
732    splits: Vec<f64>,
733}
734#[allow(dead_code)]
735impl Stopwatch {
736    /// Creates and starts a new stopwatch.
737    pub fn start() -> Self {
738        Self {
739            start: crate::wall_clock::Instant::now(),
740            splits: Vec::new(),
741        }
742    }
743    /// Records a split time (elapsed since start).
744    pub fn split(&mut self) {
745        self.splits.push(self.elapsed_ms());
746    }
747    /// Returns total elapsed milliseconds since start.
748    pub fn elapsed_ms(&self) -> f64 {
749        self.start.elapsed().as_secs_f64() * 1000.0
750    }
751    /// Returns all recorded split times.
752    pub fn splits(&self) -> &[f64] {
753        &self.splits
754    }
755    /// Returns the number of splits.
756    pub fn num_splits(&self) -> usize {
757        self.splits.len()
758    }
759}
760/// A simple directed acyclic graph.
761#[allow(dead_code)]
762pub struct SimpleDag {
763    /// `edges[i]` is the list of direct successors of node `i`.
764    edges: Vec<Vec<usize>>,
765}
766#[allow(dead_code)]
767impl SimpleDag {
768    /// Creates a DAG with `n` nodes and no edges.
769    pub fn new(n: usize) -> Self {
770        Self {
771            edges: vec![Vec::new(); n],
772        }
773    }
774    /// Adds an edge from `from` to `to`.
775    pub fn add_edge(&mut self, from: usize, to: usize) {
776        if from < self.edges.len() {
777            self.edges[from].push(to);
778        }
779    }
780    /// Returns the successors of `node`.
781    pub fn successors(&self, node: usize) -> &[usize] {
782        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
783    }
784    /// Returns `true` if `from` can reach `to` via DFS.
785    pub fn can_reach(&self, from: usize, to: usize) -> bool {
786        let mut visited = vec![false; self.edges.len()];
787        self.dfs(from, to, &mut visited)
788    }
789    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
790        if cur == target {
791            return true;
792        }
793        if cur >= visited.len() || visited[cur] {
794            return false;
795        }
796        visited[cur] = true;
797        for &next in self.successors(cur) {
798            if self.dfs(next, target, visited) {
799                return true;
800            }
801        }
802        false
803    }
804    /// Returns the topological order of nodes, or `None` if a cycle is detected.
805    pub fn topological_sort(&self) -> Option<Vec<usize>> {
806        let n = self.edges.len();
807        let mut in_degree = vec![0usize; n];
808        for succs in &self.edges {
809            for &s in succs {
810                if s < n {
811                    in_degree[s] += 1;
812                }
813            }
814        }
815        let mut queue: std::collections::VecDeque<usize> =
816            (0..n).filter(|&i| in_degree[i] == 0).collect();
817        let mut order = Vec::new();
818        while let Some(node) = queue.pop_front() {
819            order.push(node);
820            for &s in self.successors(node) {
821                if s < n {
822                    in_degree[s] -= 1;
823                    if in_degree[s] == 0 {
824                        queue.push_back(s);
825                    }
826                }
827            }
828        }
829        if order.len() == n {
830            Some(order)
831        } else {
832            None
833        }
834    }
835    /// Returns the number of nodes.
836    pub fn num_nodes(&self) -> usize {
837        self.edges.len()
838    }
839}
840/// A pool of reusable string buffers.
841#[allow(dead_code)]
842pub struct StringPool {
843    free: Vec<String>,
844}
845#[allow(dead_code)]
846impl StringPool {
847    /// Creates a new empty string pool.
848    pub fn new() -> Self {
849        Self { free: Vec::new() }
850    }
851    /// Takes a string from the pool (may be empty).
852    pub fn take(&mut self) -> String {
853        self.free.pop().unwrap_or_default()
854    }
855    /// Returns a string to the pool.
856    pub fn give(&mut self, mut s: String) {
857        s.clear();
858        self.free.push(s);
859    }
860    /// Returns the number of free strings in the pool.
861    pub fn free_count(&self) -> usize {
862        self.free.len()
863    }
864}
865/// A label set for a graph node.
866#[allow(dead_code)]
867pub struct LabelSet {
868    labels: Vec<String>,
869}
870#[allow(dead_code)]
871impl LabelSet {
872    /// Creates a new empty label set.
873    pub fn new() -> Self {
874        Self { labels: Vec::new() }
875    }
876    /// Adds a label (deduplicates).
877    pub fn add(&mut self, label: impl Into<String>) {
878        let s = label.into();
879        if !self.labels.contains(&s) {
880            self.labels.push(s);
881        }
882    }
883    /// Returns `true` if `label` is present.
884    pub fn has(&self, label: &str) -> bool {
885        self.labels.iter().any(|l| l == label)
886    }
887    /// Returns the count of labels.
888    pub fn count(&self) -> usize {
889        self.labels.len()
890    }
891    /// Returns all labels.
892    pub fn all(&self) -> &[String] {
893        &self.labels
894    }
895}
896/// A mutable reference stack for tracking the current "focus" in a tree traversal.
897#[allow(dead_code)]
898pub struct FocusStack<T> {
899    items: Vec<T>,
900}
901#[allow(dead_code)]
902impl<T> FocusStack<T> {
903    /// Creates an empty focus stack.
904    pub fn new() -> Self {
905        Self { items: Vec::new() }
906    }
907    /// Focuses on `item`.
908    pub fn focus(&mut self, item: T) {
909        self.items.push(item);
910    }
911    /// Blurs (pops) the current focus.
912    pub fn blur(&mut self) -> Option<T> {
913        self.items.pop()
914    }
915    /// Returns the current focus, or `None`.
916    pub fn current(&self) -> Option<&T> {
917        self.items.last()
918    }
919    /// Returns the focus depth.
920    pub fn depth(&self) -> usize {
921        self.items.len()
922    }
923    /// Returns `true` if there is no current focus.
924    pub fn is_empty(&self) -> bool {
925        self.items.is_empty()
926    }
927}
928/// A flat list of substitution pairs `(from, to)`.
929#[allow(dead_code)]
930pub struct FlatSubstitution {
931    pairs: Vec<(String, String)>,
932}
933#[allow(dead_code)]
934impl FlatSubstitution {
935    /// Creates an empty substitution.
936    pub fn new() -> Self {
937        Self { pairs: Vec::new() }
938    }
939    /// Adds a pair.
940    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
941        self.pairs.push((from.into(), to.into()));
942    }
943    /// Applies all substitutions to `s` (leftmost-first order).
944    pub fn apply(&self, s: &str) -> String {
945        let mut result = s.to_string();
946        for (from, to) in &self.pairs {
947            result = result.replace(from.as_str(), to.as_str());
948        }
949        result
950    }
951    /// Returns the number of pairs.
952    pub fn len(&self) -> usize {
953        self.pairs.len()
954    }
955    /// Returns `true` if empty.
956    pub fn is_empty(&self) -> bool {
957        self.pairs.is_empty()
958    }
959}
960/// A hierarchical configuration tree.
961#[allow(dead_code)]
962pub struct ConfigNode {
963    key: String,
964    value: Option<String>,
965    children: Vec<ConfigNode>,
966}
967#[allow(dead_code)]
968impl ConfigNode {
969    /// Creates a leaf config node with a value.
970    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
971        Self {
972            key: key.into(),
973            value: Some(value.into()),
974            children: Vec::new(),
975        }
976    }
977    /// Creates a section node with children.
978    pub fn section(key: impl Into<String>) -> Self {
979        Self {
980            key: key.into(),
981            value: None,
982            children: Vec::new(),
983        }
984    }
985    /// Adds a child node.
986    pub fn add_child(&mut self, child: ConfigNode) {
987        self.children.push(child);
988    }
989    /// Returns the key.
990    pub fn key(&self) -> &str {
991        &self.key
992    }
993    /// Returns the value, or `None` for section nodes.
994    pub fn value(&self) -> Option<&str> {
995        self.value.as_deref()
996    }
997    /// Returns the number of children.
998    pub fn num_children(&self) -> usize {
999        self.children.len()
1000    }
1001    /// Looks up a dot-separated path.
1002    pub fn lookup(&self, path: &str) -> Option<&str> {
1003        let mut parts = path.splitn(2, '.');
1004        let head = parts.next()?;
1005        let tail = parts.next();
1006        if head != self.key {
1007            return None;
1008        }
1009        match tail {
1010            None => self.value.as_deref(),
1011            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1012        }
1013    }
1014    fn lookup_relative(&self, path: &str) -> Option<&str> {
1015        let mut parts = path.splitn(2, '.');
1016        let head = parts.next()?;
1017        let tail = parts.next();
1018        if head != self.key {
1019            return None;
1020        }
1021        match tail {
1022            None => self.value.as_deref(),
1023            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1024        }
1025    }
1026}
1027/// A single simp lemma: a conditional rewrite rule `lhs = rhs`.
1028#[derive(Clone, Debug)]
1029pub struct SimpLemma {
1030    /// Name of the lemma.
1031    pub name: Name,
1032    /// Left-hand side pattern.
1033    pub lhs: Expr,
1034    /// Right-hand side replacement.
1035    pub rhs: Expr,
1036    /// Rewrite direction.
1037    pub direction: SimpDirection,
1038    /// Priority (higher = preferred).
1039    pub priority: i32,
1040}
1041impl SimpLemma {
1042    /// Create a forward simp lemma.
1043    pub fn forward(name: Name, lhs: Expr, rhs: Expr) -> Self {
1044        Self {
1045            name,
1046            lhs,
1047            rhs,
1048            direction: SimpDirection::Forward,
1049            priority: 1000,
1050        }
1051    }
1052    /// Create a backward simp lemma.
1053    pub fn backward(name: Name, lhs: Expr, rhs: Expr) -> Self {
1054        Self {
1055            name,
1056            lhs,
1057            rhs,
1058            direction: SimpDirection::Backward,
1059            priority: 1000,
1060        }
1061    }
1062    /// Swap the simp lemma direction.
1063    pub fn reversed(mut self) -> Self {
1064        self.direction = match self.direction {
1065            SimpDirection::Forward => SimpDirection::Backward,
1066            SimpDirection::Backward => SimpDirection::Forward,
1067        };
1068        std::mem::swap(&mut self.lhs, &mut self.rhs);
1069        self
1070    }
1071    /// Return the effective LHS (after direction is applied).
1072    pub fn effective_lhs(&self) -> &Expr {
1073        &self.lhs
1074    }
1075    /// Return the effective RHS (after direction is applied).
1076    pub fn effective_rhs(&self) -> &Expr {
1077        &self.rhs
1078    }
1079}
1080/// Represents a rewrite rule `lhs → rhs`.
1081#[allow(dead_code)]
1082#[allow(missing_docs)]
1083pub struct RewriteRule {
1084    /// The name of the rule.
1085    pub name: String,
1086    /// A string representation of the LHS pattern.
1087    pub lhs: String,
1088    /// A string representation of the RHS.
1089    pub rhs: String,
1090    /// Whether this is a conditional rule (has side conditions).
1091    pub conditional: bool,
1092}
1093#[allow(dead_code)]
1094impl RewriteRule {
1095    /// Creates an unconditional rewrite rule.
1096    pub fn unconditional(
1097        name: impl Into<String>,
1098        lhs: impl Into<String>,
1099        rhs: impl Into<String>,
1100    ) -> Self {
1101        Self {
1102            name: name.into(),
1103            lhs: lhs.into(),
1104            rhs: rhs.into(),
1105            conditional: false,
1106        }
1107    }
1108    /// Creates a conditional rewrite rule.
1109    pub fn conditional(
1110        name: impl Into<String>,
1111        lhs: impl Into<String>,
1112        rhs: impl Into<String>,
1113    ) -> Self {
1114        Self {
1115            name: name.into(),
1116            lhs: lhs.into(),
1117            rhs: rhs.into(),
1118            conditional: true,
1119        }
1120    }
1121    /// Returns a textual representation.
1122    pub fn display(&self) -> String {
1123        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
1124    }
1125}
1126/// A fixed-size sliding window that computes a running sum.
1127#[allow(dead_code)]
1128pub struct SlidingSum {
1129    window: Vec<f64>,
1130    capacity: usize,
1131    pos: usize,
1132    sum: f64,
1133    count: usize,
1134}
1135#[allow(dead_code)]
1136impl SlidingSum {
1137    /// Creates a sliding sum with the given window size.
1138    pub fn new(capacity: usize) -> Self {
1139        Self {
1140            window: vec![0.0; capacity],
1141            capacity,
1142            pos: 0,
1143            sum: 0.0,
1144            count: 0,
1145        }
1146    }
1147    /// Adds a value to the window, removing the oldest if full.
1148    pub fn push(&mut self, val: f64) {
1149        let oldest = self.window[self.pos];
1150        self.sum -= oldest;
1151        self.sum += val;
1152        self.window[self.pos] = val;
1153        self.pos = (self.pos + 1) % self.capacity;
1154        if self.count < self.capacity {
1155            self.count += 1;
1156        }
1157    }
1158    /// Returns the current window sum.
1159    pub fn sum(&self) -> f64 {
1160        self.sum
1161    }
1162    /// Returns the window mean, or `None` if empty.
1163    pub fn mean(&self) -> Option<f64> {
1164        if self.count == 0 {
1165            None
1166        } else {
1167            Some(self.sum / self.count as f64)
1168        }
1169    }
1170    /// Returns the current window size (number of valid elements).
1171    pub fn count(&self) -> usize {
1172        self.count
1173    }
1174}
1175/// A min-heap implemented as a binary heap.
1176#[allow(dead_code)]
1177pub struct MinHeap<T: Ord> {
1178    data: Vec<T>,
1179}
1180#[allow(dead_code)]
1181impl<T: Ord> MinHeap<T> {
1182    /// Creates a new empty min-heap.
1183    pub fn new() -> Self {
1184        Self { data: Vec::new() }
1185    }
1186    /// Inserts an element.
1187    pub fn push(&mut self, val: T) {
1188        self.data.push(val);
1189        self.sift_up(self.data.len() - 1);
1190    }
1191    /// Removes and returns the minimum element.
1192    pub fn pop(&mut self) -> Option<T> {
1193        if self.data.is_empty() {
1194            return None;
1195        }
1196        let n = self.data.len();
1197        self.data.swap(0, n - 1);
1198        let min = self.data.pop();
1199        if !self.data.is_empty() {
1200            self.sift_down(0);
1201        }
1202        min
1203    }
1204    /// Returns a reference to the minimum element.
1205    pub fn peek(&self) -> Option<&T> {
1206        self.data.first()
1207    }
1208    /// Returns the number of elements.
1209    pub fn len(&self) -> usize {
1210        self.data.len()
1211    }
1212    /// Returns `true` if empty.
1213    pub fn is_empty(&self) -> bool {
1214        self.data.is_empty()
1215    }
1216    fn sift_up(&mut self, mut i: usize) {
1217        while i > 0 {
1218            let parent = (i - 1) / 2;
1219            if self.data[i] < self.data[parent] {
1220                self.data.swap(i, parent);
1221                i = parent;
1222            } else {
1223                break;
1224            }
1225        }
1226    }
1227    fn sift_down(&mut self, mut i: usize) {
1228        let n = self.data.len();
1229        loop {
1230            let left = 2 * i + 1;
1231            let right = 2 * i + 2;
1232            let mut smallest = i;
1233            if left < n && self.data[left] < self.data[smallest] {
1234                smallest = left;
1235            }
1236            if right < n && self.data[right] < self.data[smallest] {
1237                smallest = right;
1238            }
1239            if smallest == i {
1240                break;
1241            }
1242            self.data.swap(i, smallest);
1243            i = smallest;
1244        }
1245    }
1246}
1247/// A tagged union for representing a simple two-case discriminated union.
1248#[allow(dead_code)]
1249pub enum Either2<A, B> {
1250    /// The first alternative.
1251    First(A),
1252    /// The second alternative.
1253    Second(B),
1254}
1255#[allow(dead_code)]
1256impl<A, B> Either2<A, B> {
1257    /// Returns `true` if this is the first alternative.
1258    pub fn is_first(&self) -> bool {
1259        matches!(self, Either2::First(_))
1260    }
1261    /// Returns `true` if this is the second alternative.
1262    pub fn is_second(&self) -> bool {
1263        matches!(self, Either2::Second(_))
1264    }
1265    /// Returns the first value if present.
1266    pub fn first(self) -> Option<A> {
1267        match self {
1268            Either2::First(a) => Some(a),
1269            _ => None,
1270        }
1271    }
1272    /// Returns the second value if present.
1273    pub fn second(self) -> Option<B> {
1274        match self {
1275            Either2::Second(b) => Some(b),
1276            _ => None,
1277        }
1278    }
1279    /// Maps over the first alternative.
1280    pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
1281        match self {
1282            Either2::First(a) => Either2::First(f(a)),
1283            Either2::Second(b) => Either2::Second(b),
1284        }
1285    }
1286}
1287/// A sparse vector: stores only non-default elements.
1288#[allow(dead_code)]
1289pub struct SparseVec<T: Default + Clone + PartialEq> {
1290    entries: std::collections::HashMap<usize, T>,
1291    default_: T,
1292    logical_len: usize,
1293}
1294#[allow(dead_code)]
1295impl<T: Default + Clone + PartialEq> SparseVec<T> {
1296    /// Creates a new sparse vector with logical length `len`.
1297    pub fn new(len: usize) -> Self {
1298        Self {
1299            entries: std::collections::HashMap::new(),
1300            default_: T::default(),
1301            logical_len: len,
1302        }
1303    }
1304    /// Sets element at `idx`.
1305    pub fn set(&mut self, idx: usize, val: T) {
1306        if val == self.default_ {
1307            self.entries.remove(&idx);
1308        } else {
1309            self.entries.insert(idx, val);
1310        }
1311    }
1312    /// Gets element at `idx`.
1313    pub fn get(&self, idx: usize) -> &T {
1314        self.entries.get(&idx).unwrap_or(&self.default_)
1315    }
1316    /// Returns the logical length.
1317    pub fn len(&self) -> usize {
1318        self.logical_len
1319    }
1320    /// Returns whether the collection is empty.
1321    pub fn is_empty(&self) -> bool {
1322        self.len() == 0
1323    }
1324    /// Returns the number of non-default elements.
1325    pub fn nnz(&self) -> usize {
1326        self.entries.len()
1327    }
1328}