Skip to main content

oxilean_std/
registry.rs

1//! Standard library module registry, dependency graph, feature flags,
2//! and metadata types.
3
4#![allow(dead_code)]
5#![allow(missing_docs)]
6
7// ============================================================================
8// Environment Builder Registry & Orchestration
9// ============================================================================
10
11/// Represents a phase in the standard library build process.
12#[allow(dead_code)]
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum BuildPhase {
15    /// Phase 1: Primitive types (Nat, Bool, Char, etc.)
16    Primitives,
17    /// Phase 2: Collection types (List, Array, Set, etc.)
18    Collections,
19    /// Phase 3: Type class definitions (Eq, Ord, Show, etc.)
20    TypeClasses,
21    /// Phase 4: Core theorems and lemmas.
22    Theorems,
23    /// Phase 5: Automation (tactic lemmas, decision procedures).
24    Automation,
25}
26
27impl BuildPhase {
28    /// Returns all phases in build order.
29    #[allow(dead_code)]
30    pub fn all_in_order() -> &'static [BuildPhase] {
31        &[
32            BuildPhase::Primitives,
33            BuildPhase::Collections,
34            BuildPhase::TypeClasses,
35            BuildPhase::Theorems,
36            BuildPhase::Automation,
37        ]
38    }
39
40    /// Returns a human-readable name for this phase.
41    #[allow(dead_code)]
42    pub fn name(&self) -> &'static str {
43        match self {
44            BuildPhase::Primitives => "primitives",
45            BuildPhase::Collections => "collections",
46            BuildPhase::TypeClasses => "type_classes",
47            BuildPhase::Theorems => "theorems",
48            BuildPhase::Automation => "automation",
49        }
50    }
51}
52
53/// A registry entry describing one standard library module.
54#[allow(dead_code)]
55#[derive(Debug, Clone)]
56pub struct StdModuleEntry {
57    /// Fully qualified module name.
58    pub qualified_name: &'static str,
59    /// Build phase this module belongs to.
60    pub phase: BuildPhase,
61    /// Whether this module is loaded by default.
62    pub default_load: bool,
63    /// Brief description of module purpose.
64    pub description: &'static str,
65}
66
67/// Inventory of all standard library modules.
68#[allow(dead_code)]
69pub static STD_MODULE_REGISTRY: &[StdModuleEntry] = &[
70    StdModuleEntry {
71        qualified_name: "Std.Nat",
72        phase: BuildPhase::Primitives,
73        default_load: true,
74        description: "Natural number type",
75    },
76    StdModuleEntry {
77        qualified_name: "Std.Bool",
78        phase: BuildPhase::Primitives,
79        default_load: true,
80        description: "Boolean type",
81    },
82    StdModuleEntry {
83        qualified_name: "Std.Char",
84        phase: BuildPhase::Primitives,
85        default_load: true,
86        description: "Unicode character type",
87    },
88    StdModuleEntry {
89        qualified_name: "Std.Int",
90        phase: BuildPhase::Primitives,
91        default_load: true,
92        description: "Signed integer type",
93    },
94    StdModuleEntry {
95        qualified_name: "Std.String",
96        phase: BuildPhase::Primitives,
97        default_load: true,
98        description: "Immutable string type",
99    },
100    StdModuleEntry {
101        qualified_name: "Std.List",
102        phase: BuildPhase::Collections,
103        default_load: true,
104        description: "Linked list type",
105    },
106    StdModuleEntry {
107        qualified_name: "Std.Array",
108        phase: BuildPhase::Collections,
109        default_load: true,
110        description: "Fixed-size arrays",
111    },
112    StdModuleEntry {
113        qualified_name: "Std.Set",
114        phase: BuildPhase::Collections,
115        default_load: false,
116        description: "Mathematical sets",
117    },
118    StdModuleEntry {
119        qualified_name: "Std.Fin",
120        phase: BuildPhase::Collections,
121        default_load: true,
122        description: "Finite sets",
123    },
124    StdModuleEntry {
125        qualified_name: "Std.Vec",
126        phase: BuildPhase::Collections,
127        default_load: false,
128        description: "Length-indexed vectors",
129    },
130    StdModuleEntry {
131        qualified_name: "Std.Option",
132        phase: BuildPhase::Collections,
133        default_load: true,
134        description: "Optional value type",
135    },
136    StdModuleEntry {
137        qualified_name: "Std.Result",
138        phase: BuildPhase::Collections,
139        default_load: true,
140        description: "Result type",
141    },
142    StdModuleEntry {
143        qualified_name: "Std.Prod",
144        phase: BuildPhase::Collections,
145        default_load: true,
146        description: "Product type",
147    },
148    StdModuleEntry {
149        qualified_name: "Std.Sum",
150        phase: BuildPhase::Collections,
151        default_load: true,
152        description: "Sum type",
153    },
154    StdModuleEntry {
155        qualified_name: "Std.Sigma",
156        phase: BuildPhase::Collections,
157        default_load: false,
158        description: "Dependent pair type",
159    },
160    StdModuleEntry {
161        qualified_name: "Std.Eq",
162        phase: BuildPhase::TypeClasses,
163        default_load: true,
164        description: "Equality type class",
165    },
166    StdModuleEntry {
167        qualified_name: "Std.Ord",
168        phase: BuildPhase::TypeClasses,
169        default_load: true,
170        description: "Total ordering type class",
171    },
172    StdModuleEntry {
173        qualified_name: "Std.Show",
174        phase: BuildPhase::TypeClasses,
175        default_load: false,
176        description: "String representation",
177    },
178    StdModuleEntry {
179        qualified_name: "Std.Functor",
180        phase: BuildPhase::TypeClasses,
181        default_load: true,
182        description: "Functor type class",
183    },
184    StdModuleEntry {
185        qualified_name: "Std.Monad",
186        phase: BuildPhase::TypeClasses,
187        default_load: false,
188        description: "Monad type class",
189    },
190    StdModuleEntry {
191        qualified_name: "Std.Decidable",
192        phase: BuildPhase::TypeClasses,
193        default_load: true,
194        description: "Decidable propositions",
195    },
196    StdModuleEntry {
197        qualified_name: "Std.Algebra",
198        phase: BuildPhase::TypeClasses,
199        default_load: false,
200        description: "Algebraic structures",
201    },
202    StdModuleEntry {
203        qualified_name: "Std.Logic",
204        phase: BuildPhase::Theorems,
205        default_load: true,
206        description: "Classical logic",
207    },
208    StdModuleEntry {
209        qualified_name: "Std.Prop",
210        phase: BuildPhase::Theorems,
211        default_load: true,
212        description: "Propositional theorems",
213    },
214    StdModuleEntry {
215        qualified_name: "Std.Order",
216        phase: BuildPhase::Theorems,
217        default_load: false,
218        description: "Order theorems",
219    },
220    StdModuleEntry {
221        qualified_name: "Std.TacticLemmas",
222        phase: BuildPhase::Automation,
223        default_load: true,
224        description: "Tactic lemmas",
225    },
226    StdModuleEntry {
227        qualified_name: "Std.WellFounded",
228        phase: BuildPhase::Automation,
229        default_load: false,
230        description: "Well-founded relations",
231    },
232];
233
234/// Get all module entries for a specific build phase.
235#[allow(dead_code)]
236pub fn modules_for_phase(phase: BuildPhase) -> Vec<&'static StdModuleEntry> {
237    STD_MODULE_REGISTRY
238        .iter()
239        .filter(|e| e.phase == phase)
240        .collect()
241}
242
243/// Get all default-loaded modules.
244#[allow(dead_code)]
245pub fn default_modules() -> Vec<&'static StdModuleEntry> {
246    STD_MODULE_REGISTRY
247        .iter()
248        .filter(|e| e.default_load)
249        .collect()
250}
251
252/// Get all modules.
253#[allow(dead_code)]
254pub fn all_modules() -> &'static [StdModuleEntry] {
255    STD_MODULE_REGISTRY
256}
257
258/// Count how many modules are loaded by default.
259#[allow(dead_code)]
260pub fn count_default_modules() -> usize {
261    STD_MODULE_REGISTRY
262        .iter()
263        .filter(|e| e.default_load)
264        .count()
265}
266
267/// Count total number of registered standard library modules.
268#[allow(dead_code)]
269pub fn count_total_modules() -> usize {
270    STD_MODULE_REGISTRY.len()
271}
272
273/// Look up a module entry by its qualified name.
274#[allow(dead_code)]
275pub fn find_module(qualified_name: &str) -> Option<&'static StdModuleEntry> {
276    STD_MODULE_REGISTRY
277        .iter()
278        .find(|e| e.qualified_name == qualified_name)
279}
280
281/// A dependency pair: (dependent, dependency).
282#[allow(dead_code)]
283#[derive(Debug, Clone, Copy)]
284pub struct ModuleDep {
285    /// The module that depends on another.
286    pub dependent: &'static str,
287    /// The module that must be built first.
288    pub dependency: &'static str,
289}
290
291/// Minimal dependency graph for core standard library modules.
292#[allow(dead_code)]
293pub static CORE_DEPS: &[ModuleDep] = &[
294    ModuleDep {
295        dependent: "Std.List",
296        dependency: "Std.Nat",
297    },
298    ModuleDep {
299        dependent: "Std.Array",
300        dependency: "Std.Nat",
301    },
302    ModuleDep {
303        dependent: "Std.Fin",
304        dependency: "Std.Nat",
305    },
306    ModuleDep {
307        dependent: "Std.Vec",
308        dependency: "Std.List",
309    },
310    ModuleDep {
311        dependent: "Std.Set",
312        dependency: "Std.List",
313    },
314    ModuleDep {
315        dependent: "Std.Ord",
316        dependency: "Std.Eq",
317    },
318    ModuleDep {
319        dependent: "Std.Functor",
320        dependency: "Std.Eq",
321    },
322    ModuleDep {
323        dependent: "Std.Monad",
324        dependency: "Std.Functor",
325    },
326    ModuleDep {
327        dependent: "Std.Algebra",
328        dependency: "Std.Eq",
329    },
330    ModuleDep {
331        dependent: "Std.Logic",
332        dependency: "Std.Bool",
333    },
334    ModuleDep {
335        dependent: "Std.Prop",
336        dependency: "Std.Logic",
337    },
338    ModuleDep {
339        dependent: "Std.Order",
340        dependency: "Std.Ord",
341    },
342    ModuleDep {
343        dependent: "Std.TacticLemmas",
344        dependency: "Std.Nat",
345    },
346    ModuleDep {
347        dependent: "Std.WellFounded",
348        dependency: "Std.Nat",
349    },
350];
351
352/// Get all dependencies of a named module (direct only).
353#[allow(dead_code)]
354pub fn direct_deps(module: &str) -> Vec<&'static str> {
355    CORE_DEPS
356        .iter()
357        .filter(|d| d.dependent == module)
358        .map(|d| d.dependency)
359        .collect()
360}
361
362/// Get all modules that depend on the given module.
363#[allow(dead_code)]
364pub fn dependents_of(module: &str) -> Vec<&'static str> {
365    CORE_DEPS
366        .iter()
367        .filter(|d| d.dependency == module)
368        .map(|d| d.dependent)
369        .collect()
370}
371
372/// Compute a topological ordering of modules based on `CORE_DEPS`.
373///
374/// Returns `None` if there is a cycle.
375#[allow(dead_code)]
376pub fn topological_sort_modules() -> Option<Vec<&'static str>> {
377    let mut in_degree: std::collections::HashMap<&'static str, usize> =
378        std::collections::HashMap::new();
379    let mut adjacency: std::collections::HashMap<&'static str, Vec<&'static str>> =
380        std::collections::HashMap::new();
381
382    // Initialize all modules
383    for entry in STD_MODULE_REGISTRY {
384        in_degree.entry(entry.qualified_name).or_insert(0);
385        adjacency.entry(entry.qualified_name).or_default();
386    }
387
388    // Build graph from dependencies
389    for dep in CORE_DEPS {
390        *in_degree.entry(dep.dependent).or_insert(0) += 1;
391        adjacency
392            .entry(dep.dependency)
393            .or_default()
394            .push(dep.dependent);
395    }
396
397    // Kahn's algorithm
398    let mut queue: std::collections::VecDeque<&'static str> = in_degree
399        .iter()
400        .filter(|(_, &d)| d == 0)
401        .map(|(&n, _)| n)
402        .collect();
403
404    let mut sorted = Vec::new();
405    while let Some(node) = queue.pop_front() {
406        sorted.push(node);
407        if let Some(neighbors) = adjacency.get(node) {
408            for &neighbor in neighbors {
409                let deg = in_degree.entry(neighbor).or_insert(0);
410                *deg -= 1;
411                if *deg == 0 {
412                    queue.push_back(neighbor);
413                }
414            }
415        }
416    }
417
418    if sorted.len() == in_degree.len() {
419        Some(sorted)
420    } else {
421        None // cycle detected
422    }
423}
424
425/// OxiLean standard library version string.
426#[allow(dead_code)]
427pub const STD_VERSION: &str = "0.1.1";
428
429/// Feature flags for optional standard library components.
430#[allow(dead_code)]
431#[derive(Debug, Clone, Default)]
432pub struct StdFeatures {
433    /// Enable classical logic axioms (excluded middle, choice).
434    pub classical: bool,
435    /// Enable propext (propositional extensionality).
436    pub propext: bool,
437    /// Enable funext (function extensionality).
438    pub funext: bool,
439    /// Enable quotient types.
440    pub quotient: bool,
441    /// Enable experimental category theory module.
442    pub category_theory: bool,
443    /// Enable topology module.
444    pub topology: bool,
445    /// Enable real number support.
446    pub reals: bool,
447}
448
449impl StdFeatures {
450    /// Create the default feature set (classical logic enabled by default).
451    #[allow(dead_code)]
452    pub fn default_features() -> Self {
453        Self {
454            classical: true,
455            propext: true,
456            funext: true,
457            quotient: false,
458            category_theory: false,
459            topology: false,
460            reals: false,
461        }
462    }
463
464    /// Create the full feature set.
465    #[allow(dead_code)]
466    pub fn full() -> Self {
467        Self {
468            classical: true,
469            propext: true,
470            funext: true,
471            quotient: true,
472            category_theory: true,
473            topology: true,
474            reals: true,
475        }
476    }
477
478    /// Count how many features are enabled.
479    #[allow(dead_code)]
480    pub fn count_enabled(&self) -> usize {
481        [
482            self.classical,
483            self.propext,
484            self.funext,
485            self.quotient,
486            self.category_theory,
487            self.topology,
488            self.reals,
489        ]
490        .iter()
491        .filter(|&&v| v)
492        .count()
493    }
494}
495
496/// Standard library module statistics.
497#[allow(dead_code)]
498#[derive(Debug, Clone, Default)]
499pub struct StdLibStats {
500    /// Total modules registered.
501    pub total_modules: usize,
502    /// Modules enabled by default.
503    pub default_modules: usize,
504    /// Modules per build phase.
505    pub per_phase: [usize; 5],
506}
507
508impl StdLibStats {
509    /// Compute statistics from the registry.
510    #[allow(dead_code)]
511    pub fn compute() -> Self {
512        let total = count_total_modules();
513        let defaults = count_default_modules();
514        let phases = [
515            modules_for_phase(BuildPhase::Primitives).len(),
516            modules_for_phase(BuildPhase::Collections).len(),
517            modules_for_phase(BuildPhase::TypeClasses).len(),
518            modules_for_phase(BuildPhase::Theorems).len(),
519            modules_for_phase(BuildPhase::Automation).len(),
520        ];
521        Self {
522            total_modules: total,
523            default_modules: defaults,
524            per_phase: phases,
525        }
526    }
527
528    /// Check if all phases have at least one module.
529    #[allow(dead_code)]
530    pub fn all_phases_populated(&self) -> bool {
531        self.per_phase.iter().all(|&n| n > 0)
532    }
533
534    /// Get total modules across all phases.
535    #[allow(dead_code)]
536    pub fn phase_total(&self) -> usize {
537        self.per_phase.iter().sum()
538    }
539}
540
541/// A record of a single OxiLean standard library theorem or definition
542/// that the elaborator knows about.
543#[allow(dead_code)]
544#[derive(Debug, Clone)]
545pub struct StdEntry {
546    /// Qualified name (e.g., `Nat.add_comm`).
547    pub name: &'static str,
548    /// Which module this entry belongs to.
549    pub module: &'static str,
550    /// Human-readable description.
551    pub description: &'static str,
552    /// Whether this is a theorem (vs. a definition).
553    pub is_theorem: bool,
554}
555
556/// A representative sample of well-known standard library entries.
557#[allow(dead_code)]
558pub const STD_KNOWN_ENTRIES: &[StdEntry] = &[
559    StdEntry {
560        name: "Nat.zero_add",
561        module: "Std.Nat",
562        description: "0 + n = n",
563        is_theorem: true,
564    },
565    StdEntry {
566        name: "Nat.add_zero",
567        module: "Std.Nat",
568        description: "n + 0 = n",
569        is_theorem: true,
570    },
571    StdEntry {
572        name: "Nat.add_comm",
573        module: "Std.Nat",
574        description: "Commutativity of natural number addition",
575        is_theorem: true,
576    },
577    StdEntry {
578        name: "Nat.add_assoc",
579        module: "Std.Nat",
580        description: "Associativity of natural number addition",
581        is_theorem: true,
582    },
583    StdEntry {
584        name: "Nat.mul_comm",
585        module: "Std.Nat",
586        description: "Commutativity of natural number multiplication",
587        is_theorem: true,
588    },
589    StdEntry {
590        name: "List.length_nil",
591        module: "Std.List",
592        description: "Length of the empty list is 0",
593        is_theorem: true,
594    },
595    StdEntry {
596        name: "List.length_cons",
597        module: "Std.List",
598        description: "Length of cons is 1 + length of tail",
599        is_theorem: true,
600    },
601    StdEntry {
602        name: "Bool.not_not",
603        module: "Std.Bool",
604        description: "Double negation elimination for Bool",
605        is_theorem: true,
606    },
607    StdEntry {
608        name: "Bool.and_comm",
609        module: "Std.Bool",
610        description: "Commutativity of boolean and",
611        is_theorem: true,
612    },
613    StdEntry {
614        name: "Option.some_ne_none",
615        module: "Std.Option",
616        description: "Some x is never None",
617        is_theorem: true,
618    },
619];
620
621/// Look up a standard library entry by its qualified name.
622#[allow(dead_code)]
623pub fn lookup_std_entry(name: &str) -> Option<&'static StdEntry> {
624    STD_KNOWN_ENTRIES.iter().find(|e| e.name == name)
625}
626
627/// Return all entries from a given module.
628#[allow(dead_code)]
629pub fn entries_in_module(module: &str) -> Vec<&'static StdEntry> {
630    STD_KNOWN_ENTRIES
631        .iter()
632        .filter(|e| e.module == module)
633        .collect()
634}
635
636/// Return all theorems (non-definitions) in the standard library sample.
637#[allow(dead_code)]
638pub fn all_theorems() -> Vec<&'static StdEntry> {
639    STD_KNOWN_ENTRIES.iter().filter(|e| e.is_theorem).collect()
640}
641
642/// A category tag for standard library modules.
643#[allow(dead_code)]
644#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
645pub enum StdCategory {
646    /// Core arithmetic (Nat, Int).
647    Arithmetic,
648    /// Logic (Prop, And, Or, Not, Iff).
649    Logic,
650    /// Data structures (List, Array, Option, etc.).
651    Data,
652    /// Type classes (Eq, Ord, Functor, etc.).
653    TypeClass,
654    /// IO and system operations.
655    IO,
656    /// String operations.
657    String,
658    /// Tactics and proof automation.
659    Tactic,
660    /// Universe polymorphism.
661    Universe,
662}
663
664impl StdCategory {
665    /// Human-readable label.
666    #[allow(dead_code)]
667    pub fn label(self) -> &'static str {
668        match self {
669            Self::Arithmetic => "Arithmetic",
670            Self::Logic => "Logic",
671            Self::Data => "Data",
672            Self::TypeClass => "TypeClass",
673            Self::IO => "IO",
674            Self::String => "String",
675            Self::Tactic => "Tactic",
676            Self::Universe => "Universe",
677        }
678    }
679}
680
681/// Map a module name to its `StdCategory`.
682#[allow(dead_code)]
683pub fn module_category(module: &str) -> StdCategory {
684    if module.contains("Nat") || module.contains("Int") {
685        StdCategory::Arithmetic
686    } else if module.contains("Logic") || module.contains("Prop") {
687        StdCategory::Logic
688    } else if module.contains("List") || module.contains("Option") || module.contains("Array") {
689        StdCategory::Data
690    } else if module.contains("Functor") || module.contains("Monad") || module.contains("Eq") {
691        StdCategory::TypeClass
692    } else if module.contains("IO") {
693        StdCategory::IO
694    } else if module.contains("String") || module.contains("Char") {
695        StdCategory::String
696    } else if module.contains("Tactic") {
697        StdCategory::Tactic
698    } else {
699        StdCategory::Universe
700    }
701}
702
703/// Version information for the OxiLean standard library.
704#[allow(dead_code)]
705pub struct StdVersion {
706    /// Major version number.
707    pub major: u32,
708    /// Minor version number.
709    pub minor: u32,
710    /// Patch version number.
711    pub patch: u32,
712    /// Pre-release label (empty for stable).
713    pub pre: &'static str,
714}
715
716impl StdVersion {
717    /// The current standard library version.
718    #[allow(dead_code)]
719    pub const CURRENT: StdVersion = StdVersion {
720        major: 0,
721        minor: 1,
722        patch: 0,
723        pre: "alpha",
724    };
725
726    /// Format as a semver string.
727    #[allow(dead_code)]
728    pub fn format_version(&self) -> String {
729        self.to_string()
730    }
731
732    /// Check if this is a stable release.
733    #[allow(dead_code)]
734    pub fn is_stable(&self) -> bool {
735        self.pre.is_empty()
736    }
737}
738
739impl std::fmt::Display for StdVersion {
740    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
741        if self.pre.is_empty() {
742            write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
743        } else {
744            write!(
745                f,
746                "{}.{}.{}-{}",
747                self.major, self.minor, self.patch, self.pre
748            )
749        }
750    }
751}
752
753// ============================================================
754// Tests
755// ============================================================
756
757#[cfg(test)]
758mod tests {
759    use super::*;
760
761    #[test]
762    fn test_module_registry_not_empty() {
763        assert!(!STD_MODULE_REGISTRY.is_empty());
764        assert!(count_total_modules() > 10);
765    }
766
767    #[test]
768    fn test_default_modules_subset() {
769        let defaults = default_modules();
770        assert!(!defaults.is_empty());
771        assert!(defaults.len() <= count_total_modules());
772    }
773
774    #[test]
775    fn test_modules_for_phase() {
776        let primitives = modules_for_phase(BuildPhase::Primitives);
777        assert!(!primitives.is_empty());
778        for m in &primitives {
779            assert_eq!(m.phase, BuildPhase::Primitives);
780        }
781    }
782
783    #[test]
784    fn test_find_module_existing() {
785        let m = find_module("Std.Nat");
786        assert!(m.is_some());
787        assert_eq!(m.expect("m should be valid").phase, BuildPhase::Primitives);
788    }
789
790    #[test]
791    fn test_find_module_nonexistent() {
792        assert!(find_module("Std.DoesNotExist").is_none());
793    }
794
795    #[test]
796    fn test_direct_deps() {
797        let deps = direct_deps("Std.List");
798        assert!(deps.contains(&"Std.Nat"));
799    }
800
801    #[test]
802    fn test_dependents_of() {
803        let deps = dependents_of("Std.Nat");
804        assert!(deps.contains(&"Std.List"));
805    }
806
807    #[test]
808    fn test_build_phase_order() {
809        let phases = BuildPhase::all_in_order();
810        assert_eq!(phases.len(), 5);
811        assert_eq!(phases[0], BuildPhase::Primitives);
812        assert_eq!(phases[4], BuildPhase::Automation);
813    }
814
815    #[test]
816    fn test_std_features_default() {
817        let f = StdFeatures::default_features();
818        assert!(f.classical);
819        assert!(f.propext);
820        assert!(!f.topology);
821    }
822
823    #[test]
824    fn test_std_features_full() {
825        let f = StdFeatures::full();
826        assert!(f.classical && f.topology && f.reals && f.quotient);
827        assert_eq!(f.count_enabled(), 7);
828    }
829
830    #[test]
831    fn test_build_phase_names() {
832        assert_eq!(BuildPhase::Primitives.name(), "primitives");
833        assert_eq!(BuildPhase::Automation.name(), "automation");
834    }
835
836    #[test]
837    fn test_count_default_modules() {
838        let count = count_default_modules();
839        assert!(count > 0);
840        assert!(count <= count_total_modules());
841    }
842
843    #[test]
844    fn test_std_lib_stats() {
845        let stats = StdLibStats::compute();
846        assert!(stats.total_modules > 0);
847        assert!(stats.all_phases_populated());
848    }
849
850    #[test]
851    fn test_all_modules_count() {
852        assert_eq!(all_modules().len(), STD_MODULE_REGISTRY.len());
853    }
854
855    #[test]
856    fn test_std_version_nonempty() {
857        assert!(!STD_VERSION.is_empty());
858    }
859
860    #[test]
861    fn test_std_lib_stats_compute() {
862        let s = StdLibStats::compute();
863        assert_eq!(s.total_modules, count_total_modules());
864        assert!(s.all_phases_populated());
865        assert!(s.phase_total() > 0);
866    }
867
868    #[test]
869    fn test_std_lib_stats_phase_total() {
870        let s = StdLibStats::compute();
871        assert_eq!(s.phase_total(), s.total_modules);
872    }
873
874    #[test]
875    fn test_module_descriptions_not_empty() {
876        for m in STD_MODULE_REGISTRY {
877            assert!(
878                !m.description.is_empty(),
879                "Module {} has empty description",
880                m.qualified_name
881            );
882        }
883    }
884
885    #[test]
886    fn test_topological_sort_acyclic() {
887        let result = topological_sort_modules();
888        assert!(result.is_some(), "Dependency graph should have no cycles");
889        let sorted = result.expect("result should be valid");
890        assert!(!sorted.is_empty());
891    }
892
893    #[test]
894    fn test_nat_before_list() {
895        let sorted = topological_sort_modules().expect("operation should succeed");
896        let nat_pos = sorted.iter().position(|&s| s == "Std.Nat");
897        let list_pos = sorted.iter().position(|&s| s == "Std.List");
898        if let (Some(np), Some(lp)) = (nat_pos, list_pos) {
899            assert!(np < lp, "Nat must appear before List");
900        }
901    }
902
903    #[test]
904    fn test_nat_before_array() {
905        let sorted = topological_sort_modules().expect("operation should succeed");
906        let nat_pos = sorted.iter().position(|&s| s == "Std.Nat");
907        let arr_pos = sorted.iter().position(|&s| s == "Std.Array");
908        if let (Some(np), Some(ap)) = (nat_pos, arr_pos) {
909            assert!(np < ap);
910        }
911    }
912
913    #[test]
914    fn test_all_modules_in_sorted() {
915        let sorted = topological_sort_modules().expect("operation should succeed");
916        for entry in STD_MODULE_REGISTRY {
917            assert!(
918                sorted.contains(&entry.qualified_name),
919                "Module {} missing from topological sort",
920                entry.qualified_name
921            );
922        }
923    }
924
925    #[test]
926    fn test_std_features_default_count() {
927        let f = StdFeatures::default_features();
928        // classical, propext, funext are true → 3 enabled
929        assert_eq!(f.count_enabled(), 3);
930    }
931
932    #[test]
933    fn test_module_dep_dependent_in_registry() {
934        let names: Vec<_> = STD_MODULE_REGISTRY
935            .iter()
936            .map(|e| e.qualified_name)
937            .collect();
938        for dep in CORE_DEPS {
939            assert!(
940                names.contains(&dep.dependent),
941                "Dependent {} not in registry",
942                dep.dependent
943            );
944        }
945    }
946
947    #[test]
948    fn test_module_dep_dependency_in_registry() {
949        let names: Vec<_> = STD_MODULE_REGISTRY
950            .iter()
951            .map(|e| e.qualified_name)
952            .collect();
953        for dep in CORE_DEPS {
954            assert!(
955                names.contains(&dep.dependency),
956                "Dependency {} not in registry",
957                dep.dependency
958            );
959        }
960    }
961
962    #[test]
963    fn test_std_lib_stats_phase_count() {
964        let s = StdLibStats::compute();
965        assert_eq!(s.per_phase.len(), 5);
966    }
967
968    #[test]
969    fn test_direct_deps_non_empty() {
970        let deps = direct_deps("Std.Monad");
971        assert!(!deps.is_empty());
972    }
973
974    #[test]
975    fn test_lookup_std_entry_found() {
976        let e = lookup_std_entry("Nat.add_comm");
977        assert!(e.is_some());
978        assert!(e.expect("e should be valid").is_theorem);
979    }
980
981    #[test]
982    fn test_lookup_std_entry_not_found() {
983        assert!(lookup_std_entry("Nonexistent.theorem").is_none());
984    }
985
986    #[test]
987    fn test_entries_in_module() {
988        let nat_entries = entries_in_module("Std.Nat");
989        assert!(!nat_entries.is_empty());
990        assert!(nat_entries.iter().all(|e| e.module == "Std.Nat"));
991    }
992
993    #[test]
994    fn test_all_theorems_nonempty() {
995        let thms = all_theorems();
996        assert!(!thms.is_empty());
997        assert!(thms.iter().all(|e| e.is_theorem));
998    }
999
1000    #[test]
1001    fn test_module_category_nat() {
1002        assert_eq!(module_category("Std.Nat"), StdCategory::Arithmetic);
1003    }
1004
1005    #[test]
1006    fn test_module_category_list() {
1007        assert_eq!(module_category("Std.List"), StdCategory::Data);
1008    }
1009
1010    #[test]
1011    fn test_module_category_io() {
1012        assert_eq!(module_category("Std.IO"), StdCategory::IO);
1013    }
1014
1015    #[test]
1016    fn test_std_version_to_string() {
1017        let v = StdVersion {
1018            major: 1,
1019            minor: 2,
1020            patch: 3,
1021            pre: "",
1022        };
1023        assert_eq!(v.to_string(), "1.2.3");
1024    }
1025
1026    #[test]
1027    fn test_std_version_prerelease_to_string() {
1028        let v = StdVersion {
1029            major: 0,
1030            minor: 1,
1031            patch: 0,
1032            pre: "alpha",
1033        };
1034        assert_eq!(v.to_string(), "0.1.0-alpha");
1035    }
1036
1037    #[test]
1038    fn test_std_version_is_stable_false() {
1039        assert!(!StdVersion::CURRENT.is_stable());
1040    }
1041
1042    #[test]
1043    fn test_std_category_label() {
1044        assert_eq!(StdCategory::Arithmetic.label(), "Arithmetic");
1045        assert_eq!(StdCategory::Logic.label(), "Logic");
1046        assert_eq!(StdCategory::Data.label(), "Data");
1047    }
1048
1049    #[test]
1050    fn test_std_known_entries_nonempty() {
1051        assert!(!STD_KNOWN_ENTRIES.is_empty());
1052    }
1053
1054    #[test]
1055    fn test_all_entries_have_module() {
1056        for e in STD_KNOWN_ENTRIES {
1057            assert!(!e.module.is_empty(), "Entry {} has empty module", e.name);
1058        }
1059    }
1060}