Skip to main content

oxilean_codegen/lean4_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::{HashMap, HashSet, VecDeque};
6
7/// Lean 4 code generation backend.
8pub struct Lean4Backend {
9    pub(super) file: Lean4File,
10}
11impl Lean4Backend {
12    /// Create a new Lean 4 backend.
13    pub fn new() -> Self {
14        Lean4Backend {
15            file: Lean4File::new(),
16        }
17    }
18    /// Create a backend with standard Lean 4 imports.
19    pub fn with_std_imports() -> Self {
20        let file = Lean4File::new().with_import("Init").with_import("Std");
21        Lean4Backend { file }
22    }
23    /// Compile a simple kernel declaration (name + type + body) to a Lean 4 def.
24    pub fn compile_kernel_decl(
25        &mut self,
26        name: &str,
27        args: Vec<(std::string::String, Lean4Type)>,
28        ret_ty: Lean4Type,
29        body: Lean4Expr,
30    ) {
31        let def = Lean4Def::simple(name, args, ret_ty, body);
32        self.file.add_decl(Lean4Decl::Def(def));
33    }
34    /// Add a theorem with a tactic proof.
35    pub fn add_theorem(
36        &mut self,
37        name: &str,
38        args: Vec<(std::string::String, Lean4Type)>,
39        ty: Lean4Type,
40        tactics: Vec<std::string::String>,
41    ) {
42        let thm = Lean4Theorem::tactic(name, args, ty, tactics);
43        self.file.add_decl(Lean4Decl::Theorem(thm));
44    }
45    /// Add an inductive type.
46    pub fn add_inductive(&mut self, ind: Lean4Inductive) {
47        self.file.add_decl(Lean4Decl::Inductive(ind));
48    }
49    /// Emit the complete Lean 4 file.
50    pub fn emit_file(&self) -> std::string::String {
51        self.file.emit()
52    }
53    /// Get a mutable reference to the file.
54    pub fn file_mut(&mut self) -> &mut Lean4File {
55        &mut self.file
56    }
57}
58#[allow(dead_code)]
59pub struct L4PassRegistry {
60    pub(super) configs: Vec<L4PassConfig>,
61    pub(super) stats: std::collections::HashMap<String, L4PassStats>,
62}
63impl L4PassRegistry {
64    #[allow(dead_code)]
65    pub fn new() -> Self {
66        L4PassRegistry {
67            configs: Vec::new(),
68            stats: std::collections::HashMap::new(),
69        }
70    }
71    #[allow(dead_code)]
72    pub fn register(&mut self, config: L4PassConfig) {
73        self.stats
74            .insert(config.pass_name.clone(), L4PassStats::new());
75        self.configs.push(config);
76    }
77    #[allow(dead_code)]
78    pub fn enabled_passes(&self) -> Vec<&L4PassConfig> {
79        self.configs.iter().filter(|c| c.enabled).collect()
80    }
81    #[allow(dead_code)]
82    pub fn get_stats(&self, name: &str) -> Option<&L4PassStats> {
83        self.stats.get(name)
84    }
85    #[allow(dead_code)]
86    pub fn total_passes(&self) -> usize {
87        self.configs.len()
88    }
89    #[allow(dead_code)]
90    pub fn enabled_count(&self) -> usize {
91        self.enabled_passes().len()
92    }
93    #[allow(dead_code)]
94    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
95        if let Some(stats) = self.stats.get_mut(name) {
96            stats.record_run(changes, time_ms, iter);
97        }
98    }
99}
100#[allow(dead_code)]
101#[derive(Debug, Clone)]
102pub struct L4DepGraph {
103    pub(super) nodes: Vec<u32>,
104    pub(super) edges: Vec<(u32, u32)>,
105}
106impl L4DepGraph {
107    #[allow(dead_code)]
108    pub fn new() -> Self {
109        L4DepGraph {
110            nodes: Vec::new(),
111            edges: Vec::new(),
112        }
113    }
114    #[allow(dead_code)]
115    pub fn add_node(&mut self, id: u32) {
116        if !self.nodes.contains(&id) {
117            self.nodes.push(id);
118        }
119    }
120    #[allow(dead_code)]
121    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
122        self.add_node(dep);
123        self.add_node(dependent);
124        self.edges.push((dep, dependent));
125    }
126    #[allow(dead_code)]
127    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
128        self.edges
129            .iter()
130            .filter(|(d, _)| *d == node)
131            .map(|(_, dep)| *dep)
132            .collect()
133    }
134    #[allow(dead_code)]
135    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
136        self.edges
137            .iter()
138            .filter(|(_, dep)| *dep == node)
139            .map(|(d, _)| *d)
140            .collect()
141    }
142    #[allow(dead_code)]
143    pub fn topological_sort(&self) -> Vec<u32> {
144        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
145        for &n in &self.nodes {
146            in_degree.insert(n, 0);
147        }
148        for (_, dep) in &self.edges {
149            *in_degree.entry(*dep).or_insert(0) += 1;
150        }
151        let mut queue: std::collections::VecDeque<u32> = self
152            .nodes
153            .iter()
154            .filter(|&&n| in_degree[&n] == 0)
155            .copied()
156            .collect();
157        let mut result = Vec::new();
158        while let Some(node) = queue.pop_front() {
159            result.push(node);
160            for dep in self.dependents_of(node) {
161                let cnt = in_degree.entry(dep).or_insert(0);
162                *cnt = cnt.saturating_sub(1);
163                if *cnt == 0 {
164                    queue.push_back(dep);
165                }
166            }
167        }
168        result
169    }
170    #[allow(dead_code)]
171    pub fn has_cycle(&self) -> bool {
172        self.topological_sort().len() < self.nodes.len()
173    }
174}
175/// Lean 4 type representation.
176#[derive(Debug, Clone, PartialEq, Eq, Hash)]
177pub enum Lean4Type {
178    /// `Nat` — natural numbers
179    Nat,
180    /// `Int` — integers
181    Int,
182    /// `Float` — 64-bit floating point
183    Float,
184    /// `Bool` — boolean
185    Bool,
186    /// `String` — string type
187    String,
188    /// `Unit` — unit type
189    Unit,
190    /// `Prop` — sort of propositions
191    Prop,
192    /// `Type u` — universe
193    Type(u32),
194    /// `List α`
195    List(Box<Lean4Type>),
196    /// `Option α`
197    Option(Box<Lean4Type>),
198    /// `α × β` (product)
199    Prod(Box<Lean4Type>, Box<Lean4Type>),
200    /// `α ⊕ β` (sum / Either)
201    Sum(Box<Lean4Type>, Box<Lean4Type>),
202    /// `α → β` (function type)
203    Fun(Box<Lean4Type>, Box<Lean4Type>),
204    /// Named type (user-defined or imported)
205    Custom(std::string::String),
206    /// `∀ (x : α), β x` (dependent function / Pi type)
207    ForAll(std::string::String, Box<Lean4Type>, Box<Lean4Type>),
208    /// Type application: `f α`
209    App(Box<Lean4Type>, Box<Lean4Type>),
210    /// `IO α`
211    IO(Box<Lean4Type>),
212    /// `Array α`
213    Array(Box<Lean4Type>),
214    /// `Fin n`
215    Fin(Box<Lean4Type>),
216    /// `Char`
217    Char,
218}
219/// Top-level Lean 4 declaration.
220#[derive(Debug, Clone)]
221pub enum Lean4Decl {
222    /// `def` definition
223    Def(Lean4Def),
224    /// `theorem` declaration
225    Theorem(Lean4Theorem),
226    /// `axiom` declaration
227    Axiom(Lean4Axiom),
228    /// `abbrev` declaration
229    Abbrev(Lean4Abbrev),
230    /// `structure` declaration
231    Structure(Lean4Structure),
232    /// `inductive` type
233    Inductive(Lean4Inductive),
234    /// `instance` declaration
235    Instance(Lean4Instance),
236    /// `#check` command
237    Check(Lean4Expr),
238    /// `#eval` command
239    Eval(Lean4Expr),
240    /// Raw source string (escape hatch)
241    Raw(std::string::String),
242    /// Section: `section Name ... end Name`
243    Section(std::string::String, Vec<Lean4Decl>),
244    /// Namespace: `namespace Name ... end Name`
245    Namespace(std::string::String, Vec<Lean4Decl>),
246}
247impl Lean4Decl {
248    /// Emit the declaration as Lean 4 source.
249    pub fn emit(&self) -> std::string::String {
250        match self {
251            Lean4Decl::Def(d) => d.emit(),
252            Lean4Decl::Theorem(t) => t.emit(),
253            Lean4Decl::Axiom(a) => a.emit(),
254            Lean4Decl::Abbrev(a) => a.emit(),
255            Lean4Decl::Structure(s) => s.emit(),
256            Lean4Decl::Inductive(i) => i.emit(),
257            Lean4Decl::Instance(inst) => inst.emit(),
258            Lean4Decl::Check(e) => format!("#check {}\n", e),
259            Lean4Decl::Eval(e) => format!("#eval {}\n", e),
260            Lean4Decl::Raw(s) => format!("{}\n", s),
261            Lean4Decl::Section(name, decls) => {
262                let mut out = format!("section {}\n\n", name);
263                for d in decls {
264                    out.push_str(&d.emit());
265                    out.push('\n');
266                }
267                out.push_str(&format!("end {}\n", name));
268                out
269            }
270            Lean4Decl::Namespace(name, decls) => {
271                let mut out = format!("namespace {}\n\n", name);
272                for d in decls {
273                    out.push_str(&d.emit());
274                    out.push('\n');
275                }
276                out.push_str(&format!("end {}\n", name));
277                out
278            }
279        }
280    }
281}
282/// A complete Lean 4 source file.
283#[derive(Debug, Clone)]
284pub struct Lean4File {
285    /// Module documentation comment
286    pub module_doc: Option<std::string::String>,
287    /// Import statements: `import Mathlib.Data.Nat.Basic`
288    pub imports: Vec<std::string::String>,
289    /// `open` statements: `open Nat List`
290    pub opens: Vec<std::string::String>,
291    /// Top-level declarations
292    pub declarations: Vec<Lean4Decl>,
293}
294impl Lean4File {
295    /// Create a new empty Lean 4 file.
296    pub fn new() -> Self {
297        Lean4File {
298            module_doc: None,
299            imports: vec![],
300            opens: vec![],
301            declarations: vec![],
302        }
303    }
304    /// Add a standard import.
305    pub fn with_import(mut self, import: impl Into<std::string::String>) -> Self {
306        self.imports.push(import.into());
307        self
308    }
309    /// Add an `open` declaration.
310    pub fn with_open(mut self, open: impl Into<std::string::String>) -> Self {
311        self.opens.push(open.into());
312        self
313    }
314    /// Add a declaration.
315    pub fn add_decl(&mut self, decl: Lean4Decl) {
316        self.declarations.push(decl);
317    }
318    /// Generate the complete Lean 4 source.
319    pub fn emit(&self) -> std::string::String {
320        let mut out = std::string::String::new();
321        if let Some(doc) = &self.module_doc {
322            out.push_str(&format!("/-!\n{}\n-/\n\n", doc));
323        }
324        for import in &self.imports {
325            out.push_str(&format!("import {}\n", import));
326        }
327        if !self.imports.is_empty() {
328            out.push('\n');
329        }
330        for open in &self.opens {
331            out.push_str(&format!("open {}\n", open));
332        }
333        if !self.opens.is_empty() {
334            out.push('\n');
335        }
336        for decl in &self.declarations {
337            out.push_str(&decl.emit());
338            out.push('\n');
339        }
340        out
341    }
342}
343/// Analysis cache for L4Ext.
344#[allow(dead_code)]
345#[derive(Debug)]
346pub struct L4ExtCache {
347    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
348    pub(super) cap: usize,
349    pub(super) total_hits: u64,
350    pub(super) total_misses: u64,
351}
352impl L4ExtCache {
353    #[allow(dead_code)]
354    pub fn new(cap: usize) -> Self {
355        Self {
356            entries: Vec::new(),
357            cap,
358            total_hits: 0,
359            total_misses: 0,
360        }
361    }
362    #[allow(dead_code)]
363    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
364        for e in self.entries.iter_mut() {
365            if e.0 == key && e.2 {
366                e.3 += 1;
367                self.total_hits += 1;
368                return Some(&e.1);
369            }
370        }
371        self.total_misses += 1;
372        None
373    }
374    #[allow(dead_code)]
375    pub fn put(&mut self, key: u64, data: Vec<u8>) {
376        if self.entries.len() >= self.cap {
377            self.entries.retain(|e| e.2);
378            if self.entries.len() >= self.cap {
379                self.entries.remove(0);
380            }
381        }
382        self.entries.push((key, data, true, 0));
383    }
384    #[allow(dead_code)]
385    pub fn invalidate(&mut self) {
386        for e in self.entries.iter_mut() {
387            e.2 = false;
388        }
389    }
390    #[allow(dead_code)]
391    pub fn hit_rate(&self) -> f64 {
392        let t = self.total_hits + self.total_misses;
393        if t == 0 {
394            0.0
395        } else {
396            self.total_hits as f64 / t as f64
397        }
398    }
399    #[allow(dead_code)]
400    pub fn live_count(&self) -> usize {
401        self.entries.iter().filter(|e| e.2).count()
402    }
403}
404/// A constructor of an inductive type.
405#[derive(Debug, Clone)]
406pub struct Lean4Constructor {
407    /// Constructor name
408    pub name: std::string::String,
409    /// Field types with optional names
410    pub fields: Vec<(Option<std::string::String>, Lean4Type)>,
411}
412impl Lean4Constructor {
413    /// Simple constructor with positional fields.
414    pub fn positional(name: impl Into<std::string::String>, fields: Vec<Lean4Type>) -> Self {
415        Lean4Constructor {
416            name: name.into(),
417            fields: fields.into_iter().map(|t| (None, t)).collect(),
418        }
419    }
420    /// Constructor with named fields (record-style).
421    pub fn named(
422        name: impl Into<std::string::String>,
423        fields: Vec<(std::string::String, Lean4Type)>,
424    ) -> Self {
425        Lean4Constructor {
426            name: name.into(),
427            fields: fields.into_iter().map(|(n, t)| (Some(n), t)).collect(),
428        }
429    }
430    pub fn emit(&self, type_name: &str) -> std::string::String {
431        let mut out = std::string::String::new();
432        out.push_str(&format!("  | {} : ", self.name));
433        for (oname, ty) in &self.fields {
434            if let Some(fname) = oname {
435                out.push_str(&format!("({} : {}) → ", fname, ty));
436            } else {
437                out.push_str(&format!("{} → ", ty));
438            }
439        }
440        out.push_str(type_name);
441        out
442    }
443}
444/// Worklist for L4Ext.
445#[allow(dead_code)]
446#[derive(Debug, Clone)]
447pub struct L4ExtWorklist {
448    pub(super) items: std::collections::VecDeque<usize>,
449    pub(super) present: Vec<bool>,
450}
451impl L4ExtWorklist {
452    #[allow(dead_code)]
453    pub fn new(capacity: usize) -> Self {
454        Self {
455            items: std::collections::VecDeque::new(),
456            present: vec![false; capacity],
457        }
458    }
459    #[allow(dead_code)]
460    pub fn push(&mut self, id: usize) {
461        if id < self.present.len() && !self.present[id] {
462            self.present[id] = true;
463            self.items.push_back(id);
464        }
465    }
466    #[allow(dead_code)]
467    pub fn push_front(&mut self, id: usize) {
468        if id < self.present.len() && !self.present[id] {
469            self.present[id] = true;
470            self.items.push_front(id);
471        }
472    }
473    #[allow(dead_code)]
474    pub fn pop(&mut self) -> Option<usize> {
475        let id = self.items.pop_front()?;
476        if id < self.present.len() {
477            self.present[id] = false;
478        }
479        Some(id)
480    }
481    #[allow(dead_code)]
482    pub fn is_empty(&self) -> bool {
483        self.items.is_empty()
484    }
485    #[allow(dead_code)]
486    pub fn len(&self) -> usize {
487        self.items.len()
488    }
489    #[allow(dead_code)]
490    pub fn contains(&self, id: usize) -> bool {
491        id < self.present.len() && self.present[id]
492    }
493    #[allow(dead_code)]
494    pub fn drain_all(&mut self) -> Vec<usize> {
495        let v: Vec<usize> = self.items.drain(..).collect();
496        for &id in &v {
497            if id < self.present.len() {
498                self.present[id] = false;
499            }
500        }
501        v
502    }
503}
504/// Configuration for L4Ext passes.
505#[allow(dead_code)]
506#[derive(Debug, Clone)]
507pub struct L4ExtPassConfig {
508    pub name: String,
509    pub phase: L4ExtPassPhase,
510    pub enabled: bool,
511    pub max_iterations: usize,
512    pub debug: u32,
513    pub timeout_ms: Option<u64>,
514}
515impl L4ExtPassConfig {
516    #[allow(dead_code)]
517    pub fn new(name: impl Into<String>) -> Self {
518        Self {
519            name: name.into(),
520            phase: L4ExtPassPhase::Middle,
521            enabled: true,
522            max_iterations: 100,
523            debug: 0,
524            timeout_ms: None,
525        }
526    }
527    #[allow(dead_code)]
528    pub fn with_phase(mut self, phase: L4ExtPassPhase) -> Self {
529        self.phase = phase;
530        self
531    }
532    #[allow(dead_code)]
533    pub fn with_max_iter(mut self, n: usize) -> Self {
534        self.max_iterations = n;
535        self
536    }
537    #[allow(dead_code)]
538    pub fn with_debug(mut self, d: u32) -> Self {
539        self.debug = d;
540        self
541    }
542    #[allow(dead_code)]
543    pub fn disabled(mut self) -> Self {
544        self.enabled = false;
545        self
546    }
547    #[allow(dead_code)]
548    pub fn with_timeout(mut self, ms: u64) -> Self {
549        self.timeout_ms = Some(ms);
550        self
551    }
552    #[allow(dead_code)]
553    pub fn is_debug_enabled(&self) -> bool {
554        self.debug > 0
555    }
556}
557#[allow(dead_code)]
558#[derive(Debug, Clone, Default)]
559pub struct L4PassStats {
560    pub total_runs: u32,
561    pub successful_runs: u32,
562    pub total_changes: u64,
563    pub time_ms: u64,
564    pub iterations_used: u32,
565}
566impl L4PassStats {
567    #[allow(dead_code)]
568    pub fn new() -> Self {
569        Self::default()
570    }
571    #[allow(dead_code)]
572    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
573        self.total_runs += 1;
574        self.successful_runs += 1;
575        self.total_changes += changes;
576        self.time_ms += time_ms;
577        self.iterations_used = iterations;
578    }
579    #[allow(dead_code)]
580    pub fn average_changes_per_run(&self) -> f64 {
581        if self.total_runs == 0 {
582            return 0.0;
583        }
584        self.total_changes as f64 / self.total_runs as f64
585    }
586    #[allow(dead_code)]
587    pub fn success_rate(&self) -> f64 {
588        if self.total_runs == 0 {
589            return 0.0;
590        }
591        self.successful_runs as f64 / self.total_runs as f64
592    }
593    #[allow(dead_code)]
594    pub fn format_summary(&self) -> String {
595        format!(
596            "Runs: {}/{}, Changes: {}, Time: {}ms",
597            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
598        )
599    }
600}
601/// A Lean 4 `instance` declaration.
602#[derive(Debug, Clone)]
603pub struct Lean4Instance {
604    /// Optional instance name
605    pub name: Option<std::string::String>,
606    /// Type class application (the type being instantiated)
607    pub ty: Lean4Type,
608    /// Arguments (implicit, typeclass)
609    pub args: Vec<(std::string::String, Lean4Type)>,
610    /// The instance body (a `where` block or a term)
611    pub body: Lean4Expr,
612}
613impl Lean4Instance {
614    /// Emit the instance as Lean 4 source.
615    pub fn emit(&self) -> std::string::String {
616        let mut out = std::string::String::new();
617        out.push_str("instance");
618        if let Some(name) = &self.name {
619            out.push(' ');
620            out.push_str(name);
621        }
622        for (aname, aty) in &self.args {
623            out.push_str(&format!(" ({} : {})", aname, aty));
624        }
625        out.push_str(&format!(" : {} :=\n  {}\n", self.ty, self.body));
626        out
627    }
628}
629/// Statements in a `do` block.
630#[derive(Debug, Clone, PartialEq)]
631pub enum Lean4DoStmt {
632    /// `let x ← action`
633    Bind(std::string::String, Option<Lean4Type>, Box<Lean4Expr>),
634    /// `let x := value`
635    LetBind(std::string::String, Option<Lean4Type>, Box<Lean4Expr>),
636    /// Plain expression statement: `action`
637    Expr(Box<Lean4Expr>),
638    /// `return e`
639    Return(Box<Lean4Expr>),
640    /// `pure e`
641    Pure(Box<Lean4Expr>),
642    /// `if c then ...`
643    If(Box<Lean4Expr>, Vec<Lean4DoStmt>, Vec<Lean4DoStmt>),
644}
645/// A Lean 4 `axiom` declaration.
646#[derive(Debug, Clone)]
647pub struct Lean4Axiom {
648    /// Axiom name
649    pub name: std::string::String,
650    /// Arguments
651    pub args: Vec<(std::string::String, Lean4Type)>,
652    /// The type (proposition)
653    pub ty: Lean4Type,
654    /// Optional doc comment
655    pub doc_comment: Option<std::string::String>,
656}
657impl Lean4Axiom {
658    /// Emit the axiom as Lean 4 source.
659    pub fn emit(&self) -> std::string::String {
660        let mut out = std::string::String::new();
661        if let Some(doc) = &self.doc_comment {
662            out.push_str(&format!("/-- {} -/\n", doc));
663        }
664        out.push_str("axiom ");
665        out.push_str(&self.name);
666        for (aname, aty) in &self.args {
667            out.push_str(&format!(" ({} : {})", aname, aty));
668        }
669        out.push_str(&format!(" : {}\n", self.ty));
670        out
671    }
672}
673#[allow(dead_code)]
674#[derive(Debug, Clone)]
675pub struct L4LivenessInfo {
676    pub live_in: Vec<std::collections::HashSet<u32>>,
677    pub live_out: Vec<std::collections::HashSet<u32>>,
678    pub defs: Vec<std::collections::HashSet<u32>>,
679    pub uses: Vec<std::collections::HashSet<u32>>,
680}
681impl L4LivenessInfo {
682    #[allow(dead_code)]
683    pub fn new(block_count: usize) -> Self {
684        L4LivenessInfo {
685            live_in: vec![std::collections::HashSet::new(); block_count],
686            live_out: vec![std::collections::HashSet::new(); block_count],
687            defs: vec![std::collections::HashSet::new(); block_count],
688            uses: vec![std::collections::HashSet::new(); block_count],
689        }
690    }
691    #[allow(dead_code)]
692    pub fn add_def(&mut self, block: usize, var: u32) {
693        if block < self.defs.len() {
694            self.defs[block].insert(var);
695        }
696    }
697    #[allow(dead_code)]
698    pub fn add_use(&mut self, block: usize, var: u32) {
699        if block < self.uses.len() {
700            self.uses[block].insert(var);
701        }
702    }
703    #[allow(dead_code)]
704    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
705        self.live_in
706            .get(block)
707            .map(|s| s.contains(&var))
708            .unwrap_or(false)
709    }
710    #[allow(dead_code)]
711    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
712        self.live_out
713            .get(block)
714            .map(|s| s.contains(&var))
715            .unwrap_or(false)
716    }
717}
718/// Dependency graph for L4Ext.
719#[allow(dead_code)]
720#[derive(Debug, Clone)]
721pub struct L4ExtDepGraph {
722    pub(super) n: usize,
723    pub(super) adj: Vec<Vec<usize>>,
724    pub(super) rev: Vec<Vec<usize>>,
725    pub(super) edge_count: usize,
726}
727impl L4ExtDepGraph {
728    #[allow(dead_code)]
729    pub fn new(n: usize) -> Self {
730        Self {
731            n,
732            adj: vec![Vec::new(); n],
733            rev: vec![Vec::new(); n],
734            edge_count: 0,
735        }
736    }
737    #[allow(dead_code)]
738    pub fn add_edge(&mut self, from: usize, to: usize) {
739        if from < self.n && to < self.n {
740            if !self.adj[from].contains(&to) {
741                self.adj[from].push(to);
742                self.rev[to].push(from);
743                self.edge_count += 1;
744            }
745        }
746    }
747    #[allow(dead_code)]
748    pub fn succs(&self, n: usize) -> &[usize] {
749        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
750    }
751    #[allow(dead_code)]
752    pub fn preds(&self, n: usize) -> &[usize] {
753        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
754    }
755    #[allow(dead_code)]
756    pub fn topo_sort(&self) -> Option<Vec<usize>> {
757        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
758        let mut q: std::collections::VecDeque<usize> =
759            (0..self.n).filter(|&i| deg[i] == 0).collect();
760        let mut out = Vec::with_capacity(self.n);
761        while let Some(u) = q.pop_front() {
762            out.push(u);
763            for &v in &self.adj[u] {
764                deg[v] -= 1;
765                if deg[v] == 0 {
766                    q.push_back(v);
767                }
768            }
769        }
770        if out.len() == self.n {
771            Some(out)
772        } else {
773            None
774        }
775    }
776    #[allow(dead_code)]
777    pub fn has_cycle(&self) -> bool {
778        self.topo_sort().is_none()
779    }
780    #[allow(dead_code)]
781    pub fn reachable(&self, start: usize) -> Vec<usize> {
782        let mut vis = vec![false; self.n];
783        let mut stk = vec![start];
784        let mut out = Vec::new();
785        while let Some(u) = stk.pop() {
786            if u < self.n && !vis[u] {
787                vis[u] = true;
788                out.push(u);
789                for &v in &self.adj[u] {
790                    if !vis[v] {
791                        stk.push(v);
792                    }
793                }
794            }
795        }
796        out
797    }
798    #[allow(dead_code)]
799    pub fn scc(&self) -> Vec<Vec<usize>> {
800        let mut visited = vec![false; self.n];
801        let mut order = Vec::new();
802        for i in 0..self.n {
803            if !visited[i] {
804                let mut stk = vec![(i, 0usize)];
805                while let Some((u, idx)) = stk.last_mut() {
806                    if !visited[*u] {
807                        visited[*u] = true;
808                    }
809                    if *idx < self.adj[*u].len() {
810                        let v = self.adj[*u][*idx];
811                        *idx += 1;
812                        if !visited[v] {
813                            stk.push((v, 0));
814                        }
815                    } else {
816                        order.push(*u);
817                        stk.pop();
818                    }
819                }
820            }
821        }
822        let mut comp = vec![usize::MAX; self.n];
823        let mut components: Vec<Vec<usize>> = Vec::new();
824        for &start in order.iter().rev() {
825            if comp[start] == usize::MAX {
826                let cid = components.len();
827                let mut component = Vec::new();
828                let mut stk = vec![start];
829                while let Some(u) = stk.pop() {
830                    if comp[u] == usize::MAX {
831                        comp[u] = cid;
832                        component.push(u);
833                        for &v in &self.rev[u] {
834                            if comp[v] == usize::MAX {
835                                stk.push(v);
836                            }
837                        }
838                    }
839                }
840                components.push(component);
841            }
842        }
843        components
844    }
845    #[allow(dead_code)]
846    pub fn node_count(&self) -> usize {
847        self.n
848    }
849    #[allow(dead_code)]
850    pub fn edge_count(&self) -> usize {
851        self.edge_count
852    }
853}
854/// Pass execution phase for L4Ext.
855#[allow(dead_code)]
856#[derive(Debug, Clone, PartialEq, Eq, Hash)]
857pub enum L4ExtPassPhase {
858    Early,
859    Middle,
860    Late,
861    Finalize,
862}
863impl L4ExtPassPhase {
864    #[allow(dead_code)]
865    pub fn is_early(&self) -> bool {
866        matches!(self, Self::Early)
867    }
868    #[allow(dead_code)]
869    pub fn is_middle(&self) -> bool {
870        matches!(self, Self::Middle)
871    }
872    #[allow(dead_code)]
873    pub fn is_late(&self) -> bool {
874        matches!(self, Self::Late)
875    }
876    #[allow(dead_code)]
877    pub fn is_finalize(&self) -> bool {
878        matches!(self, Self::Finalize)
879    }
880    #[allow(dead_code)]
881    pub fn order(&self) -> u32 {
882        match self {
883            Self::Early => 0,
884            Self::Middle => 1,
885            Self::Late => 2,
886            Self::Finalize => 3,
887        }
888    }
889    #[allow(dead_code)]
890    pub fn from_order(n: u32) -> Option<Self> {
891        match n {
892            0 => Some(Self::Early),
893            1 => Some(Self::Middle),
894            2 => Some(Self::Late),
895            3 => Some(Self::Finalize),
896            _ => None,
897        }
898    }
899}
900#[allow(dead_code)]
901#[derive(Debug, Clone)]
902pub struct L4CacheEntry {
903    pub key: String,
904    pub data: Vec<u8>,
905    pub timestamp: u64,
906    pub valid: bool,
907}
908#[allow(dead_code)]
909#[derive(Debug, Clone, PartialEq)]
910pub enum L4PassPhase {
911    Analysis,
912    Transformation,
913    Verification,
914    Cleanup,
915}
916impl L4PassPhase {
917    #[allow(dead_code)]
918    pub fn name(&self) -> &str {
919        match self {
920            L4PassPhase::Analysis => "analysis",
921            L4PassPhase::Transformation => "transformation",
922            L4PassPhase::Verification => "verification",
923            L4PassPhase::Cleanup => "cleanup",
924        }
925    }
926    #[allow(dead_code)]
927    pub fn is_modifying(&self) -> bool {
928        matches!(self, L4PassPhase::Transformation | L4PassPhase::Cleanup)
929    }
930}
931/// Pattern in a `match` expression.
932#[derive(Debug, Clone, PartialEq, Eq, Hash)]
933pub enum Lean4Pattern {
934    /// Wildcard: `_`
935    Wildcard,
936    /// Variable binding: `x`
937    Var(std::string::String),
938    /// Constructor pattern: `.some x`, `List.cons h t`
939    Ctor(std::string::String, Vec<Lean4Pattern>),
940    /// Tuple pattern: `(a, b)`
941    Tuple(Vec<Lean4Pattern>),
942    /// Literal pattern: `0`, `true`, `"hello"`
943    Lit(std::string::String),
944    /// Or pattern: `p | q`
945    Or(Box<Lean4Pattern>, Box<Lean4Pattern>),
946    /// Anonymous constructor: `⟨a, b⟩`
947    Anonymous(Vec<Lean4Pattern>),
948}
949/// A Lean 4 `theorem` declaration.
950#[derive(Debug, Clone)]
951pub struct Lean4Theorem {
952    /// Name of the theorem
953    pub name: std::string::String,
954    /// Universe type parameters
955    pub type_params: Vec<(std::string::String, std::string::String)>,
956    /// Hypotheses/arguments: `(h : P)`
957    pub args: Vec<(std::string::String, Lean4Type)>,
958    /// The proposition type
959    pub ty: Lean4Type,
960    /// The proof term or tactic block
961    pub proof: Lean4Expr,
962    /// Optional doc comment
963    pub doc_comment: Option<std::string::String>,
964}
965impl Lean4Theorem {
966    /// Create a theorem with a tactic proof.
967    pub fn tactic(
968        name: impl Into<std::string::String>,
969        args: Vec<(std::string::String, Lean4Type)>,
970        ty: Lean4Type,
971        tactics: Vec<std::string::String>,
972    ) -> Self {
973        Lean4Theorem {
974            name: name.into(),
975            type_params: vec![],
976            args,
977            ty,
978            proof: Lean4Expr::ByTactic(tactics),
979            doc_comment: None,
980        }
981    }
982    /// Create a theorem with a term-mode proof.
983    pub fn term_mode(
984        name: impl Into<std::string::String>,
985        args: Vec<(std::string::String, Lean4Type)>,
986        ty: Lean4Type,
987        proof: Lean4Expr,
988    ) -> Self {
989        Lean4Theorem {
990            name: name.into(),
991            type_params: vec![],
992            args,
993            ty,
994            proof,
995            doc_comment: None,
996        }
997    }
998    /// Emit the theorem as Lean 4 source.
999    pub fn emit(&self) -> std::string::String {
1000        let mut out = std::string::String::new();
1001        if let Some(doc) = &self.doc_comment {
1002            out.push_str(&format!("/-- {} -/\n", doc));
1003        }
1004        out.push_str("theorem ");
1005        out.push_str(&self.name);
1006        for (name, ty) in &self.args {
1007            out.push_str(&format!(" ({} : {})", name, ty));
1008        }
1009        out.push_str(&format!(" : {} :=\n  {}\n", self.ty, self.proof));
1010        out
1011    }
1012}
1013#[allow(dead_code)]
1014#[derive(Debug, Clone)]
1015pub struct L4AnalysisCache {
1016    pub(super) entries: std::collections::HashMap<String, L4CacheEntry>,
1017    pub(super) max_size: usize,
1018    pub(super) hits: u64,
1019    pub(super) misses: u64,
1020}
1021impl L4AnalysisCache {
1022    #[allow(dead_code)]
1023    pub fn new(max_size: usize) -> Self {
1024        L4AnalysisCache {
1025            entries: std::collections::HashMap::new(),
1026            max_size,
1027            hits: 0,
1028            misses: 0,
1029        }
1030    }
1031    #[allow(dead_code)]
1032    pub fn get(&mut self, key: &str) -> Option<&L4CacheEntry> {
1033        if self.entries.contains_key(key) {
1034            self.hits += 1;
1035            self.entries.get(key)
1036        } else {
1037            self.misses += 1;
1038            None
1039        }
1040    }
1041    #[allow(dead_code)]
1042    pub fn insert(&mut self, key: String, data: Vec<u8>) {
1043        if self.entries.len() >= self.max_size {
1044            if let Some(oldest) = self.entries.keys().next().cloned() {
1045                self.entries.remove(&oldest);
1046            }
1047        }
1048        self.entries.insert(
1049            key.clone(),
1050            L4CacheEntry {
1051                key,
1052                data,
1053                timestamp: 0,
1054                valid: true,
1055            },
1056        );
1057    }
1058    #[allow(dead_code)]
1059    pub fn invalidate(&mut self, key: &str) {
1060        if let Some(entry) = self.entries.get_mut(key) {
1061            entry.valid = false;
1062        }
1063    }
1064    #[allow(dead_code)]
1065    pub fn clear(&mut self) {
1066        self.entries.clear();
1067    }
1068    #[allow(dead_code)]
1069    pub fn hit_rate(&self) -> f64 {
1070        let total = self.hits + self.misses;
1071        if total == 0 {
1072            return 0.0;
1073        }
1074        self.hits as f64 / total as f64
1075    }
1076    #[allow(dead_code)]
1077    pub fn size(&self) -> usize {
1078        self.entries.len()
1079    }
1080}
1081/// A Lean 4 `inductive` type declaration.
1082#[derive(Debug, Clone)]
1083pub struct Lean4Inductive {
1084    /// Type name
1085    pub name: std::string::String,
1086    /// Type parameters: `(α : Type u)`
1087    pub type_params: Vec<(std::string::String, Lean4Type)>,
1088    /// Index types (GADTs)
1089    pub indices: Vec<Lean4Type>,
1090    /// Constructors
1091    pub constructors: Vec<Lean4Constructor>,
1092    /// Derives (e.g., `Repr`, `DecidableEq`, `BEq`)
1093    pub derives: Vec<std::string::String>,
1094    /// Optional doc comment
1095    pub doc_comment: Option<std::string::String>,
1096}
1097impl Lean4Inductive {
1098    /// Create a simple inductive type.
1099    pub fn simple(
1100        name: impl Into<std::string::String>,
1101        constructors: Vec<Lean4Constructor>,
1102    ) -> Self {
1103        Lean4Inductive {
1104            name: name.into(),
1105            type_params: vec![],
1106            indices: vec![],
1107            constructors,
1108            derives: vec![],
1109            doc_comment: None,
1110        }
1111    }
1112    /// Emit the inductive as Lean 4 source.
1113    pub fn emit(&self) -> std::string::String {
1114        let mut out = std::string::String::new();
1115        if let Some(doc) = &self.doc_comment {
1116            out.push_str(&format!("/-- {} -/\n", doc));
1117        }
1118        out.push_str("inductive ");
1119        out.push_str(&self.name);
1120        for (pname, pty) in &self.type_params {
1121            out.push_str(&format!(" ({} : {})", pname, pty));
1122        }
1123        if !self.indices.is_empty() {
1124            out.push_str(" : ");
1125            for idx in &self.indices {
1126                out.push_str(&format!("{} → ", idx));
1127            }
1128            out.push_str("Type");
1129        }
1130        out.push_str(" where\n");
1131        for ctor in &self.constructors {
1132            out.push_str(&ctor.emit(&self.name));
1133            out.push('\n');
1134        }
1135        if !self.derives.is_empty() {
1136            out.push_str(&format!("  deriving {}\n", self.derives.join(", ")));
1137        }
1138        out
1139    }
1140}
1141/// A Lean 4 `structure` declaration.
1142#[derive(Debug, Clone)]
1143pub struct Lean4Structure {
1144    /// Structure name
1145    pub name: std::string::String,
1146    /// Type parameters
1147    pub type_params: Vec<(std::string::String, Lean4Type)>,
1148    /// Parent structures (extends)
1149    pub extends: Vec<std::string::String>,
1150    /// Fields: (name, type, default_value)
1151    pub fields: Vec<(std::string::String, Lean4Type, Option<Lean4Expr>)>,
1152    /// Optional doc comment
1153    pub doc_comment: Option<std::string::String>,
1154    /// Derives
1155    pub derives: Vec<std::string::String>,
1156}
1157impl Lean4Structure {
1158    /// Create a simple structure.
1159    pub fn simple(
1160        name: impl Into<std::string::String>,
1161        fields: Vec<(std::string::String, Lean4Type)>,
1162    ) -> Self {
1163        Lean4Structure {
1164            name: name.into(),
1165            type_params: vec![],
1166            extends: vec![],
1167            fields: fields.into_iter().map(|(n, t)| (n, t, None)).collect(),
1168            doc_comment: None,
1169            derives: vec![],
1170        }
1171    }
1172    /// Emit the structure as Lean 4 source.
1173    pub fn emit(&self) -> std::string::String {
1174        let mut out = std::string::String::new();
1175        if let Some(doc) = &self.doc_comment {
1176            out.push_str(&format!("/-- {} -/\n", doc));
1177        }
1178        out.push_str("structure ");
1179        out.push_str(&self.name);
1180        for (pname, pty) in &self.type_params {
1181            out.push_str(&format!(" ({} : {})", pname, pty));
1182        }
1183        if !self.extends.is_empty() {
1184            out.push_str(&format!(" extends {}", self.extends.join(", ")));
1185        }
1186        out.push_str(" where\n");
1187        for (fname, fty, default) in &self.fields {
1188            if let Some(def_val) = default {
1189                out.push_str(&format!("  {} : {} := {}\n", fname, fty, def_val));
1190            } else {
1191                out.push_str(&format!("  {} : {}\n", fname, fty));
1192            }
1193        }
1194        if !self.derives.is_empty() {
1195            out.push_str(&format!("  deriving {}\n", self.derives.join(", ")));
1196        }
1197        out
1198    }
1199}
1200#[allow(dead_code)]
1201#[derive(Debug, Clone)]
1202pub struct L4Worklist {
1203    pub(super) items: std::collections::VecDeque<u32>,
1204    pub(super) in_worklist: std::collections::HashSet<u32>,
1205}
1206impl L4Worklist {
1207    #[allow(dead_code)]
1208    pub fn new() -> Self {
1209        L4Worklist {
1210            items: std::collections::VecDeque::new(),
1211            in_worklist: std::collections::HashSet::new(),
1212        }
1213    }
1214    #[allow(dead_code)]
1215    pub fn push(&mut self, item: u32) -> bool {
1216        if self.in_worklist.insert(item) {
1217            self.items.push_back(item);
1218            true
1219        } else {
1220            false
1221        }
1222    }
1223    #[allow(dead_code)]
1224    pub fn pop(&mut self) -> Option<u32> {
1225        let item = self.items.pop_front()?;
1226        self.in_worklist.remove(&item);
1227        Some(item)
1228    }
1229    #[allow(dead_code)]
1230    pub fn is_empty(&self) -> bool {
1231        self.items.is_empty()
1232    }
1233    #[allow(dead_code)]
1234    pub fn len(&self) -> usize {
1235        self.items.len()
1236    }
1237    #[allow(dead_code)]
1238    pub fn contains(&self, item: u32) -> bool {
1239        self.in_worklist.contains(&item)
1240    }
1241}
1242#[allow(dead_code)]
1243#[derive(Debug, Clone)]
1244pub struct L4DominatorTree {
1245    pub idom: Vec<Option<u32>>,
1246    pub dom_children: Vec<Vec<u32>>,
1247    pub dom_depth: Vec<u32>,
1248}
1249impl L4DominatorTree {
1250    #[allow(dead_code)]
1251    pub fn new(size: usize) -> Self {
1252        L4DominatorTree {
1253            idom: vec![None; size],
1254            dom_children: vec![Vec::new(); size],
1255            dom_depth: vec![0; size],
1256        }
1257    }
1258    #[allow(dead_code)]
1259    pub fn set_idom(&mut self, node: usize, idom: u32) {
1260        self.idom[node] = Some(idom);
1261    }
1262    #[allow(dead_code)]
1263    pub fn dominates(&self, a: usize, b: usize) -> bool {
1264        if a == b {
1265            return true;
1266        }
1267        let mut cur = b;
1268        loop {
1269            match self.idom[cur] {
1270                Some(parent) if parent as usize == a => return true,
1271                Some(parent) if parent as usize == cur => return false,
1272                Some(parent) => cur = parent as usize,
1273                None => return false,
1274            }
1275        }
1276    }
1277    #[allow(dead_code)]
1278    pub fn depth(&self, node: usize) -> u32 {
1279        self.dom_depth.get(node).copied().unwrap_or(0)
1280    }
1281}
1282/// A Lean 4 `abbrev` declaration (transparent definition).
1283#[derive(Debug, Clone)]
1284pub struct Lean4Abbrev {
1285    /// Name
1286    pub name: std::string::String,
1287    /// Arguments
1288    pub args: Vec<(std::string::String, Lean4Type)>,
1289    /// Return type
1290    pub ty: Option<Lean4Type>,
1291    /// Body
1292    pub body: Lean4Expr,
1293}
1294impl Lean4Abbrev {
1295    /// Emit the abbrev as Lean 4 source.
1296    pub fn emit(&self) -> std::string::String {
1297        let mut out = std::string::String::new();
1298        out.push_str("abbrev ");
1299        out.push_str(&self.name);
1300        for (aname, aty) in &self.args {
1301            out.push_str(&format!(" ({} : {})", aname, aty));
1302        }
1303        if let Some(ty) = &self.ty {
1304            out.push_str(&format!(" : {}", ty));
1305        }
1306        out.push_str(&format!(" := {}\n", self.body));
1307        out
1308    }
1309}
1310#[allow(dead_code)]
1311pub struct L4ConstantFoldingHelper;
1312impl L4ConstantFoldingHelper {
1313    #[allow(dead_code)]
1314    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1315        a.checked_add(b)
1316    }
1317    #[allow(dead_code)]
1318    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1319        a.checked_sub(b)
1320    }
1321    #[allow(dead_code)]
1322    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1323        a.checked_mul(b)
1324    }
1325    #[allow(dead_code)]
1326    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1327        if b == 0 {
1328            None
1329        } else {
1330            a.checked_div(b)
1331        }
1332    }
1333    #[allow(dead_code)]
1334    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1335        a + b
1336    }
1337    #[allow(dead_code)]
1338    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1339        a * b
1340    }
1341    #[allow(dead_code)]
1342    pub fn fold_neg_i64(a: i64) -> Option<i64> {
1343        a.checked_neg()
1344    }
1345    #[allow(dead_code)]
1346    pub fn fold_not_bool(a: bool) -> bool {
1347        !a
1348    }
1349    #[allow(dead_code)]
1350    pub fn fold_and_bool(a: bool, b: bool) -> bool {
1351        a && b
1352    }
1353    #[allow(dead_code)]
1354    pub fn fold_or_bool(a: bool, b: bool) -> bool {
1355        a || b
1356    }
1357    #[allow(dead_code)]
1358    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1359        a.checked_shl(b)
1360    }
1361    #[allow(dead_code)]
1362    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1363        a.checked_shr(b)
1364    }
1365    #[allow(dead_code)]
1366    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1367        if b == 0 {
1368            None
1369        } else {
1370            Some(a % b)
1371        }
1372    }
1373    #[allow(dead_code)]
1374    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1375        a & b
1376    }
1377    #[allow(dead_code)]
1378    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1379        a | b
1380    }
1381    #[allow(dead_code)]
1382    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1383        a ^ b
1384    }
1385    #[allow(dead_code)]
1386    pub fn fold_bitnot_i64(a: i64) -> i64 {
1387        !a
1388    }
1389}
1390/// Liveness analysis for L4Ext.
1391#[allow(dead_code)]
1392#[derive(Debug, Clone, Default)]
1393pub struct L4ExtLiveness {
1394    pub live_in: Vec<Vec<usize>>,
1395    pub live_out: Vec<Vec<usize>>,
1396    pub defs: Vec<Vec<usize>>,
1397    pub uses: Vec<Vec<usize>>,
1398}
1399impl L4ExtLiveness {
1400    #[allow(dead_code)]
1401    pub fn new(n: usize) -> Self {
1402        Self {
1403            live_in: vec![Vec::new(); n],
1404            live_out: vec![Vec::new(); n],
1405            defs: vec![Vec::new(); n],
1406            uses: vec![Vec::new(); n],
1407        }
1408    }
1409    #[allow(dead_code)]
1410    pub fn live_in(&self, b: usize, v: usize) -> bool {
1411        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1412    }
1413    #[allow(dead_code)]
1414    pub fn live_out(&self, b: usize, v: usize) -> bool {
1415        self.live_out
1416            .get(b)
1417            .map(|s| s.contains(&v))
1418            .unwrap_or(false)
1419    }
1420    #[allow(dead_code)]
1421    pub fn add_def(&mut self, b: usize, v: usize) {
1422        if let Some(s) = self.defs.get_mut(b) {
1423            if !s.contains(&v) {
1424                s.push(v);
1425            }
1426        }
1427    }
1428    #[allow(dead_code)]
1429    pub fn add_use(&mut self, b: usize, v: usize) {
1430        if let Some(s) = self.uses.get_mut(b) {
1431            if !s.contains(&v) {
1432                s.push(v);
1433            }
1434        }
1435    }
1436    #[allow(dead_code)]
1437    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1438        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1439    }
1440    #[allow(dead_code)]
1441    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1442        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1443    }
1444}
1445/// Pass registry for L4Ext.
1446#[allow(dead_code)]
1447#[derive(Debug, Default)]
1448pub struct L4ExtPassRegistry {
1449    pub(super) configs: Vec<L4ExtPassConfig>,
1450    pub(super) stats: Vec<L4ExtPassStats>,
1451}
1452impl L4ExtPassRegistry {
1453    #[allow(dead_code)]
1454    pub fn new() -> Self {
1455        Self::default()
1456    }
1457    #[allow(dead_code)]
1458    pub fn register(&mut self, c: L4ExtPassConfig) {
1459        self.stats.push(L4ExtPassStats::new());
1460        self.configs.push(c);
1461    }
1462    #[allow(dead_code)]
1463    pub fn len(&self) -> usize {
1464        self.configs.len()
1465    }
1466    #[allow(dead_code)]
1467    pub fn is_empty(&self) -> bool {
1468        self.configs.is_empty()
1469    }
1470    #[allow(dead_code)]
1471    pub fn get(&self, i: usize) -> Option<&L4ExtPassConfig> {
1472        self.configs.get(i)
1473    }
1474    #[allow(dead_code)]
1475    pub fn get_stats(&self, i: usize) -> Option<&L4ExtPassStats> {
1476        self.stats.get(i)
1477    }
1478    #[allow(dead_code)]
1479    pub fn enabled_passes(&self) -> Vec<&L4ExtPassConfig> {
1480        self.configs.iter().filter(|c| c.enabled).collect()
1481    }
1482    #[allow(dead_code)]
1483    pub fn passes_in_phase(&self, ph: &L4ExtPassPhase) -> Vec<&L4ExtPassConfig> {
1484        self.configs
1485            .iter()
1486            .filter(|c| c.enabled && &c.phase == ph)
1487            .collect()
1488    }
1489    #[allow(dead_code)]
1490    pub fn total_nodes_visited(&self) -> usize {
1491        self.stats.iter().map(|s| s.nodes_visited).sum()
1492    }
1493    #[allow(dead_code)]
1494    pub fn any_changed(&self) -> bool {
1495        self.stats.iter().any(|s| s.changed)
1496    }
1497}
1498/// Statistics for L4Ext passes.
1499#[allow(dead_code)]
1500#[derive(Debug, Clone, Default)]
1501pub struct L4ExtPassStats {
1502    pub iterations: usize,
1503    pub changed: bool,
1504    pub nodes_visited: usize,
1505    pub nodes_modified: usize,
1506    pub time_ms: u64,
1507    pub memory_bytes: usize,
1508    pub errors: usize,
1509}
1510impl L4ExtPassStats {
1511    #[allow(dead_code)]
1512    pub fn new() -> Self {
1513        Self::default()
1514    }
1515    #[allow(dead_code)]
1516    pub fn visit(&mut self) {
1517        self.nodes_visited += 1;
1518    }
1519    #[allow(dead_code)]
1520    pub fn modify(&mut self) {
1521        self.nodes_modified += 1;
1522        self.changed = true;
1523    }
1524    #[allow(dead_code)]
1525    pub fn iterate(&mut self) {
1526        self.iterations += 1;
1527    }
1528    #[allow(dead_code)]
1529    pub fn error(&mut self) {
1530        self.errors += 1;
1531    }
1532    #[allow(dead_code)]
1533    pub fn efficiency(&self) -> f64 {
1534        if self.nodes_visited == 0 {
1535            0.0
1536        } else {
1537            self.nodes_modified as f64 / self.nodes_visited as f64
1538        }
1539    }
1540    #[allow(dead_code)]
1541    pub fn merge(&mut self, o: &L4ExtPassStats) {
1542        self.iterations += o.iterations;
1543        self.changed |= o.changed;
1544        self.nodes_visited += o.nodes_visited;
1545        self.nodes_modified += o.nodes_modified;
1546        self.time_ms += o.time_ms;
1547        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1548        self.errors += o.errors;
1549    }
1550}
1551/// Constant folding helper for L4Ext.
1552#[allow(dead_code)]
1553#[derive(Debug, Clone, Default)]
1554pub struct L4ExtConstFolder {
1555    pub(super) folds: usize,
1556    pub(super) failures: usize,
1557    pub(super) enabled: bool,
1558}
1559impl L4ExtConstFolder {
1560    #[allow(dead_code)]
1561    pub fn new() -> Self {
1562        Self {
1563            folds: 0,
1564            failures: 0,
1565            enabled: true,
1566        }
1567    }
1568    #[allow(dead_code)]
1569    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1570        self.folds += 1;
1571        a.checked_add(b)
1572    }
1573    #[allow(dead_code)]
1574    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1575        self.folds += 1;
1576        a.checked_sub(b)
1577    }
1578    #[allow(dead_code)]
1579    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1580        self.folds += 1;
1581        a.checked_mul(b)
1582    }
1583    #[allow(dead_code)]
1584    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1585        if b == 0 {
1586            self.failures += 1;
1587            None
1588        } else {
1589            self.folds += 1;
1590            a.checked_div(b)
1591        }
1592    }
1593    #[allow(dead_code)]
1594    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1595        if b == 0 {
1596            self.failures += 1;
1597            None
1598        } else {
1599            self.folds += 1;
1600            a.checked_rem(b)
1601        }
1602    }
1603    #[allow(dead_code)]
1604    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
1605        self.folds += 1;
1606        a.checked_neg()
1607    }
1608    #[allow(dead_code)]
1609    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1610        if s >= 64 {
1611            self.failures += 1;
1612            None
1613        } else {
1614            self.folds += 1;
1615            a.checked_shl(s)
1616        }
1617    }
1618    #[allow(dead_code)]
1619    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1620        if s >= 64 {
1621            self.failures += 1;
1622            None
1623        } else {
1624            self.folds += 1;
1625            a.checked_shr(s)
1626        }
1627    }
1628    #[allow(dead_code)]
1629    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
1630        self.folds += 1;
1631        a & b
1632    }
1633    #[allow(dead_code)]
1634    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
1635        self.folds += 1;
1636        a | b
1637    }
1638    #[allow(dead_code)]
1639    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
1640        self.folds += 1;
1641        a ^ b
1642    }
1643    #[allow(dead_code)]
1644    pub fn not_i64(&mut self, a: i64) -> i64 {
1645        self.folds += 1;
1646        !a
1647    }
1648    #[allow(dead_code)]
1649    pub fn fold_count(&self) -> usize {
1650        self.folds
1651    }
1652    #[allow(dead_code)]
1653    pub fn failure_count(&self) -> usize {
1654        self.failures
1655    }
1656    #[allow(dead_code)]
1657    pub fn enable(&mut self) {
1658        self.enabled = true;
1659    }
1660    #[allow(dead_code)]
1661    pub fn disable(&mut self) {
1662        self.enabled = false;
1663    }
1664    #[allow(dead_code)]
1665    pub fn is_enabled(&self) -> bool {
1666        self.enabled
1667    }
1668}
1669/// A Lean 4 `def` definition.
1670#[derive(Debug, Clone)]
1671pub struct Lean4Def {
1672    /// Name of the definition
1673    pub name: std::string::String,
1674    /// Universe-polymorphic type parameters: `{u v : Type}`
1675    pub type_params: Vec<(std::string::String, std::string::String)>,
1676    /// Term-level arguments: `(x : Nat) (y : Bool)`
1677    pub args: Vec<(std::string::String, Lean4Type)>,
1678    /// Return type ascription
1679    pub type_ascription: Option<Lean4Type>,
1680    /// Body expression
1681    pub body: Lean4Expr,
1682    /// Optional doc comment
1683    pub doc_comment: Option<std::string::String>,
1684    /// Lean attributes: `@[simp]`, `@[inline]`, etc.
1685    pub attributes: Vec<std::string::String>,
1686    /// `noncomputable`
1687    pub is_noncomputable: bool,
1688    /// `private`
1689    pub is_private: bool,
1690}
1691impl Lean4Def {
1692    /// Create a simple definition.
1693    pub fn simple(
1694        name: impl Into<std::string::String>,
1695        args: Vec<(std::string::String, Lean4Type)>,
1696        ret_ty: Lean4Type,
1697        body: Lean4Expr,
1698    ) -> Self {
1699        Lean4Def {
1700            name: name.into(),
1701            type_params: vec![],
1702            args,
1703            type_ascription: Some(ret_ty),
1704            body,
1705            doc_comment: None,
1706            attributes: vec![],
1707            is_noncomputable: false,
1708            is_private: false,
1709        }
1710    }
1711    /// Emit the definition as Lean 4 source.
1712    pub fn emit(&self) -> std::string::String {
1713        let mut out = std::string::String::new();
1714        if let Some(doc) = &self.doc_comment {
1715            out.push_str(&format!("/-- {} -/\n", doc));
1716        }
1717        for attr in &self.attributes {
1718            out.push_str(&format!("@[{}]\n", attr));
1719        }
1720        if self.is_noncomputable {
1721            out.push_str("noncomputable ");
1722        }
1723        if self.is_private {
1724            out.push_str("private ");
1725        }
1726        out.push_str("def ");
1727        out.push_str(&self.name);
1728        if !self.type_params.is_empty() {
1729            out.push_str(".{");
1730            for (i, (n, _k)) in self.type_params.iter().enumerate() {
1731                if i > 0 {
1732                    out.push_str(", ");
1733                }
1734                out.push_str(n);
1735            }
1736            out.push('}');
1737        }
1738        for (name, ty) in &self.args {
1739            out.push_str(&format!(" ({} : {})", name, ty));
1740        }
1741        if let Some(ty) = &self.type_ascription {
1742            out.push_str(&format!(" : {}", ty));
1743        }
1744        out.push_str(&format!(" :=\n  {}\n", self.body));
1745        out
1746    }
1747}
1748/// One step in a `calc` proof.
1749#[derive(Debug, Clone, PartialEq)]
1750pub struct Lean4CalcStep {
1751    /// Left-hand side expression
1752    pub lhs: Lean4Expr,
1753    /// Relation: `=`, `≤`, `<`, `≥`, `>`, etc.
1754    pub relation: std::string::String,
1755    /// Right-hand side expression
1756    pub rhs: Lean4Expr,
1757    /// Justification: `by ...` or a term
1758    pub justification: Lean4Expr,
1759}
1760/// Lean 4 expression representation.
1761#[derive(Debug, Clone, PartialEq)]
1762pub enum Lean4Expr {
1763    /// Variable reference: `x`
1764    Var(std::string::String),
1765    /// Natural number literal: `42`
1766    NatLit(u64),
1767    /// Integer literal: `-5`
1768    IntLit(i64),
1769    /// Boolean literal: `true` / `false`
1770    BoolLit(bool),
1771    /// String literal: `"hello"`
1772    StrLit(std::string::String),
1773    /// Float literal: `3.14`
1774    FloatLit(f64),
1775    /// Hole / sorry placeholder: `_`
1776    Hole,
1777    /// `sorry`
1778    Sorry,
1779    /// Panic: `panic! "message"`
1780    Panic(std::string::String),
1781    /// Function application: `f x`
1782    App(Box<Lean4Expr>, Box<Lean4Expr>),
1783    /// Lambda: `fun x => body`
1784    Lambda(std::string::String, Option<Box<Lean4Type>>, Box<Lean4Expr>),
1785    /// Dependent function type (Pi): `(x : α) → β x`
1786    Pi(std::string::String, Box<Lean4Type>, Box<Lean4Expr>),
1787    /// Let binding: `let x := e; body`
1788    Let(
1789        std::string::String,
1790        Option<Box<Lean4Type>>,
1791        Box<Lean4Expr>,
1792        Box<Lean4Expr>,
1793    ),
1794    /// Recursive let: `let rec f := ...`
1795    LetRec(std::string::String, Box<Lean4Expr>, Box<Lean4Expr>),
1796    /// Pattern match: `match e with | p => b | ...`
1797    Match(Box<Lean4Expr>, Vec<(Lean4Pattern, Lean4Expr)>),
1798    /// If-then-else: `if c then t else e`
1799    If(Box<Lean4Expr>, Box<Lean4Expr>, Box<Lean4Expr>),
1800    /// Do notation block
1801    Do(Vec<Lean4DoStmt>),
1802    /// `have h : T := proof; rest`
1803    Have(
1804        std::string::String,
1805        Box<Lean4Type>,
1806        Box<Lean4Expr>,
1807        Box<Lean4Expr>,
1808    ),
1809    /// `show T from e`
1810    Show(Box<Lean4Type>, Box<Lean4Expr>),
1811    /// Calc block
1812    Calc(Vec<Lean4CalcStep>),
1813    /// Tactic block: `by tac1; tac2; ...`
1814    ByTactic(Vec<std::string::String>),
1815    /// Type ascription: `(e : T)`
1816    Ascription(Box<Lean4Expr>, Box<Lean4Type>),
1817    /// Tuple: `(a, b)`
1818    Tuple(Vec<Lean4Expr>),
1819    /// Anonymous constructor: `⟨a, b⟩`
1820    AnonymousCtor(Vec<Lean4Expr>),
1821    /// Projection: `e.1`, `e.field`
1822    Proj(Box<Lean4Expr>, std::string::String),
1823    /// Structure literal: `{ field := val, ... }`
1824    StructLit(std::string::String, Vec<(std::string::String, Lean4Expr)>),
1825}
1826#[allow(dead_code)]
1827#[derive(Debug, Clone)]
1828pub struct L4PassConfig {
1829    pub phase: L4PassPhase,
1830    pub enabled: bool,
1831    pub max_iterations: u32,
1832    pub debug_output: bool,
1833    pub pass_name: String,
1834}
1835impl L4PassConfig {
1836    #[allow(dead_code)]
1837    pub fn new(name: impl Into<String>, phase: L4PassPhase) -> Self {
1838        L4PassConfig {
1839            phase,
1840            enabled: true,
1841            max_iterations: 10,
1842            debug_output: false,
1843            pass_name: name.into(),
1844        }
1845    }
1846    #[allow(dead_code)]
1847    pub fn disabled(mut self) -> Self {
1848        self.enabled = false;
1849        self
1850    }
1851    #[allow(dead_code)]
1852    pub fn with_debug(mut self) -> Self {
1853        self.debug_output = true;
1854        self
1855    }
1856    #[allow(dead_code)]
1857    pub fn max_iter(mut self, n: u32) -> Self {
1858        self.max_iterations = n;
1859        self
1860    }
1861}
1862/// Dominator tree for L4Ext.
1863#[allow(dead_code)]
1864#[derive(Debug, Clone)]
1865pub struct L4ExtDomTree {
1866    pub(super) idom: Vec<Option<usize>>,
1867    pub(super) children: Vec<Vec<usize>>,
1868    pub(super) depth: Vec<usize>,
1869}
1870impl L4ExtDomTree {
1871    #[allow(dead_code)]
1872    pub fn new(n: usize) -> Self {
1873        Self {
1874            idom: vec![None; n],
1875            children: vec![Vec::new(); n],
1876            depth: vec![0; n],
1877        }
1878    }
1879    #[allow(dead_code)]
1880    pub fn set_idom(&mut self, node: usize, dom: usize) {
1881        if node < self.idom.len() {
1882            self.idom[node] = Some(dom);
1883            if dom < self.children.len() {
1884                self.children[dom].push(node);
1885            }
1886            self.depth[node] = if dom < self.depth.len() {
1887                self.depth[dom] + 1
1888            } else {
1889                1
1890            };
1891        }
1892    }
1893    #[allow(dead_code)]
1894    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
1895        if a == b {
1896            return true;
1897        }
1898        let n = self.idom.len();
1899        for _ in 0..n {
1900            match self.idom.get(b).copied().flatten() {
1901                None => return false,
1902                Some(p) if p == a => return true,
1903                Some(p) if p == b => return false,
1904                Some(p) => b = p,
1905            }
1906        }
1907        false
1908    }
1909    #[allow(dead_code)]
1910    pub fn children_of(&self, n: usize) -> &[usize] {
1911        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1912    }
1913    #[allow(dead_code)]
1914    pub fn depth_of(&self, n: usize) -> usize {
1915        self.depth.get(n).copied().unwrap_or(0)
1916    }
1917    #[allow(dead_code)]
1918    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
1919        let n = self.idom.len();
1920        for _ in 0..(2 * n) {
1921            if a == b {
1922                return a;
1923            }
1924            if self.depth_of(a) > self.depth_of(b) {
1925                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
1926            } else {
1927                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
1928            }
1929        }
1930        0
1931    }
1932}