Skip to main content

oxilean_kernel/match_compile/
types.rs

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