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