Skip to main content

oxilean_kernel/conversion/
types.rs

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