Skip to main content

oxilean_kernel/quotient/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use crate::{Expr, KernelError, Name};
7use std::collections::HashMap;
8
9/// A versioned record that stores a history of values.
10#[allow(dead_code)]
11pub struct VersionedRecord<T: Clone> {
12    history: Vec<T>,
13}
14#[allow(dead_code)]
15impl<T: Clone> VersionedRecord<T> {
16    /// Creates a new record with an initial value.
17    pub fn new(initial: T) -> Self {
18        Self {
19            history: vec![initial],
20        }
21    }
22    /// Updates the record with a new version.
23    pub fn update(&mut self, val: T) {
24        self.history.push(val);
25    }
26    /// Returns the current (latest) value.
27    pub fn current(&self) -> &T {
28        self.history
29            .last()
30            .expect("VersionedRecord history is always non-empty after construction")
31    }
32    /// Returns the value at version `n` (0-indexed), or `None`.
33    pub fn at_version(&self, n: usize) -> Option<&T> {
34        self.history.get(n)
35    }
36    /// Returns the version number of the current value.
37    pub fn version(&self) -> usize {
38        self.history.len() - 1
39    }
40    /// Returns `true` if more than one version exists.
41    pub fn has_history(&self) -> bool {
42        self.history.len() > 1
43    }
44}
45/// A reusable scratch buffer for path computations.
46#[allow(dead_code)]
47pub struct PathBuf {
48    components: Vec<String>,
49}
50#[allow(dead_code)]
51impl PathBuf {
52    /// Creates a new empty path buffer.
53    pub fn new() -> Self {
54        Self {
55            components: Vec::new(),
56        }
57    }
58    /// Pushes a component.
59    pub fn push(&mut self, comp: impl Into<String>) {
60        self.components.push(comp.into());
61    }
62    /// Pops the last component.
63    pub fn pop(&mut self) {
64        self.components.pop();
65    }
66    /// Returns the current path as a `/`-separated string.
67    pub fn as_str(&self) -> String {
68        self.components.join("/")
69    }
70    /// Returns the depth of the path.
71    pub fn depth(&self) -> usize {
72        self.components.len()
73    }
74    /// Clears the path.
75    pub fn clear(&mut self) {
76        self.components.clear();
77    }
78}
79/// A generic counter that tracks min/max/sum for statistical summaries.
80#[allow(dead_code)]
81pub struct StatSummary {
82    count: u64,
83    sum: f64,
84    min: f64,
85    max: f64,
86}
87#[allow(dead_code)]
88impl StatSummary {
89    /// Creates an empty summary.
90    pub fn new() -> Self {
91        Self {
92            count: 0,
93            sum: 0.0,
94            min: f64::INFINITY,
95            max: f64::NEG_INFINITY,
96        }
97    }
98    /// Records a sample.
99    pub fn record(&mut self, val: f64) {
100        self.count += 1;
101        self.sum += val;
102        if val < self.min {
103            self.min = val;
104        }
105        if val > self.max {
106            self.max = val;
107        }
108    }
109    /// Returns the mean, or `None` if no samples.
110    pub fn mean(&self) -> Option<f64> {
111        if self.count == 0 {
112            None
113        } else {
114            Some(self.sum / self.count as f64)
115        }
116    }
117    /// Returns the minimum, or `None` if no samples.
118    pub fn min(&self) -> Option<f64> {
119        if self.count == 0 {
120            None
121        } else {
122            Some(self.min)
123        }
124    }
125    /// Returns the maximum, or `None` if no samples.
126    pub fn max(&self) -> Option<f64> {
127        if self.count == 0 {
128            None
129        } else {
130            Some(self.max)
131        }
132    }
133    /// Returns the count of recorded samples.
134    pub fn count(&self) -> u64 {
135        self.count
136    }
137}
138/// A non-empty list (at least one element guaranteed).
139#[allow(dead_code)]
140pub struct NonEmptyVec<T> {
141    head: T,
142    tail: Vec<T>,
143}
144#[allow(dead_code)]
145impl<T> NonEmptyVec<T> {
146    /// Creates a non-empty vec with a single element.
147    pub fn singleton(val: T) -> Self {
148        Self {
149            head: val,
150            tail: Vec::new(),
151        }
152    }
153    /// Pushes an element.
154    pub fn push(&mut self, val: T) {
155        self.tail.push(val);
156    }
157    /// Returns a reference to the first element.
158    pub fn first(&self) -> &T {
159        &self.head
160    }
161    /// Returns a reference to the last element.
162    pub fn last(&self) -> &T {
163        self.tail.last().unwrap_or(&self.head)
164    }
165    /// Returns the number of elements.
166    pub fn len(&self) -> usize {
167        1 + self.tail.len()
168    }
169    /// Returns whether the collection is empty.
170    pub fn is_empty(&self) -> bool {
171        self.len() == 0
172    }
173    /// Returns all elements as a Vec.
174    pub fn to_vec(&self) -> Vec<&T> {
175        let mut v = vec![&self.head];
176        v.extend(self.tail.iter());
177        v
178    }
179}
180/// Kernel-level record for quotient operations.
181#[derive(Debug, Clone)]
182pub struct QuotientKernel {
183    quot_types: Vec<QuotientType>,
184}
185impl QuotientKernel {
186    /// Create a new empty quotient kernel.
187    pub fn new() -> Self {
188        Self {
189            quot_types: Vec::new(),
190        }
191    }
192    /// Register a quotient type.
193    pub fn register(&mut self, qt: QuotientType) {
194        self.quot_types.push(qt);
195    }
196    /// Find by base type.
197    pub fn find_by_base(&self, base: &Expr) -> Option<&QuotientType> {
198        self.quot_types.iter().find(|qt| &qt.base_type == base)
199    }
200    /// Check if an expression is a quotient type.
201    ///
202    /// Returns `true` if `expr` is either:
203    /// 1. Registered in this kernel (exact match against `qt.quot_type`), or
204    /// 2. Structurally a `Quot α r` application (`App(App(Quot, _), _)`).
205    pub fn is_quot_type(&self, expr: &Expr) -> bool {
206        if is_quot_type_expr(expr) {
207            return true;
208        }
209        self.quot_types.iter().any(|qt| &qt.quot_type == expr)
210    }
211    /// Attempt to reduce a quotient expression.
212    pub fn reduce(&self, head: &Expr, args: &[Expr]) -> Option<Expr> {
213        try_reduce_quot(head, args)
214    }
215    /// Count registered quotient types.
216    pub fn count(&self) -> usize {
217        self.quot_types.len()
218    }
219}
220/// A normalizer that reduces quotient expressions to normal form.
221///
222/// Repeatedly applies quotient reduction rules until no more apply.
223#[allow(dead_code)]
224pub struct QuotientNormalizer {
225    /// Maximum number of normalization steps.
226    max_steps: usize,
227    /// Accumulated reduction steps.
228    steps_taken: usize,
229}
230impl QuotientNormalizer {
231    /// Create a new normalizer.
232    #[allow(dead_code)]
233    pub fn new(max_steps: usize) -> Self {
234        Self {
235            max_steps,
236            steps_taken: 0,
237        }
238    }
239    /// Normalize an expression, returning the result and whether any change occurred.
240    #[allow(dead_code)]
241    pub fn normalize(&mut self, expr: Expr) -> (Expr, bool) {
242        let mut current = expr;
243        let mut changed = false;
244        while self.steps_taken < self.max_steps {
245            let (next, did_change) = self.step(&current);
246            if !did_change {
247                break;
248            }
249            current = next;
250            changed = true;
251            self.steps_taken += 1;
252        }
253        (current, changed)
254    }
255    fn step(&self, expr: &Expr) -> (Expr, bool) {
256        match expr {
257            Expr::App(f, _) => {
258                let args = collect_args_norm(expr);
259                if let Expr::Const(name, _) = &args[0] {
260                    if *name == Name::str("Quot.lift") && args.len() >= 4 {
261                        if let Some(r) = reduce_quot_lift(&args[1..]) {
262                            return (r, true);
263                        }
264                    }
265                    if *name == Name::str("Quot.ind") && args.len() >= 3 {
266                        if let Some(r) = reduce_quot_ind(&args[1..]) {
267                            return (r, true);
268                        }
269                    }
270                }
271                let (f2, cf) = self.step(f);
272                let arg = args.last().cloned().unwrap_or(Expr::BVar(0));
273                let (a2, ca) = self.step(&arg);
274                if cf || ca {
275                    return (Expr::App(Box::new(f2), Box::new(a2)), true);
276                }
277                (expr.clone(), false)
278            }
279            _ => (expr.clone(), false),
280        }
281    }
282    /// Number of steps taken.
283    #[allow(dead_code)]
284    pub fn steps_taken(&self) -> usize {
285        self.steps_taken
286    }
287    /// Reset the step counter.
288    #[allow(dead_code)]
289    pub fn reset(&mut self) {
290        self.steps_taken = 0;
291    }
292}
293/// A pool of reusable string buffers.
294#[allow(dead_code)]
295pub struct StringPool {
296    free: Vec<String>,
297}
298#[allow(dead_code)]
299impl StringPool {
300    /// Creates a new empty string pool.
301    pub fn new() -> Self {
302        Self { free: Vec::new() }
303    }
304    /// Takes a string from the pool (may be empty).
305    pub fn take(&mut self) -> String {
306        self.free.pop().unwrap_or_default()
307    }
308    /// Returns a string to the pool.
309    pub fn give(&mut self, mut s: String) {
310        s.clear();
311        self.free.push(s);
312    }
313    /// Returns the number of free strings in the pool.
314    pub fn free_count(&self) -> usize {
315        self.free.len()
316    }
317}
318/// Description of a quotient type for display purposes.
319pub struct QuotientDescription {
320    /// Display name of the base type.
321    pub base_name: String,
322    /// Display name of the relation.
323    pub relation_name: String,
324    /// Number of elements in a finite model (if known).
325    pub finite_model_size: Option<usize>,
326}
327impl QuotientDescription {
328    /// Create a description.
329    pub fn new(base_name: impl Into<String>, relation_name: impl Into<String>) -> Self {
330        Self {
331            base_name: base_name.into(),
332            relation_name: relation_name.into(),
333            finite_model_size: None,
334        }
335    }
336    /// Set the finite model size.
337    pub fn with_model_size(mut self, n: usize) -> Self {
338        self.finite_model_size = Some(n);
339        self
340    }
341    /// Return a human-readable summary.
342    pub fn display(&self) -> String {
343        match self.finite_model_size {
344            Some(n) => {
345                format!(
346                    "Quot {} {} ({} elements)",
347                    self.base_name, self.relation_name, n
348                )
349            }
350            None => format!("Quot {} {}", self.base_name, self.relation_name),
351        }
352    }
353}
354/// A pair of `StatSummary` values tracking before/after a transformation.
355#[allow(dead_code)]
356pub struct TransformStat {
357    before: StatSummary,
358    after: StatSummary,
359}
360#[allow(dead_code)]
361impl TransformStat {
362    /// Creates a new transform stat recorder.
363    pub fn new() -> Self {
364        Self {
365            before: StatSummary::new(),
366            after: StatSummary::new(),
367        }
368    }
369    /// Records a before value.
370    pub fn record_before(&mut self, v: f64) {
371        self.before.record(v);
372    }
373    /// Records an after value.
374    pub fn record_after(&mut self, v: f64) {
375        self.after.record(v);
376    }
377    /// Returns the mean reduction ratio (after/before).
378    pub fn mean_ratio(&self) -> Option<f64> {
379        let b = self.before.mean()?;
380        let a = self.after.mean()?;
381        if b.abs() < f64::EPSILON {
382            return None;
383        }
384        Some(a / b)
385    }
386}
387/// A label set for a graph node.
388#[allow(dead_code)]
389pub struct LabelSet {
390    labels: Vec<String>,
391}
392#[allow(dead_code)]
393impl LabelSet {
394    /// Creates a new empty label set.
395    pub fn new() -> Self {
396        Self { labels: Vec::new() }
397    }
398    /// Adds a label (deduplicates).
399    pub fn add(&mut self, label: impl Into<String>) {
400        let s = label.into();
401        if !self.labels.contains(&s) {
402            self.labels.push(s);
403        }
404    }
405    /// Returns `true` if `label` is present.
406    pub fn has(&self, label: &str) -> bool {
407        self.labels.iter().any(|l| l == label)
408    }
409    /// Returns the count of labels.
410    pub fn count(&self) -> usize {
411        self.labels.len()
412    }
413    /// Returns all labels.
414    pub fn all(&self) -> &[String] {
415        &self.labels
416    }
417}
418/// A stack-based quotient reduction engine.
419pub struct QuotientReducer {
420    steps: Vec<QuotReductionStep>,
421    #[allow(dead_code)]
422    cache: QuotLiftCache,
423    pub(crate) max_steps: usize,
424}
425impl QuotientReducer {
426    /// Create a new reducer with the given step limit.
427    pub fn new(max_steps: usize) -> Self {
428        Self {
429            steps: Vec::new(),
430            cache: QuotLiftCache::new(),
431            max_steps,
432        }
433    }
434    /// Try to reduce an expression. Returns the reduced expression and whether any reduction happened.
435    pub fn reduce(&mut self, expr: &Expr) -> (Expr, bool) {
436        match expr {
437            Expr::App(f, _arg) => {
438                if let Expr::Const(name, _) = f.as_ref() {
439                    if *name == Name::str("Quot.lift") {
440                        let args = collect_args(expr);
441                        if let Some((reduced, kind)) = try_reduce_quot_full(&args[0], &args[1..]) {
442                            let step = QuotReductionStep::new(kind, expr.clone(), reduced.clone());
443                            if self.steps.len() < self.max_steps {
444                                self.steps.push(step);
445                            }
446                            return (reduced, true);
447                        }
448                    }
449                }
450                (expr.clone(), false)
451            }
452            _ => (expr.clone(), false),
453        }
454    }
455    /// Get all recorded reduction steps.
456    pub fn steps(&self) -> &[QuotReductionStep] {
457        &self.steps
458    }
459    /// Clear the step history.
460    pub fn clear_steps(&mut self) {
461        self.steps.clear();
462    }
463    /// Number of recorded steps.
464    pub fn step_count(&self) -> usize {
465        self.steps.len()
466    }
467}
468/// A quotient type declaration.
469#[derive(Clone, Debug, PartialEq)]
470pub struct QuotientType {
471    /// Type being quotiented.
472    pub base_type: Expr,
473    /// Equivalence relation.
474    pub relation: Expr,
475    /// The quotient type itself.
476    pub quot_type: Expr,
477}
478impl QuotientType {
479    /// Create a new quotient type.
480    pub fn new(base_type: Expr, relation: Expr) -> Self {
481        let quot_type = Expr::App(
482            Box::new(Expr::App(
483                Box::new(Expr::Const(Name::str("Quot"), vec![])),
484                Box::new(base_type.clone()),
485            )),
486            Box::new(relation.clone()),
487        );
488        Self {
489            base_type,
490            relation,
491            quot_type,
492        }
493    }
494    /// Get the mk constant.
495    pub fn mk_const(&self) -> Expr {
496        Expr::Const(Name::str("Quot.mk"), vec![])
497    }
498    /// Apply Quot.mk to an element.
499    pub fn mk_apply(&self, elem: Expr) -> Expr {
500        Expr::App(Box::new(self.mk_const()), Box::new(elem))
501    }
502    /// Get the lift constant.
503    pub fn lift_const(&self) -> Expr {
504        Expr::Const(Name::str("Quot.lift"), vec![])
505    }
506    /// Get the ind constant.
507    pub fn ind_const(&self) -> Expr {
508        Expr::Const(Name::str("Quot.ind"), vec![])
509    }
510    /// Get the sound constant.
511    pub fn sound_const(&self) -> Expr {
512        Expr::Const(Name::str("Quot.sound"), vec![])
513    }
514    /// Apply Quot.lift f h q.
515    pub fn lift_apply(&self, f: Expr, h: Expr, q: Expr) -> Expr {
516        Expr::App(
517            Box::new(Expr::App(
518                Box::new(Expr::App(Box::new(self.lift_const()), Box::new(f))),
519                Box::new(h),
520            )),
521            Box::new(q),
522        )
523    }
524}
525/// A simple cache for Quot.lift reductions.
526#[derive(Clone, Debug, Default)]
527pub struct QuotLiftCache {
528    cache: std::collections::HashMap<String, Expr>,
529}
530impl QuotLiftCache {
531    /// Create an empty cache.
532    pub fn new() -> Self {
533        Self {
534            cache: std::collections::HashMap::new(),
535        }
536    }
537    /// Look up a cached reduction.
538    pub fn get(&self, f: &Expr, a: &Expr) -> Option<&Expr> {
539        let key = format!("{:?}-{:?}", f, a);
540        self.cache.get(&key)
541    }
542    /// Store a reduction result.
543    pub fn put(&mut self, f: &Expr, a: &Expr, result: Expr) {
544        let key = format!("{:?}-{:?}", f, a);
545        self.cache.insert(key, result);
546    }
547    /// Clear the cache.
548    pub fn clear(&mut self) {
549        self.cache.clear();
550    }
551    /// Number of cached entries.
552    pub fn len(&self) -> usize {
553        self.cache.len()
554    }
555    /// Check if empty.
556    pub fn is_empty(&self) -> bool {
557        self.cache.is_empty()
558    }
559}
560/// A token bucket rate limiter.
561#[allow(dead_code)]
562pub struct TokenBucket {
563    capacity: u64,
564    tokens: u64,
565    refill_per_ms: u64,
566    last_refill: std::time::Instant,
567}
568#[allow(dead_code)]
569impl TokenBucket {
570    /// Creates a new token bucket.
571    pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
572        Self {
573            capacity,
574            tokens: capacity,
575            refill_per_ms,
576            last_refill: std::time::Instant::now(),
577        }
578    }
579    /// Attempts to consume `n` tokens.  Returns `true` on success.
580    pub fn try_consume(&mut self, n: u64) -> bool {
581        self.refill();
582        if self.tokens >= n {
583            self.tokens -= n;
584            true
585        } else {
586            false
587        }
588    }
589    fn refill(&mut self) {
590        let now = std::time::Instant::now();
591        let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
592        if elapsed_ms > 0 {
593            let new_tokens = elapsed_ms * self.refill_per_ms;
594            self.tokens = (self.tokens + new_tokens).min(self.capacity);
595            self.last_refill = now;
596        }
597    }
598    /// Returns the number of currently available tokens.
599    pub fn available(&self) -> u64 {
600        self.tokens
601    }
602    /// Returns the bucket capacity.
603    pub fn capacity(&self) -> u64 {
604        self.capacity
605    }
606}
607/// Equivalence class representative system.
608///
609/// Given an equivalence relation, this tracks canonical representatives for
610/// each equivalence class using a union-find style approach.
611#[derive(Clone, Debug, Default)]
612pub struct EquivClassSystem {
613    /// Map from expression debug string to the canonical representative.
614    reps: std::collections::HashMap<String, Expr>,
615}
616impl EquivClassSystem {
617    /// Create an empty system.
618    pub fn new() -> Self {
619        Self {
620            reps: std::collections::HashMap::new(),
621        }
622    }
623    /// Register `expr` as its own representative (singleton class).
624    pub fn insert(&mut self, expr: Expr) {
625        let key = format!("{:?}", expr);
626        self.reps.entry(key).or_insert(expr);
627    }
628    /// Merge the classes of `a` and `b` (choosing `a` as canonical).
629    pub fn merge(&mut self, a: &Expr, b: &Expr) {
630        let key_b = format!("{:?}", b);
631        let rep_a = self.rep_of(a).unwrap_or_else(|| a.clone());
632        self.reps.insert(key_b, rep_a);
633    }
634    /// Look up the representative of `expr`.
635    pub fn rep_of(&self, expr: &Expr) -> Option<Expr> {
636        let key = format!("{:?}", expr);
637        self.reps.get(&key).cloned()
638    }
639    /// Check whether `a` and `b` have the same representative.
640    pub fn same_class(&self, a: &Expr, b: &Expr) -> bool {
641        match (self.rep_of(a), self.rep_of(b)) {
642            (Some(ra), Some(rb)) => ra == rb,
643            _ => a == b,
644        }
645    }
646    /// Number of registered expressions.
647    pub fn len(&self) -> usize {
648        self.reps.len()
649    }
650    /// Check if empty.
651    pub fn is_empty(&self) -> bool {
652        self.reps.is_empty()
653    }
654}
655/// A window iterator that yields overlapping windows of size `n`.
656#[allow(dead_code)]
657pub struct WindowIterator<'a, T> {
658    pub(super) data: &'a [T],
659    pub(super) pos: usize,
660    pub(super) window: usize,
661}
662#[allow(dead_code)]
663impl<'a, T> WindowIterator<'a, T> {
664    /// Creates a new window iterator.
665    pub fn new(data: &'a [T], window: usize) -> Self {
666        Self {
667            data,
668            pos: 0,
669            window,
670        }
671    }
672}
673/// Builder for constructing quotient type structures step by step.
674#[derive(Debug, Default)]
675pub struct QuotientBuilder {
676    base_type: Option<Expr>,
677    relation: Option<Expr>,
678}
679impl QuotientBuilder {
680    /// Create a new empty builder.
681    pub fn new() -> Self {
682        Self {
683            base_type: None,
684            relation: None,
685        }
686    }
687    /// Set the base type.
688    pub fn base(mut self, ty: Expr) -> Self {
689        self.base_type = Some(ty);
690        self
691    }
692    /// Set the equivalence relation.
693    pub fn relation(mut self, rel: Expr) -> Self {
694        self.relation = Some(rel);
695        self
696    }
697    /// Build the `QuotientType`, returning `None` if either field is missing.
698    pub fn build(self) -> Option<QuotientType> {
699        Some(QuotientType::new(self.base_type?, self.relation?))
700    }
701}
702/// A fixed-size sliding window that computes a running sum.
703#[allow(dead_code)]
704pub struct SlidingSum {
705    window: Vec<f64>,
706    capacity: usize,
707    pos: usize,
708    sum: f64,
709    count: usize,
710}
711#[allow(dead_code)]
712impl SlidingSum {
713    /// Creates a sliding sum with the given window size.
714    pub fn new(capacity: usize) -> Self {
715        Self {
716            window: vec![0.0; capacity],
717            capacity,
718            pos: 0,
719            sum: 0.0,
720            count: 0,
721        }
722    }
723    /// Adds a value to the window, removing the oldest if full.
724    pub fn push(&mut self, val: f64) {
725        let oldest = self.window[self.pos];
726        self.sum -= oldest;
727        self.sum += val;
728        self.window[self.pos] = val;
729        self.pos = (self.pos + 1) % self.capacity;
730        if self.count < self.capacity {
731            self.count += 1;
732        }
733    }
734    /// Returns the current window sum.
735    pub fn sum(&self) -> f64 {
736        self.sum
737    }
738    /// Returns the window mean, or `None` if empty.
739    pub fn mean(&self) -> Option<f64> {
740        if self.count == 0 {
741            None
742        } else {
743            Some(self.sum / self.count as f64)
744        }
745    }
746    /// Returns the current window size (number of valid elements).
747    pub fn count(&self) -> usize {
748        self.count
749    }
750}
751/// Kind of a quotient reduction rule.
752#[derive(Clone, Debug, PartialEq, Eq)]
753pub enum QuotReductionKind {
754    /// Quot.lift f h (Quot.mk a) → f a.
755    Lift,
756    /// Quot.ind h (Quot.mk a) → h a.
757    Ind,
758    /// Quot.sound h → Quot.mk a = Quot.mk b (when r a b).
759    Sound,
760}
761impl QuotReductionKind {
762    /// Human-readable description.
763    pub fn description(&self) -> &'static str {
764        match self {
765            QuotReductionKind::Lift => "Quot.lift reduction",
766            QuotReductionKind::Ind => "Quot.ind reduction",
767            QuotReductionKind::Sound => "Quot.sound",
768        }
769    }
770}
771/// A hierarchical configuration tree.
772#[allow(dead_code)]
773pub struct ConfigNode {
774    key: String,
775    value: Option<String>,
776    children: Vec<ConfigNode>,
777}
778#[allow(dead_code)]
779impl ConfigNode {
780    /// Creates a leaf config node with a value.
781    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
782        Self {
783            key: key.into(),
784            value: Some(value.into()),
785            children: Vec::new(),
786        }
787    }
788    /// Creates a section node with children.
789    pub fn section(key: impl Into<String>) -> Self {
790        Self {
791            key: key.into(),
792            value: None,
793            children: Vec::new(),
794        }
795    }
796    /// Adds a child node.
797    pub fn add_child(&mut self, child: ConfigNode) {
798        self.children.push(child);
799    }
800    /// Returns the key.
801    pub fn key(&self) -> &str {
802        &self.key
803    }
804    /// Returns the value, or `None` for section nodes.
805    pub fn value(&self) -> Option<&str> {
806        self.value.as_deref()
807    }
808    /// Returns the number of children.
809    pub fn num_children(&self) -> usize {
810        self.children.len()
811    }
812    /// Looks up a dot-separated path.
813    pub fn lookup(&self, path: &str) -> Option<&str> {
814        let mut parts = path.splitn(2, '.');
815        let head = parts.next()?;
816        let tail = parts.next();
817        if head != self.key {
818            return None;
819        }
820        match tail {
821            None => self.value.as_deref(),
822            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
823        }
824    }
825    fn lookup_relative(&self, path: &str) -> Option<&str> {
826        let mut parts = path.splitn(2, '.');
827        let head = parts.next()?;
828        let tail = parts.next();
829        if head != self.key {
830            return None;
831        }
832        match tail {
833            None => self.value.as_deref(),
834            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
835        }
836    }
837}
838/// A mutable reference stack for tracking the current "focus" in a tree traversal.
839#[allow(dead_code)]
840pub struct FocusStack<T> {
841    items: Vec<T>,
842}
843#[allow(dead_code)]
844impl<T> FocusStack<T> {
845    /// Creates an empty focus stack.
846    pub fn new() -> Self {
847        Self { items: Vec::new() }
848    }
849    /// Focuses on `item`.
850    pub fn focus(&mut self, item: T) {
851        self.items.push(item);
852    }
853    /// Blurs (pops) the current focus.
854    pub fn blur(&mut self) -> Option<T> {
855        self.items.pop()
856    }
857    /// Returns the current focus, or `None`.
858    pub fn current(&self) -> Option<&T> {
859        self.items.last()
860    }
861    /// Returns the focus depth.
862    pub fn depth(&self) -> usize {
863        self.items.len()
864    }
865    /// Returns `true` if there is no current focus.
866    pub fn is_empty(&self) -> bool {
867        self.items.is_empty()
868    }
869}
870/// Statistics about quotient usage in an expression.
871#[derive(Debug, Default, Clone)]
872pub struct QuotStats {
873    /// Total `Quot.mk` applications found.
874    pub mk_count: usize,
875    /// Total `Quot.lift` applications found.
876    pub lift_count: usize,
877    /// Total `Quot.ind` applications found.
878    pub ind_count: usize,
879    /// Total `Quot.sound` applications found.
880    pub sound_count: usize,
881}
882impl QuotStats {
883    /// Compute statistics for an expression.
884    pub fn compute(expr: &Expr) -> Self {
885        let mut stats = Self::default();
886        Self::walk(expr, &mut stats);
887        stats
888    }
889    fn walk(expr: &Expr, stats: &mut Self) {
890        match expr {
891            Expr::Const(name, _) => {
892                if *name == Name::str("Quot.mk") {
893                    stats.mk_count += 1;
894                } else if *name == Name::str("Quot.lift") {
895                    stats.lift_count += 1;
896                } else if *name == Name::str("Quot.ind") {
897                    stats.ind_count += 1;
898                } else if *name == Name::str("Quot.sound") {
899                    stats.sound_count += 1;
900                }
901            }
902            Expr::App(f, a) => {
903                Self::walk(f, stats);
904                Self::walk(a, stats);
905            }
906            Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
907                Self::walk(ty, stats);
908                Self::walk(body, stats);
909            }
910            Expr::Let(_, ty, val, body) => {
911                Self::walk(ty, stats);
912                Self::walk(val, stats);
913                Self::walk(body, stats);
914            }
915            _ => {}
916        }
917    }
918    /// Total number of quotient-related subterms.
919    pub fn total(&self) -> usize {
920        self.mk_count + self.lift_count + self.ind_count + self.sound_count
921    }
922    /// Check whether any quotient terms appear.
923    pub fn has_quot(&self) -> bool {
924        self.total() > 0
925    }
926}
927/// Properties of an equivalence relation.
928#[derive(Debug, Clone, PartialEq, Eq)]
929pub enum EquivProperty {
930    /// Reflexivity: ∀ a, r a a.
931    Reflexive,
932    /// Symmetry: ∀ a b, r a b → r b a.
933    Symmetric,
934    /// Transitivity: ∀ a b c, r a b → r b c → r a c.
935    Transitive,
936}
937/// A simple directed acyclic graph.
938#[allow(dead_code)]
939pub struct SimpleDag {
940    /// `edges[i]` is the list of direct successors of node `i`.
941    edges: Vec<Vec<usize>>,
942}
943#[allow(dead_code)]
944impl SimpleDag {
945    /// Creates a DAG with `n` nodes and no edges.
946    pub fn new(n: usize) -> Self {
947        Self {
948            edges: vec![Vec::new(); n],
949        }
950    }
951    /// Adds an edge from `from` to `to`.
952    pub fn add_edge(&mut self, from: usize, to: usize) {
953        if from < self.edges.len() {
954            self.edges[from].push(to);
955        }
956    }
957    /// Returns the successors of `node`.
958    pub fn successors(&self, node: usize) -> &[usize] {
959        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
960    }
961    /// Returns `true` if `from` can reach `to` via DFS.
962    pub fn can_reach(&self, from: usize, to: usize) -> bool {
963        let mut visited = vec![false; self.edges.len()];
964        self.dfs(from, to, &mut visited)
965    }
966    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
967        if cur == target {
968            return true;
969        }
970        if cur >= visited.len() || visited[cur] {
971            return false;
972        }
973        visited[cur] = true;
974        for &next in self.successors(cur) {
975            if self.dfs(next, target, visited) {
976                return true;
977            }
978        }
979        false
980    }
981    /// Returns the topological order of nodes, or `None` if a cycle is detected.
982    pub fn topological_sort(&self) -> Option<Vec<usize>> {
983        let n = self.edges.len();
984        let mut in_degree = vec![0usize; n];
985        for succs in &self.edges {
986            for &s in succs {
987                if s < n {
988                    in_degree[s] += 1;
989                }
990            }
991        }
992        let mut queue: std::collections::VecDeque<usize> =
993            (0..n).filter(|&i| in_degree[i] == 0).collect();
994        let mut order = Vec::new();
995        while let Some(node) = queue.pop_front() {
996            order.push(node);
997            for &s in self.successors(node) {
998                if s < n {
999                    in_degree[s] -= 1;
1000                    if in_degree[s] == 0 {
1001                        queue.push_back(s);
1002                    }
1003                }
1004            }
1005        }
1006        if order.len() == n {
1007            Some(order)
1008        } else {
1009            None
1010        }
1011    }
1012    /// Returns the number of nodes.
1013    pub fn num_nodes(&self) -> usize {
1014        self.edges.len()
1015    }
1016}
1017/// A type-erased function pointer with arity tracking.
1018#[allow(dead_code)]
1019pub struct RawFnPtr {
1020    /// The raw function pointer (stored as usize for type erasure).
1021    ptr: usize,
1022    arity: usize,
1023    name: String,
1024}
1025#[allow(dead_code)]
1026impl RawFnPtr {
1027    /// Creates a new raw function pointer descriptor.
1028    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
1029        Self {
1030            ptr,
1031            arity,
1032            name: name.into(),
1033        }
1034    }
1035    /// Returns the arity.
1036    pub fn arity(&self) -> usize {
1037        self.arity
1038    }
1039    /// Returns the name.
1040    pub fn name(&self) -> &str {
1041        &self.name
1042    }
1043    /// Returns the raw pointer value.
1044    pub fn raw(&self) -> usize {
1045        self.ptr
1046    }
1047}
1048/// Which quotient elimination form is being checked.
1049#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1050pub enum QuotUsageKind {
1051    /// `Quot.lift` — eliminator; valid for any motive sort.
1052    Lift,
1053    /// `Quot.ind` — induction principle; motive must be propositional.
1054    Ind,
1055}
1056/// A simple key-value store backed by a sorted Vec for small maps.
1057#[allow(dead_code)]
1058pub struct SmallMap<K: Ord + Clone, V: Clone> {
1059    entries: Vec<(K, V)>,
1060}
1061#[allow(dead_code)]
1062impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
1063    /// Creates a new empty small map.
1064    pub fn new() -> Self {
1065        Self {
1066            entries: Vec::new(),
1067        }
1068    }
1069    /// Inserts or replaces the value for `key`.
1070    pub fn insert(&mut self, key: K, val: V) {
1071        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
1072            Ok(i) => self.entries[i].1 = val,
1073            Err(i) => self.entries.insert(i, (key, val)),
1074        }
1075    }
1076    /// Returns the value for `key`, or `None`.
1077    pub fn get(&self, key: &K) -> Option<&V> {
1078        self.entries
1079            .binary_search_by_key(&key, |(k, _)| k)
1080            .ok()
1081            .map(|i| &self.entries[i].1)
1082    }
1083    /// Returns the number of entries.
1084    pub fn len(&self) -> usize {
1085        self.entries.len()
1086    }
1087    /// Returns `true` if empty.
1088    pub fn is_empty(&self) -> bool {
1089        self.entries.is_empty()
1090    }
1091    /// Returns all keys.
1092    pub fn keys(&self) -> Vec<&K> {
1093        self.entries.iter().map(|(k, _)| k).collect()
1094    }
1095    /// Returns all values.
1096    pub fn values(&self) -> Vec<&V> {
1097        self.entries.iter().map(|(_, v)| v).collect()
1098    }
1099}
1100/// A dependency closure builder (transitive closure via BFS).
1101#[allow(dead_code)]
1102pub struct TransitiveClosure {
1103    adj: Vec<Vec<usize>>,
1104    n: usize,
1105}
1106#[allow(dead_code)]
1107impl TransitiveClosure {
1108    /// Creates a transitive closure builder for `n` nodes.
1109    pub fn new(n: usize) -> Self {
1110        Self {
1111            adj: vec![Vec::new(); n],
1112            n,
1113        }
1114    }
1115    /// Adds a direct edge.
1116    pub fn add_edge(&mut self, from: usize, to: usize) {
1117        if from < self.n {
1118            self.adj[from].push(to);
1119        }
1120    }
1121    /// Computes all nodes reachable from `start` (including `start`).
1122    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
1123        let mut visited = vec![false; self.n];
1124        let mut queue = std::collections::VecDeque::new();
1125        queue.push_back(start);
1126        while let Some(node) = queue.pop_front() {
1127            if node >= self.n || visited[node] {
1128                continue;
1129            }
1130            visited[node] = true;
1131            for &next in &self.adj[node] {
1132                queue.push_back(next);
1133            }
1134        }
1135        (0..self.n).filter(|&i| visited[i]).collect()
1136    }
1137    /// Returns `true` if `from` can transitively reach `to`.
1138    pub fn can_reach(&self, from: usize, to: usize) -> bool {
1139        self.reachable_from(from).contains(&to)
1140    }
1141}
1142/// Represents a rewrite rule `lhs → rhs`.
1143#[allow(dead_code)]
1144#[allow(missing_docs)]
1145pub struct RewriteRule {
1146    /// The name of the rule.
1147    pub name: String,
1148    /// A string representation of the LHS pattern.
1149    pub lhs: String,
1150    /// A string representation of the RHS.
1151    pub rhs: String,
1152    /// Whether this is a conditional rule (has side conditions).
1153    pub conditional: bool,
1154}
1155#[allow(dead_code)]
1156impl RewriteRule {
1157    /// Creates an unconditional rewrite rule.
1158    pub fn unconditional(
1159        name: impl Into<String>,
1160        lhs: impl Into<String>,
1161        rhs: impl Into<String>,
1162    ) -> Self {
1163        Self {
1164            name: name.into(),
1165            lhs: lhs.into(),
1166            rhs: rhs.into(),
1167            conditional: false,
1168        }
1169    }
1170    /// Creates a conditional rewrite rule.
1171    pub fn conditional(
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: true,
1181        }
1182    }
1183    /// Returns a textual representation.
1184    pub fn display(&self) -> String {
1185        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
1186    }
1187}
1188/// A single quotient reduction step (for tracing).
1189#[derive(Clone, Debug)]
1190pub struct QuotReductionStep {
1191    /// Which rule was applied.
1192    pub kind: QuotReductionKind,
1193    /// The expression before reduction.
1194    pub before: Expr,
1195    /// The expression after reduction.
1196    pub after: Expr,
1197}
1198impl QuotReductionStep {
1199    /// Create a new reduction step.
1200    pub fn new(kind: QuotReductionKind, before: Expr, after: Expr) -> Self {
1201        Self {
1202            kind,
1203            before,
1204            after,
1205        }
1206    }
1207}
1208/// A set of rewrite rules.
1209#[allow(dead_code)]
1210pub struct RewriteRuleSet {
1211    rules: Vec<RewriteRule>,
1212}
1213#[allow(dead_code)]
1214impl RewriteRuleSet {
1215    /// Creates an empty rule set.
1216    pub fn new() -> Self {
1217        Self { rules: Vec::new() }
1218    }
1219    /// Adds a rule.
1220    pub fn add(&mut self, rule: RewriteRule) {
1221        self.rules.push(rule);
1222    }
1223    /// Returns the number of rules.
1224    pub fn len(&self) -> usize {
1225        self.rules.len()
1226    }
1227    /// Returns `true` if the set is empty.
1228    pub fn is_empty(&self) -> bool {
1229        self.rules.is_empty()
1230    }
1231    /// Returns all conditional rules.
1232    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
1233        self.rules.iter().filter(|r| r.conditional).collect()
1234    }
1235    /// Returns all unconditional rules.
1236    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
1237        self.rules.iter().filter(|r| !r.conditional).collect()
1238    }
1239    /// Looks up a rule by name.
1240    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
1241        self.rules.iter().find(|r| r.name == name)
1242    }
1243}