Skip to main content

oxilean_meta/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::tactic::TacticState;
6use std::collections::{HashMap, VecDeque};
7
8/// Report of proof state completeness.
9#[derive(Debug, Clone)]
10pub struct ProofStateReport {
11    /// Number of open goals.
12    pub open_goals: usize,
13    /// Whether the proof is complete.
14    pub is_complete: bool,
15}
16impl ProofStateReport {
17    /// Create from a tactic state.
18    pub fn from_state(state: &TacticState) -> Self {
19        ProofStateReport {
20            open_goals: state.num_goals(),
21            is_complete: state.is_done(),
22        }
23    }
24}
25/// A scored candidate.
26#[derive(Debug, Clone)]
27pub struct ScoredCandidate<T> {
28    /// The candidate.
29    pub candidate: T,
30    /// Score.
31    pub score: i64,
32}
33impl<T> ScoredCandidate<T> {
34    /// Create a new scored candidate.
35    pub fn new(candidate: T, score: i64) -> Self {
36        Self { candidate, score }
37    }
38}
39#[allow(dead_code)]
40#[derive(Debug, Clone)]
41pub enum LibExtResult1300 {
42    /// Operation completed successfully.
43    Ok(String),
44    /// Operation encountered an error.
45    Err(String),
46    /// Operation partially completed.
47    Partial { done: usize, total: usize },
48    /// Operation was skipped.
49    Skipped,
50}
51impl LibExtResult1300 {
52    #[allow(dead_code)]
53    pub fn is_ok(&self) -> bool {
54        matches!(self, LibExtResult1300::Ok(_))
55    }
56    #[allow(dead_code)]
57    pub fn is_err(&self) -> bool {
58        matches!(self, LibExtResult1300::Err(_))
59    }
60    #[allow(dead_code)]
61    pub fn is_partial(&self) -> bool {
62        matches!(self, LibExtResult1300::Partial { .. })
63    }
64    #[allow(dead_code)]
65    pub fn is_skipped(&self) -> bool {
66        matches!(self, LibExtResult1300::Skipped)
67    }
68    #[allow(dead_code)]
69    pub fn ok_msg(&self) -> Option<&str> {
70        if let LibExtResult1300::Ok(s) = self {
71            Some(s)
72        } else {
73            None
74        }
75    }
76    #[allow(dead_code)]
77    pub fn err_msg(&self) -> Option<&str> {
78        if let LibExtResult1300::Err(s) = self {
79            Some(s)
80        } else {
81            None
82        }
83    }
84    #[allow(dead_code)]
85    pub fn progress(&self) -> f64 {
86        match self {
87            LibExtResult1300::Ok(_) => 1.0,
88            LibExtResult1300::Err(_) => 0.0,
89            LibExtResult1300::Partial { done, total } => {
90                if *total == 0 {
91                    0.0
92                } else {
93                    *done as f64 / *total as f64
94                }
95            }
96            LibExtResult1300::Skipped => 0.5,
97        }
98    }
99}
100/// A result type for Lib analysis.
101#[allow(dead_code)]
102#[derive(Debug, Clone, PartialEq)]
103pub enum LibResult {
104    Ok(String),
105    Err(String),
106    Partial { done: usize, total: usize },
107    Skipped,
108}
109#[allow(dead_code)]
110impl LibResult {
111    pub fn is_ok(&self) -> bool {
112        matches!(self, LibResult::Ok(_))
113    }
114    pub fn is_err(&self) -> bool {
115        matches!(self, LibResult::Err(_))
116    }
117    pub fn is_partial(&self) -> bool {
118        matches!(self, LibResult::Partial { .. })
119    }
120    pub fn is_skipped(&self) -> bool {
121        matches!(self, LibResult::Skipped)
122    }
123    pub fn ok_msg(&self) -> Option<&str> {
124        match self {
125            LibResult::Ok(s) => Some(s),
126            _ => None,
127        }
128    }
129    pub fn err_msg(&self) -> Option<&str> {
130        match self {
131            LibResult::Err(s) => Some(s),
132            _ => None,
133        }
134    }
135    pub fn progress(&self) -> f64 {
136        match self {
137            LibResult::Ok(_) => 1.0,
138            LibResult::Err(_) => 0.0,
139            LibResult::Skipped => 0.0,
140            LibResult::Partial { done, total } => {
141                if *total == 0 {
142                    0.0
143                } else {
144                    *done as f64 / *total as f64
145                }
146            }
147        }
148    }
149}
150/// A builder pattern for MetaLib.
151#[allow(dead_code)]
152pub struct MetaLibBuilder {
153    pub name: String,
154    pub items: Vec<String>,
155    pub config: std::collections::HashMap<String, String>,
156}
157#[allow(dead_code)]
158impl MetaLibBuilder {
159    pub fn new(name: &str) -> Self {
160        MetaLibBuilder {
161            name: name.to_string(),
162            items: Vec::new(),
163            config: std::collections::HashMap::new(),
164        }
165    }
166    pub fn add_item(mut self, item: &str) -> Self {
167        self.items.push(item.to_string());
168        self
169    }
170    pub fn set_config(mut self, key: &str, value: &str) -> Self {
171        self.config.insert(key.to_string(), value.to_string());
172        self
173    }
174    pub fn item_count(&self) -> usize {
175        self.items.len()
176    }
177    pub fn has_config(&self, key: &str) -> bool {
178        self.config.contains_key(key)
179    }
180    pub fn get_config(&self, key: &str) -> Option<&str> {
181        self.config.get(key).map(|s| s.as_str())
182    }
183    pub fn build_summary(&self) -> String {
184        format!(
185            "{}: {} items, {} config keys",
186            self.name,
187            self.items.len(),
188            self.config.len()
189        )
190    }
191}
192/// A simple cache for memoizing meta computations.
193#[derive(Debug)]
194pub struct MetaCache<K, V> {
195    pub entries: std::collections::HashMap<K, V>,
196    pub capacity: usize,
197    pub hits: u64,
198    pub misses: u64,
199}
200impl<K: std::hash::Hash + Eq + Clone, V> MetaCache<K, V> {
201    /// Create a cache with a given capacity.
202    pub fn with_capacity(capacity: usize) -> Self {
203        Self {
204            entries: std::collections::HashMap::with_capacity(capacity),
205            capacity,
206            hits: 0,
207            misses: 0,
208        }
209    }
210    /// Insert a value.
211    pub fn insert(&mut self, key: K, value: V) {
212        if self.entries.len() >= self.capacity {
213            let len = self.entries.len();
214            if len > 0 {
215                let to_remove = len / 2;
216                let keys: Vec<K> = self.entries.keys().take(to_remove).cloned().collect();
217                for k in keys {
218                    self.entries.remove(&k);
219                }
220            }
221        }
222        self.entries.insert(key, value);
223    }
224    /// Look up a value.
225    pub fn get(&mut self, key: &K) -> Option<&V> {
226        if self.entries.contains_key(key) {
227            self.hits += 1;
228            self.entries.get(key)
229        } else {
230            self.misses += 1;
231            None
232        }
233    }
234    /// Cache hit rate.
235    pub fn hit_rate(&self) -> f64 {
236        let total = self.hits + self.misses;
237        if total == 0 {
238            0.0
239        } else {
240            self.hits as f64 / total as f64
241        }
242    }
243    /// Number of entries.
244    pub fn len(&self) -> usize {
245        self.entries.len()
246    }
247    /// Whether empty.
248    pub fn is_empty(&self) -> bool {
249        self.entries.is_empty()
250    }
251    /// Clear the cache.
252    pub fn clear(&mut self) {
253        self.entries.clear();
254        self.hits = 0;
255        self.misses = 0;
256    }
257}
258/// A pipeline of Lib analysis passes.
259#[allow(dead_code)]
260pub struct LibPipeline {
261    pub passes: Vec<LibAnalysisPass>,
262    pub name: String,
263    pub total_inputs_processed: usize,
264}
265#[allow(dead_code)]
266impl LibPipeline {
267    pub fn new(name: &str) -> Self {
268        LibPipeline {
269            passes: Vec::new(),
270            name: name.to_string(),
271            total_inputs_processed: 0,
272        }
273    }
274    pub fn add_pass(&mut self, pass: LibAnalysisPass) {
275        self.passes.push(pass);
276    }
277    pub fn run_all(&mut self, input: &str) -> Vec<LibResult> {
278        self.total_inputs_processed += 1;
279        self.passes
280            .iter_mut()
281            .filter(|p| p.enabled)
282            .map(|p| p.run(input))
283            .collect()
284    }
285    pub fn num_passes(&self) -> usize {
286        self.passes.len()
287    }
288    pub fn num_enabled_passes(&self) -> usize {
289        self.passes.iter().filter(|p| p.enabled).count()
290    }
291    pub fn total_success_rate(&self) -> f64 {
292        if self.passes.is_empty() {
293            0.0
294        } else {
295            let total_rate: f64 = self.passes.iter().map(|p| p.success_rate()).sum();
296            total_rate / self.passes.len() as f64
297        }
298    }
299}
300#[allow(dead_code)]
301pub struct LibExtDiff1300 {
302    pub added: Vec<String>,
303    pub removed: Vec<String>,
304    pub unchanged: Vec<String>,
305}
306impl LibExtDiff1300 {
307    #[allow(dead_code)]
308    pub fn new() -> Self {
309        Self {
310            added: Vec::new(),
311            removed: Vec::new(),
312            unchanged: Vec::new(),
313        }
314    }
315    #[allow(dead_code)]
316    pub fn add(&mut self, s: &str) {
317        self.added.push(s.to_string());
318    }
319    #[allow(dead_code)]
320    pub fn remove(&mut self, s: &str) {
321        self.removed.push(s.to_string());
322    }
323    #[allow(dead_code)]
324    pub fn keep(&mut self, s: &str) {
325        self.unchanged.push(s.to_string());
326    }
327    #[allow(dead_code)]
328    pub fn is_empty(&self) -> bool {
329        self.added.is_empty() && self.removed.is_empty()
330    }
331    #[allow(dead_code)]
332    pub fn total_changes(&self) -> usize {
333        self.added.len() + self.removed.len()
334    }
335    #[allow(dead_code)]
336    pub fn net_additions(&self) -> i64 {
337        self.added.len() as i64 - self.removed.len() as i64
338    }
339    #[allow(dead_code)]
340    pub fn summary(&self) -> String {
341        format!(
342            "+{} -{} =={}",
343            self.added.len(),
344            self.removed.len(),
345            self.unchanged.len()
346        )
347    }
348}
349/// A named group of related tactics.
350#[derive(Clone, Debug)]
351pub struct TacticGroup {
352    /// Group name.
353    pub name: String,
354    /// Tactic names in this group.
355    pub members: Vec<String>,
356    /// Short description of the group.
357    pub description: String,
358}
359impl TacticGroup {
360    /// Create a tactic group.
361    pub fn new(name: &str, description: &str) -> Self {
362        Self {
363            name: name.to_string(),
364            members: Vec::new(),
365            description: description.to_string(),
366        }
367    }
368    /// Add a member tactic.
369    #[allow(clippy::should_implement_trait)]
370    pub fn add(mut self, tactic: &str) -> Self {
371        self.members.push(tactic.to_string());
372        self
373    }
374    /// Whether a tactic is in this group.
375    pub fn contains(&self, tactic: &str) -> bool {
376        self.members.iter().any(|m| m == tactic)
377    }
378}
379/// An extended map for MetaLib keys to values.
380#[allow(dead_code)]
381pub struct MetaLibExtMap<V> {
382    pub data: std::collections::HashMap<String, V>,
383    pub default_key: Option<String>,
384}
385#[allow(dead_code)]
386impl<V: Clone + Default> MetaLibExtMap<V> {
387    pub fn new() -> Self {
388        MetaLibExtMap {
389            data: std::collections::HashMap::new(),
390            default_key: None,
391        }
392    }
393    pub fn insert(&mut self, key: &str, value: V) {
394        self.data.insert(key.to_string(), value);
395    }
396    pub fn get(&self, key: &str) -> Option<&V> {
397        self.data.get(key)
398    }
399    pub fn get_or_default(&self, key: &str) -> V {
400        self.data.get(key).cloned().unwrap_or_default()
401    }
402    pub fn contains(&self, key: &str) -> bool {
403        self.data.contains_key(key)
404    }
405    pub fn remove(&mut self, key: &str) -> Option<V> {
406        self.data.remove(key)
407    }
408    pub fn size(&self) -> usize {
409        self.data.len()
410    }
411    pub fn is_empty(&self) -> bool {
412        self.data.is_empty()
413    }
414    pub fn set_default(&mut self, key: &str) {
415        self.default_key = Some(key.to_string());
416    }
417    pub fn keys_sorted(&self) -> Vec<&String> {
418        let mut keys: Vec<&String> = self.data.keys().collect();
419        keys.sort();
420        keys
421    }
422}
423/// A counter map for MetaLib frequency analysis.
424#[allow(dead_code)]
425pub struct MetaLibCounterMap {
426    pub counts: std::collections::HashMap<String, usize>,
427    pub total: usize,
428}
429#[allow(dead_code)]
430impl MetaLibCounterMap {
431    pub fn new() -> Self {
432        MetaLibCounterMap {
433            counts: std::collections::HashMap::new(),
434            total: 0,
435        }
436    }
437    pub fn increment(&mut self, key: &str) {
438        *self.counts.entry(key.to_string()).or_insert(0) += 1;
439        self.total += 1;
440    }
441    pub fn count(&self, key: &str) -> usize {
442        *self.counts.get(key).unwrap_or(&0)
443    }
444    pub fn frequency(&self, key: &str) -> f64 {
445        if self.total == 0 {
446            0.0
447        } else {
448            self.count(key) as f64 / self.total as f64
449        }
450    }
451    pub fn most_common(&self) -> Option<(&String, usize)> {
452        self.counts
453            .iter()
454            .max_by_key(|(_, &v)| v)
455            .map(|(k, &v)| (k, v))
456    }
457    pub fn num_unique(&self) -> usize {
458        self.counts.len()
459    }
460    pub fn is_empty(&self) -> bool {
461        self.counts.is_empty()
462    }
463}
464/// A sliding window accumulator for MetaLib.
465#[allow(dead_code)]
466pub struct MetaLibWindow {
467    pub buffer: std::collections::VecDeque<f64>,
468    pub capacity: usize,
469    pub running_sum: f64,
470}
471#[allow(dead_code)]
472impl MetaLibWindow {
473    pub fn new(capacity: usize) -> Self {
474        MetaLibWindow {
475            buffer: std::collections::VecDeque::new(),
476            capacity,
477            running_sum: 0.0,
478        }
479    }
480    pub fn push(&mut self, v: f64) {
481        if self.buffer.len() >= self.capacity {
482            if let Some(old) = self.buffer.pop_front() {
483                self.running_sum -= old;
484            }
485        }
486        self.buffer.push_back(v);
487        self.running_sum += v;
488    }
489    pub fn mean(&self) -> f64 {
490        if self.buffer.is_empty() {
491            0.0
492        } else {
493            self.running_sum / self.buffer.len() as f64
494        }
495    }
496    pub fn variance(&self) -> f64 {
497        if self.buffer.len() < 2 {
498            return 0.0;
499        }
500        let m = self.mean();
501        self.buffer.iter().map(|&x| (x - m).powi(2)).sum::<f64>() / self.buffer.len() as f64
502    }
503    pub fn std_dev(&self) -> f64 {
504        self.variance().sqrt()
505    }
506    pub fn len(&self) -> usize {
507        self.buffer.len()
508    }
509    pub fn is_full(&self) -> bool {
510        self.buffer.len() >= self.capacity
511    }
512    pub fn is_empty(&self) -> bool {
513        self.buffer.is_empty()
514    }
515}
516#[allow(dead_code)]
517pub struct LibExtConfig1300 {
518    pub values: std::collections::HashMap<String, LibExtConfigVal1300>,
519    pub read_only: bool,
520    pub name: String,
521}
522impl LibExtConfig1300 {
523    #[allow(dead_code)]
524    pub fn new() -> Self {
525        Self {
526            values: std::collections::HashMap::new(),
527            read_only: false,
528            name: String::new(),
529        }
530    }
531    #[allow(dead_code)]
532    pub fn named(name: &str) -> Self {
533        Self {
534            values: std::collections::HashMap::new(),
535            read_only: false,
536            name: name.to_string(),
537        }
538    }
539    #[allow(dead_code)]
540    pub fn set(&mut self, key: &str, value: LibExtConfigVal1300) -> bool {
541        if self.read_only {
542            return false;
543        }
544        self.values.insert(key.to_string(), value);
545        true
546    }
547    #[allow(dead_code)]
548    pub fn get(&self, key: &str) -> Option<&LibExtConfigVal1300> {
549        self.values.get(key)
550    }
551    #[allow(dead_code)]
552    pub fn get_bool(&self, key: &str) -> Option<bool> {
553        self.get(key)?.as_bool()
554    }
555    #[allow(dead_code)]
556    pub fn get_int(&self, key: &str) -> Option<i64> {
557        self.get(key)?.as_int()
558    }
559    #[allow(dead_code)]
560    pub fn get_str(&self, key: &str) -> Option<&str> {
561        self.get(key)?.as_str()
562    }
563    #[allow(dead_code)]
564    pub fn set_bool(&mut self, key: &str, v: bool) -> bool {
565        self.set(key, LibExtConfigVal1300::Bool(v))
566    }
567    #[allow(dead_code)]
568    pub fn set_int(&mut self, key: &str, v: i64) -> bool {
569        self.set(key, LibExtConfigVal1300::Int(v))
570    }
571    #[allow(dead_code)]
572    pub fn set_str(&mut self, key: &str, v: &str) -> bool {
573        self.set(key, LibExtConfigVal1300::Str(v.to_string()))
574    }
575    #[allow(dead_code)]
576    pub fn lock(&mut self) {
577        self.read_only = true;
578    }
579    #[allow(dead_code)]
580    pub fn unlock(&mut self) {
581        self.read_only = false;
582    }
583    #[allow(dead_code)]
584    pub fn size(&self) -> usize {
585        self.values.len()
586    }
587    #[allow(dead_code)]
588    pub fn has(&self, key: &str) -> bool {
589        self.values.contains_key(key)
590    }
591    #[allow(dead_code)]
592    pub fn remove(&mut self, key: &str) -> bool {
593        self.values.remove(key).is_some()
594    }
595}
596/// A work queue for MetaLib items.
597#[allow(dead_code)]
598pub struct MetaLibWorkQueue {
599    pub pending: std::collections::VecDeque<String>,
600    pub processed: Vec<String>,
601    pub capacity: usize,
602}
603#[allow(dead_code)]
604impl MetaLibWorkQueue {
605    pub fn new(capacity: usize) -> Self {
606        MetaLibWorkQueue {
607            pending: std::collections::VecDeque::new(),
608            processed: Vec::new(),
609            capacity,
610        }
611    }
612    pub fn enqueue(&mut self, item: String) -> bool {
613        if self.pending.len() >= self.capacity {
614            return false;
615        }
616        self.pending.push_back(item);
617        true
618    }
619    pub fn dequeue(&mut self) -> Option<String> {
620        let item = self.pending.pop_front()?;
621        self.processed.push(item.clone());
622        Some(item)
623    }
624    pub fn pending_count(&self) -> usize {
625        self.pending.len()
626    }
627    pub fn processed_count(&self) -> usize {
628        self.processed.len()
629    }
630    pub fn is_empty(&self) -> bool {
631        self.pending.is_empty()
632    }
633    pub fn is_full(&self) -> bool {
634        self.pending.len() >= self.capacity
635    }
636    pub fn total_processed(&self) -> usize {
637        self.processed.len()
638    }
639}
640#[allow(dead_code)]
641#[derive(Debug, Clone)]
642pub enum LibExtConfigVal1300 {
643    Bool(bool),
644    Int(i64),
645    Float(f64),
646    Str(String),
647    List(Vec<String>),
648}
649impl LibExtConfigVal1300 {
650    #[allow(dead_code)]
651    pub fn as_bool(&self) -> Option<bool> {
652        if let LibExtConfigVal1300::Bool(b) = self {
653            Some(*b)
654        } else {
655            None
656        }
657    }
658    #[allow(dead_code)]
659    pub fn as_int(&self) -> Option<i64> {
660        if let LibExtConfigVal1300::Int(i) = self {
661            Some(*i)
662        } else {
663            None
664        }
665    }
666    #[allow(dead_code)]
667    pub fn as_float(&self) -> Option<f64> {
668        if let LibExtConfigVal1300::Float(f) = self {
669            Some(*f)
670        } else {
671            None
672        }
673    }
674    #[allow(dead_code)]
675    pub fn as_str(&self) -> Option<&str> {
676        if let LibExtConfigVal1300::Str(s) = self {
677            Some(s)
678        } else {
679            None
680        }
681    }
682    #[allow(dead_code)]
683    pub fn as_list(&self) -> Option<&[String]> {
684        if let LibExtConfigVal1300::List(l) = self {
685            Some(l)
686        } else {
687            None
688        }
689    }
690    #[allow(dead_code)]
691    pub fn type_name(&self) -> &'static str {
692        match self {
693            LibExtConfigVal1300::Bool(_) => "bool",
694            LibExtConfigVal1300::Int(_) => "int",
695            LibExtConfigVal1300::Float(_) => "float",
696            LibExtConfigVal1300::Str(_) => "str",
697            LibExtConfigVal1300::List(_) => "list",
698        }
699    }
700}
701/// A typed slot for Lib configuration.
702#[allow(dead_code)]
703#[derive(Debug, Clone)]
704pub enum LibConfigValue {
705    Bool(bool),
706    Int(i64),
707    Float(f64),
708    Str(String),
709    List(Vec<String>),
710}
711#[allow(dead_code)]
712impl LibConfigValue {
713    pub fn as_bool(&self) -> Option<bool> {
714        match self {
715            LibConfigValue::Bool(b) => Some(*b),
716            _ => None,
717        }
718    }
719    pub fn as_int(&self) -> Option<i64> {
720        match self {
721            LibConfigValue::Int(i) => Some(*i),
722            _ => None,
723        }
724    }
725    pub fn as_float(&self) -> Option<f64> {
726        match self {
727            LibConfigValue::Float(f) => Some(*f),
728            _ => None,
729        }
730    }
731    pub fn as_str(&self) -> Option<&str> {
732        match self {
733            LibConfigValue::Str(s) => Some(s),
734            _ => None,
735        }
736    }
737    pub fn as_list(&self) -> Option<&[String]> {
738        match self {
739            LibConfigValue::List(v) => Some(v),
740            _ => None,
741        }
742    }
743    pub fn type_name(&self) -> &'static str {
744        match self {
745            LibConfigValue::Bool(_) => "bool",
746            LibConfigValue::Int(_) => "int",
747            LibConfigValue::Float(_) => "float",
748            LibConfigValue::Str(_) => "str",
749            LibConfigValue::List(_) => "list",
750        }
751    }
752}
753/// An extended utility type for MetaLib.
754#[allow(dead_code)]
755#[derive(Debug, Clone, Default)]
756pub struct MetaLibExtUtil {
757    pub key: String,
758    pub data: Vec<i64>,
759    pub active: bool,
760    pub flags: u32,
761}
762#[allow(dead_code)]
763impl MetaLibExtUtil {
764    pub fn new(key: &str) -> Self {
765        MetaLibExtUtil {
766            key: key.to_string(),
767            data: Vec::new(),
768            active: true,
769            flags: 0,
770        }
771    }
772    pub fn push(&mut self, v: i64) {
773        self.data.push(v);
774    }
775    pub fn pop(&mut self) -> Option<i64> {
776        self.data.pop()
777    }
778    pub fn sum(&self) -> i64 {
779        self.data.iter().sum()
780    }
781    pub fn min_val(&self) -> Option<i64> {
782        self.data.iter().copied().reduce(i64::min)
783    }
784    pub fn max_val(&self) -> Option<i64> {
785        self.data.iter().copied().reduce(i64::max)
786    }
787    pub fn len(&self) -> usize {
788        self.data.len()
789    }
790    pub fn is_empty(&self) -> bool {
791        self.data.is_empty()
792    }
793    pub fn clear(&mut self) {
794        self.data.clear();
795    }
796    pub fn set_flag(&mut self, bit: u32) {
797        self.flags |= 1 << bit;
798    }
799    pub fn has_flag(&self, bit: u32) -> bool {
800        self.flags & (1 << bit) != 0
801    }
802    pub fn deactivate(&mut self) {
803        self.active = false;
804    }
805    pub fn activate(&mut self) {
806        self.active = true;
807    }
808}
809/// Summary statistics about a `MetaContext`.
810#[derive(Debug, Clone, Default)]
811pub struct MetaStats {
812    /// Number of expression metavariables.
813    pub num_expr_mvars: usize,
814    /// Number of assigned expression metavariables.
815    pub num_assigned_expr: usize,
816    /// Number of level metavariables.
817    pub num_level_mvars: usize,
818    /// Number of assigned level metavariables.
819    pub num_assigned_levels: usize,
820    /// Number of postponed constraints.
821    pub num_postponed: usize,
822}
823/// An analysis pass for Lib.
824#[allow(dead_code)]
825pub struct LibAnalysisPass {
826    pub name: String,
827    pub enabled: bool,
828    pub results: Vec<LibResult>,
829    pub total_runs: usize,
830}
831#[allow(dead_code)]
832impl LibAnalysisPass {
833    pub fn new(name: &str) -> Self {
834        LibAnalysisPass {
835            name: name.to_string(),
836            enabled: true,
837            results: Vec::new(),
838            total_runs: 0,
839        }
840    }
841    pub fn run(&mut self, input: &str) -> LibResult {
842        self.total_runs += 1;
843        let result = if input.is_empty() {
844            LibResult::Err("empty input".to_string())
845        } else {
846            LibResult::Ok(format!("processed: {}", input))
847        };
848        self.results.push(result.clone());
849        result
850    }
851    pub fn success_count(&self) -> usize {
852        self.results.iter().filter(|r| r.is_ok()).count()
853    }
854    pub fn error_count(&self) -> usize {
855        self.results.iter().filter(|r| r.is_err()).count()
856    }
857    pub fn success_rate(&self) -> f64 {
858        if self.total_runs == 0 {
859            0.0
860        } else {
861            self.success_count() as f64 / self.total_runs as f64
862        }
863    }
864    pub fn disable(&mut self) {
865        self.enabled = false;
866    }
867    pub fn enable(&mut self) {
868        self.enabled = true;
869    }
870    pub fn clear_results(&mut self) {
871        self.results.clear();
872    }
873}
874/// Simple accumulator for meta-layer performance statistics.
875#[allow(dead_code)]
876pub struct PerfStats {
877    /// Total number of elaboration attempts.
878    pub elab_attempts: u64,
879    /// Number of successful elaborations.
880    pub elab_successes: u64,
881    /// Total unification attempts.
882    pub unif_attempts: u64,
883    /// Number of successful unifications.
884    pub unif_successes: u64,
885    /// Total elapsed time in microseconds.
886    pub elapsed_us: u64,
887}
888#[allow(dead_code)]
889impl PerfStats {
890    /// Create an empty stats record.
891    pub fn new() -> Self {
892        PerfStats {
893            elab_attempts: 0,
894            elab_successes: 0,
895            unif_attempts: 0,
896            unif_successes: 0,
897            elapsed_us: 0,
898        }
899    }
900    /// Return the elaboration success rate as a fraction in [0, 1].
901    pub fn elab_success_rate(&self) -> f64 {
902        if self.elab_attempts == 0 {
903            return 0.0;
904        }
905        self.elab_successes as f64 / self.elab_attempts as f64
906    }
907    /// Return the unification success rate as a fraction in [0, 1].
908    pub fn unif_success_rate(&self) -> f64 {
909        if self.unif_attempts == 0 {
910            return 0.0;
911        }
912        self.unif_successes as f64 / self.unif_attempts as f64
913    }
914    /// Merge another `PerfStats` into this one.
915    pub fn merge(&mut self, other: &PerfStats) {
916        self.elab_attempts += other.elab_attempts;
917        self.elab_successes += other.elab_successes;
918        self.unif_attempts += other.unif_attempts;
919        self.unif_successes += other.unif_successes;
920        self.elapsed_us += other.elapsed_us;
921    }
922}
923impl Default for PerfStats {
924    fn default() -> Self {
925        Self::new()
926    }
927}
928/// A named tactic registry.
929#[derive(Debug, Default)]
930pub struct TacticRegistry {
931    pub entries: std::collections::HashMap<String, usize>,
932    pub names: Vec<String>,
933}
934impl TacticRegistry {
935    /// Create a new empty registry.
936    pub fn new() -> Self {
937        Self::default()
938    }
939    /// Register a tactic name.
940    pub fn register(&mut self, name: impl Into<String>) -> usize {
941        let name = name.into();
942        if let Some(&idx) = self.entries.get(&name) {
943            return idx;
944        }
945        let idx = self.names.len();
946        self.entries.insert(name.clone(), idx);
947        self.names.push(name);
948        idx
949    }
950    /// Look up a tactic index by name.
951    pub fn lookup(&self, name: &str) -> Option<usize> {
952        self.entries.get(name).copied()
953    }
954    /// Get the name for an index.
955    pub fn name_of(&self, idx: usize) -> Option<&str> {
956        self.names.get(idx).map(String::as_str)
957    }
958    /// Number of registered tactics.
959    pub fn len(&self) -> usize {
960        self.names.len()
961    }
962    /// Whether the registry is empty.
963    pub fn is_empty(&self) -> bool {
964        self.names.is_empty()
965    }
966    /// Get all registered names.
967    pub fn all_names(&self) -> &[String] {
968        &self.names
969    }
970}
971/// A state machine for MetaLib.
972#[allow(dead_code)]
973#[derive(Debug, Clone, PartialEq)]
974pub enum MetaLibState {
975    Initial,
976    Running,
977    Paused,
978    Complete,
979    Failed(String),
980}
981#[allow(dead_code)]
982impl MetaLibState {
983    pub fn is_terminal(&self) -> bool {
984        matches!(self, MetaLibState::Complete | MetaLibState::Failed(_))
985    }
986    pub fn can_run(&self) -> bool {
987        matches!(self, MetaLibState::Initial | MetaLibState::Paused)
988    }
989    pub fn is_running(&self) -> bool {
990        matches!(self, MetaLibState::Running)
991    }
992    pub fn error_msg(&self) -> Option<&str> {
993        match self {
994            MetaLibState::Failed(s) => Some(s),
995            _ => None,
996        }
997    }
998}
999/// A diff for Lib analysis results.
1000#[allow(dead_code)]
1001#[derive(Debug, Clone)]
1002pub struct LibDiff {
1003    pub added: Vec<String>,
1004    pub removed: Vec<String>,
1005    pub unchanged: Vec<String>,
1006}
1007#[allow(dead_code)]
1008impl LibDiff {
1009    pub fn new() -> Self {
1010        LibDiff {
1011            added: Vec::new(),
1012            removed: Vec::new(),
1013            unchanged: Vec::new(),
1014        }
1015    }
1016    pub fn add(&mut self, s: &str) {
1017        self.added.push(s.to_string());
1018    }
1019    pub fn remove(&mut self, s: &str) {
1020        self.removed.push(s.to_string());
1021    }
1022    pub fn keep(&mut self, s: &str) {
1023        self.unchanged.push(s.to_string());
1024    }
1025    pub fn is_empty(&self) -> bool {
1026        self.added.is_empty() && self.removed.is_empty()
1027    }
1028    pub fn total_changes(&self) -> usize {
1029        self.added.len() + self.removed.len()
1030    }
1031    pub fn net_additions(&self) -> i64 {
1032        self.added.len() as i64 - self.removed.len() as i64
1033    }
1034    pub fn summary(&self) -> String {
1035        format!(
1036            "+{} -{} =={}",
1037            self.added.len(),
1038            self.removed.len(),
1039            self.unchanged.len()
1040        )
1041    }
1042}
1043/// A configuration store for Lib.
1044#[allow(dead_code)]
1045pub struct LibConfig {
1046    pub values: std::collections::HashMap<String, LibConfigValue>,
1047    pub read_only: bool,
1048}
1049#[allow(dead_code)]
1050impl LibConfig {
1051    pub fn new() -> Self {
1052        LibConfig {
1053            values: std::collections::HashMap::new(),
1054            read_only: false,
1055        }
1056    }
1057    pub fn set(&mut self, key: &str, value: LibConfigValue) -> bool {
1058        if self.read_only {
1059            return false;
1060        }
1061        self.values.insert(key.to_string(), value);
1062        true
1063    }
1064    pub fn get(&self, key: &str) -> Option<&LibConfigValue> {
1065        self.values.get(key)
1066    }
1067    pub fn get_bool(&self, key: &str) -> Option<bool> {
1068        self.get(key)?.as_bool()
1069    }
1070    pub fn get_int(&self, key: &str) -> Option<i64> {
1071        self.get(key)?.as_int()
1072    }
1073    pub fn get_str(&self, key: &str) -> Option<&str> {
1074        self.get(key)?.as_str()
1075    }
1076    pub fn set_bool(&mut self, key: &str, v: bool) -> bool {
1077        self.set(key, LibConfigValue::Bool(v))
1078    }
1079    pub fn set_int(&mut self, key: &str, v: i64) -> bool {
1080        self.set(key, LibConfigValue::Int(v))
1081    }
1082    pub fn set_str(&mut self, key: &str, v: &str) -> bool {
1083        self.set(key, LibConfigValue::Str(v.to_string()))
1084    }
1085    pub fn lock(&mut self) {
1086        self.read_only = true;
1087    }
1088    pub fn unlock(&mut self) {
1089        self.read_only = false;
1090    }
1091    pub fn size(&self) -> usize {
1092        self.values.len()
1093    }
1094    pub fn has(&self, key: &str) -> bool {
1095        self.values.contains_key(key)
1096    }
1097    pub fn remove(&mut self, key: &str) -> bool {
1098        self.values.remove(key).is_some()
1099    }
1100}
1101#[allow(dead_code)]
1102pub struct LibExtPass1300 {
1103    pub name: String,
1104    pub total_runs: usize,
1105    pub successes: usize,
1106    pub errors: usize,
1107    pub enabled: bool,
1108    pub results: Vec<LibExtResult1300>,
1109}
1110impl LibExtPass1300 {
1111    #[allow(dead_code)]
1112    pub fn new(name: &str) -> Self {
1113        Self {
1114            name: name.to_string(),
1115            total_runs: 0,
1116            successes: 0,
1117            errors: 0,
1118            enabled: true,
1119            results: Vec::new(),
1120        }
1121    }
1122    #[allow(dead_code)]
1123    pub fn run(&mut self, input: &str) -> LibExtResult1300 {
1124        if !self.enabled {
1125            return LibExtResult1300::Skipped;
1126        }
1127        self.total_runs += 1;
1128        let result = if input.is_empty() {
1129            self.errors += 1;
1130            LibExtResult1300::Err(format!("empty input in pass '{}'", self.name))
1131        } else {
1132            self.successes += 1;
1133            LibExtResult1300::Ok(format!(
1134                "processed {} chars in pass '{}'",
1135                input.len(),
1136                self.name
1137            ))
1138        };
1139        self.results.push(result.clone());
1140        result
1141    }
1142    #[allow(dead_code)]
1143    pub fn success_count(&self) -> usize {
1144        self.successes
1145    }
1146    #[allow(dead_code)]
1147    pub fn error_count(&self) -> usize {
1148        self.errors
1149    }
1150    #[allow(dead_code)]
1151    pub fn success_rate(&self) -> f64 {
1152        if self.total_runs == 0 {
1153            0.0
1154        } else {
1155            self.successes as f64 / self.total_runs as f64
1156        }
1157    }
1158    #[allow(dead_code)]
1159    pub fn disable(&mut self) {
1160        self.enabled = false;
1161    }
1162    #[allow(dead_code)]
1163    pub fn enable(&mut self) {
1164        self.enabled = true;
1165    }
1166    #[allow(dead_code)]
1167    pub fn clear_results(&mut self) {
1168        self.results.clear();
1169    }
1170}
1171#[allow(dead_code)]
1172pub struct LibExtPipeline1300 {
1173    pub name: String,
1174    pub passes: Vec<LibExtPass1300>,
1175    pub run_count: usize,
1176}
1177impl LibExtPipeline1300 {
1178    #[allow(dead_code)]
1179    pub fn new(name: &str) -> Self {
1180        Self {
1181            name: name.to_string(),
1182            passes: Vec::new(),
1183            run_count: 0,
1184        }
1185    }
1186    #[allow(dead_code)]
1187    pub fn add_pass(&mut self, pass: LibExtPass1300) {
1188        self.passes.push(pass);
1189    }
1190    #[allow(dead_code)]
1191    pub fn run_all(&mut self, input: &str) -> Vec<LibExtResult1300> {
1192        self.run_count += 1;
1193        self.passes
1194            .iter_mut()
1195            .filter(|p| p.enabled)
1196            .map(|p| p.run(input))
1197            .collect()
1198    }
1199    #[allow(dead_code)]
1200    pub fn num_passes(&self) -> usize {
1201        self.passes.len()
1202    }
1203    #[allow(dead_code)]
1204    pub fn num_enabled_passes(&self) -> usize {
1205        self.passes.iter().filter(|p| p.enabled).count()
1206    }
1207    #[allow(dead_code)]
1208    pub fn total_success_rate(&self) -> f64 {
1209        let total: usize = self.passes.iter().map(|p| p.total_runs).sum();
1210        let ok: usize = self.passes.iter().map(|p| p.successes).sum();
1211        if total == 0 {
1212            0.0
1213        } else {
1214            ok as f64 / total as f64
1215        }
1216    }
1217}
1218/// Feature flags for the meta layer.
1219#[derive(Clone, Debug)]
1220pub struct MetaFeatures {
1221    /// Enable discrimination tree indexing for simp lemmas.
1222    pub discr_tree: bool,
1223    /// Enable memoization of WHNF results.
1224    pub whnf_cache: bool,
1225    /// Enable proof term recording.
1226    pub proof_recording: bool,
1227    /// Enable instance synthesis.
1228    pub instance_synth: bool,
1229    /// Enable congr-lemma automation.
1230    pub congr_lemmas: bool,
1231}
1232impl MetaFeatures {
1233    /// All features enabled.
1234    pub fn all_enabled() -> Self {
1235        Self {
1236            discr_tree: true,
1237            whnf_cache: true,
1238            proof_recording: true,
1239            instance_synth: true,
1240            congr_lemmas: true,
1241        }
1242    }
1243    /// Minimal features (fast, less complete).
1244    pub fn minimal() -> Self {
1245        Self {
1246            discr_tree: false,
1247            whnf_cache: false,
1248            proof_recording: false,
1249            instance_synth: false,
1250            congr_lemmas: false,
1251        }
1252    }
1253    /// Whether at least one caching feature is enabled.
1254    pub fn any_caching(&self) -> bool {
1255        self.whnf_cache || self.proof_recording
1256    }
1257}
1258impl Default for MetaFeatures {
1259    fn default() -> Self {
1260        Self {
1261            discr_tree: true,
1262            whnf_cache: true,
1263            proof_recording: false,
1264            instance_synth: true,
1265            congr_lemmas: true,
1266        }
1267    }
1268}
1269/// A diagnostic reporter for Lib.
1270#[allow(dead_code)]
1271pub struct LibDiagnostics {
1272    pub errors: Vec<String>,
1273    pub warnings: Vec<String>,
1274    pub notes: Vec<String>,
1275    pub max_errors: usize,
1276}
1277#[allow(dead_code)]
1278impl LibDiagnostics {
1279    pub fn new(max_errors: usize) -> Self {
1280        LibDiagnostics {
1281            errors: Vec::new(),
1282            warnings: Vec::new(),
1283            notes: Vec::new(),
1284            max_errors,
1285        }
1286    }
1287    pub fn error(&mut self, msg: &str) {
1288        if self.errors.len() < self.max_errors {
1289            self.errors.push(msg.to_string());
1290        }
1291    }
1292    pub fn warning(&mut self, msg: &str) {
1293        self.warnings.push(msg.to_string());
1294    }
1295    pub fn note(&mut self, msg: &str) {
1296        self.notes.push(msg.to_string());
1297    }
1298    pub fn has_errors(&self) -> bool {
1299        !self.errors.is_empty()
1300    }
1301    pub fn num_errors(&self) -> usize {
1302        self.errors.len()
1303    }
1304    pub fn num_warnings(&self) -> usize {
1305        self.warnings.len()
1306    }
1307    pub fn is_clean(&self) -> bool {
1308        self.errors.is_empty() && self.warnings.is_empty()
1309    }
1310    pub fn at_error_limit(&self) -> bool {
1311        self.errors.len() >= self.max_errors
1312    }
1313    pub fn clear(&mut self) {
1314        self.errors.clear();
1315        self.warnings.clear();
1316        self.notes.clear();
1317    }
1318    pub fn summary(&self) -> String {
1319        format!(
1320            "{} error(s), {} warning(s)",
1321            self.errors.len(),
1322            self.warnings.len()
1323        )
1324    }
1325}
1326#[allow(dead_code)]
1327pub struct LibExtDiag1300 {
1328    pub errors: Vec<String>,
1329    pub warnings: Vec<String>,
1330    pub notes: Vec<String>,
1331    pub max_errors: usize,
1332}
1333impl LibExtDiag1300 {
1334    #[allow(dead_code)]
1335    pub fn new(max_errors: usize) -> Self {
1336        Self {
1337            errors: Vec::new(),
1338            warnings: Vec::new(),
1339            notes: Vec::new(),
1340            max_errors,
1341        }
1342    }
1343    #[allow(dead_code)]
1344    pub fn error(&mut self, msg: &str) {
1345        if self.errors.len() < self.max_errors {
1346            self.errors.push(msg.to_string());
1347        }
1348    }
1349    #[allow(dead_code)]
1350    pub fn warning(&mut self, msg: &str) {
1351        self.warnings.push(msg.to_string());
1352    }
1353    #[allow(dead_code)]
1354    pub fn note(&mut self, msg: &str) {
1355        self.notes.push(msg.to_string());
1356    }
1357    #[allow(dead_code)]
1358    pub fn has_errors(&self) -> bool {
1359        !self.errors.is_empty()
1360    }
1361    #[allow(dead_code)]
1362    pub fn num_errors(&self) -> usize {
1363        self.errors.len()
1364    }
1365    #[allow(dead_code)]
1366    pub fn num_warnings(&self) -> usize {
1367        self.warnings.len()
1368    }
1369    #[allow(dead_code)]
1370    pub fn is_clean(&self) -> bool {
1371        self.errors.is_empty() && self.warnings.is_empty()
1372    }
1373    #[allow(dead_code)]
1374    pub fn at_error_limit(&self) -> bool {
1375        self.errors.len() >= self.max_errors
1376    }
1377    #[allow(dead_code)]
1378    pub fn clear(&mut self) {
1379        self.errors.clear();
1380        self.warnings.clear();
1381        self.notes.clear();
1382    }
1383    #[allow(dead_code)]
1384    pub fn summary(&self) -> String {
1385        format!(
1386            "{} error(s), {} warning(s)",
1387            self.errors.len(),
1388            self.warnings.len()
1389        )
1390    }
1391}
1392/// A state machine controller for MetaLib.
1393#[allow(dead_code)]
1394pub struct MetaLibStateMachine {
1395    pub state: MetaLibState,
1396    pub transitions: usize,
1397    pub history: Vec<String>,
1398}
1399#[allow(dead_code)]
1400impl MetaLibStateMachine {
1401    pub fn new() -> Self {
1402        MetaLibStateMachine {
1403            state: MetaLibState::Initial,
1404            transitions: 0,
1405            history: Vec::new(),
1406        }
1407    }
1408    pub fn transition_to(&mut self, new_state: MetaLibState) -> bool {
1409        if self.state.is_terminal() {
1410            return false;
1411        }
1412        let desc = format!("{:?} -> {:?}", self.state, new_state);
1413        self.state = new_state;
1414        self.transitions += 1;
1415        self.history.push(desc);
1416        true
1417    }
1418    pub fn start(&mut self) -> bool {
1419        self.transition_to(MetaLibState::Running)
1420    }
1421    pub fn pause(&mut self) -> bool {
1422        self.transition_to(MetaLibState::Paused)
1423    }
1424    pub fn complete(&mut self) -> bool {
1425        self.transition_to(MetaLibState::Complete)
1426    }
1427    pub fn fail(&mut self, msg: &str) -> bool {
1428        self.transition_to(MetaLibState::Failed(msg.to_string()))
1429    }
1430    pub fn num_transitions(&self) -> usize {
1431        self.transitions
1432    }
1433}
1434
1435// ── Default implementations ───────────────────────────────────────────────────
1436
1437impl Default for LibExtDiff1300 {
1438    fn default() -> Self {
1439        Self::new()
1440    }
1441}
1442
1443impl<V: Clone + Default> Default for MetaLibExtMap<V> {
1444    fn default() -> Self {
1445        Self::new()
1446    }
1447}
1448
1449impl Default for MetaLibCounterMap {
1450    fn default() -> Self {
1451        Self::new()
1452    }
1453}
1454
1455impl Default for LibExtConfig1300 {
1456    fn default() -> Self {
1457        Self::new()
1458    }
1459}
1460
1461impl Default for LibDiff {
1462    fn default() -> Self {
1463        Self::new()
1464    }
1465}
1466
1467impl Default for LibConfig {
1468    fn default() -> Self {
1469        Self::new()
1470    }
1471}
1472
1473impl Default for MetaLibStateMachine {
1474    fn default() -> Self {
1475        Self::new()
1476    }
1477}