Skip to main content

oxilean_meta/basic/
types.rs

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