Skip to main content

oxilean_kernel/axiom/
types.rs

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