1use std::collections::{HashMap, HashSet, VecDeque};
6
7pub struct Lean4Backend {
9 pub(super) file: Lean4File,
10}
11impl Lean4Backend {
12 pub fn new() -> Self {
14 Lean4Backend {
15 file: Lean4File::new(),
16 }
17 }
18 pub fn with_std_imports() -> Self {
20 let file = Lean4File::new().with_import("Init").with_import("Std");
21 Lean4Backend { file }
22 }
23 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 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 pub fn add_inductive(&mut self, ind: Lean4Inductive) {
47 self.file.add_decl(Lean4Decl::Inductive(ind));
48 }
49 pub fn emit_file(&self) -> std::string::String {
51 self.file.emit()
52 }
53 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
177pub enum Lean4Type {
178 Nat,
180 Int,
182 Float,
184 Bool,
186 String,
188 Unit,
190 Prop,
192 Type(u32),
194 List(Box<Lean4Type>),
196 Option(Box<Lean4Type>),
198 Prod(Box<Lean4Type>, Box<Lean4Type>),
200 Sum(Box<Lean4Type>, Box<Lean4Type>),
202 Fun(Box<Lean4Type>, Box<Lean4Type>),
204 Custom(std::string::String),
206 ForAll(std::string::String, Box<Lean4Type>, Box<Lean4Type>),
208 App(Box<Lean4Type>, Box<Lean4Type>),
210 IO(Box<Lean4Type>),
212 Array(Box<Lean4Type>),
214 Fin(Box<Lean4Type>),
216 Char,
218}
219#[derive(Debug, Clone)]
221pub enum Lean4Decl {
222 Def(Lean4Def),
224 Theorem(Lean4Theorem),
226 Axiom(Lean4Axiom),
228 Abbrev(Lean4Abbrev),
230 Structure(Lean4Structure),
232 Inductive(Lean4Inductive),
234 Instance(Lean4Instance),
236 Check(Lean4Expr),
238 Eval(Lean4Expr),
240 Raw(std::string::String),
242 Section(std::string::String, Vec<Lean4Decl>),
244 Namespace(std::string::String, Vec<Lean4Decl>),
246}
247impl Lean4Decl {
248 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#[derive(Debug, Clone)]
284pub struct Lean4File {
285 pub module_doc: Option<std::string::String>,
287 pub imports: Vec<std::string::String>,
289 pub opens: Vec<std::string::String>,
291 pub declarations: Vec<Lean4Decl>,
293}
294impl Lean4File {
295 pub fn new() -> Self {
297 Lean4File {
298 module_doc: None,
299 imports: vec![],
300 opens: vec![],
301 declarations: vec![],
302 }
303 }
304 pub fn with_import(mut self, import: impl Into<std::string::String>) -> Self {
306 self.imports.push(import.into());
307 self
308 }
309 pub fn with_open(mut self, open: impl Into<std::string::String>) -> Self {
311 self.opens.push(open.into());
312 self
313 }
314 pub fn add_decl(&mut self, decl: Lean4Decl) {
316 self.declarations.push(decl);
317 }
318 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#[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#[derive(Debug, Clone)]
406pub struct Lean4Constructor {
407 pub name: std::string::String,
409 pub fields: Vec<(Option<std::string::String>, Lean4Type)>,
411}
412impl Lean4Constructor {
413 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 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#[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#[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#[derive(Debug, Clone)]
603pub struct Lean4Instance {
604 pub name: Option<std::string::String>,
606 pub ty: Lean4Type,
608 pub args: Vec<(std::string::String, Lean4Type)>,
610 pub body: Lean4Expr,
612}
613impl Lean4Instance {
614 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#[derive(Debug, Clone, PartialEq)]
631pub enum Lean4DoStmt {
632 Bind(std::string::String, Option<Lean4Type>, Box<Lean4Expr>),
634 LetBind(std::string::String, Option<Lean4Type>, Box<Lean4Expr>),
636 Expr(Box<Lean4Expr>),
638 Return(Box<Lean4Expr>),
640 Pure(Box<Lean4Expr>),
642 If(Box<Lean4Expr>, Vec<Lean4DoStmt>, Vec<Lean4DoStmt>),
644}
645#[derive(Debug, Clone)]
647pub struct Lean4Axiom {
648 pub name: std::string::String,
650 pub args: Vec<(std::string::String, Lean4Type)>,
652 pub ty: Lean4Type,
654 pub doc_comment: Option<std::string::String>,
656}
657impl Lean4Axiom {
658 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#[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#[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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
933pub enum Lean4Pattern {
934 Wildcard,
936 Var(std::string::String),
938 Ctor(std::string::String, Vec<Lean4Pattern>),
940 Tuple(Vec<Lean4Pattern>),
942 Lit(std::string::String),
944 Or(Box<Lean4Pattern>, Box<Lean4Pattern>),
946 Anonymous(Vec<Lean4Pattern>),
948}
949#[derive(Debug, Clone)]
951pub struct Lean4Theorem {
952 pub name: std::string::String,
954 pub type_params: Vec<(std::string::String, std::string::String)>,
956 pub args: Vec<(std::string::String, Lean4Type)>,
958 pub ty: Lean4Type,
960 pub proof: Lean4Expr,
962 pub doc_comment: Option<std::string::String>,
964}
965impl Lean4Theorem {
966 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 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 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#[derive(Debug, Clone)]
1083pub struct Lean4Inductive {
1084 pub name: std::string::String,
1086 pub type_params: Vec<(std::string::String, Lean4Type)>,
1088 pub indices: Vec<Lean4Type>,
1090 pub constructors: Vec<Lean4Constructor>,
1092 pub derives: Vec<std::string::String>,
1094 pub doc_comment: Option<std::string::String>,
1096}
1097impl Lean4Inductive {
1098 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 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#[derive(Debug, Clone)]
1143pub struct Lean4Structure {
1144 pub name: std::string::String,
1146 pub type_params: Vec<(std::string::String, Lean4Type)>,
1148 pub extends: Vec<std::string::String>,
1150 pub fields: Vec<(std::string::String, Lean4Type, Option<Lean4Expr>)>,
1152 pub doc_comment: Option<std::string::String>,
1154 pub derives: Vec<std::string::String>,
1156}
1157impl Lean4Structure {
1158 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 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#[derive(Debug, Clone)]
1284pub struct Lean4Abbrev {
1285 pub name: std::string::String,
1287 pub args: Vec<(std::string::String, Lean4Type)>,
1289 pub ty: Option<Lean4Type>,
1291 pub body: Lean4Expr,
1293}
1294impl Lean4Abbrev {
1295 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#[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#[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#[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#[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#[derive(Debug, Clone)]
1671pub struct Lean4Def {
1672 pub name: std::string::String,
1674 pub type_params: Vec<(std::string::String, std::string::String)>,
1676 pub args: Vec<(std::string::String, Lean4Type)>,
1678 pub type_ascription: Option<Lean4Type>,
1680 pub body: Lean4Expr,
1682 pub doc_comment: Option<std::string::String>,
1684 pub attributes: Vec<std::string::String>,
1686 pub is_noncomputable: bool,
1688 pub is_private: bool,
1690}
1691impl Lean4Def {
1692 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 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#[derive(Debug, Clone, PartialEq)]
1750pub struct Lean4CalcStep {
1751 pub lhs: Lean4Expr,
1753 pub relation: std::string::String,
1755 pub rhs: Lean4Expr,
1757 pub justification: Lean4Expr,
1759}
1760#[derive(Debug, Clone, PartialEq)]
1762pub enum Lean4Expr {
1763 Var(std::string::String),
1765 NatLit(u64),
1767 IntLit(i64),
1769 BoolLit(bool),
1771 StrLit(std::string::String),
1773 FloatLit(f64),
1775 Hole,
1777 Sorry,
1779 Panic(std::string::String),
1781 App(Box<Lean4Expr>, Box<Lean4Expr>),
1783 Lambda(std::string::String, Option<Box<Lean4Type>>, Box<Lean4Expr>),
1785 Pi(std::string::String, Box<Lean4Type>, Box<Lean4Expr>),
1787 Let(
1789 std::string::String,
1790 Option<Box<Lean4Type>>,
1791 Box<Lean4Expr>,
1792 Box<Lean4Expr>,
1793 ),
1794 LetRec(std::string::String, Box<Lean4Expr>, Box<Lean4Expr>),
1796 Match(Box<Lean4Expr>, Vec<(Lean4Pattern, Lean4Expr)>),
1798 If(Box<Lean4Expr>, Box<Lean4Expr>, Box<Lean4Expr>),
1800 Do(Vec<Lean4DoStmt>),
1802 Have(
1804 std::string::String,
1805 Box<Lean4Type>,
1806 Box<Lean4Expr>,
1807 Box<Lean4Expr>,
1808 ),
1809 Show(Box<Lean4Type>, Box<Lean4Expr>),
1811 Calc(Vec<Lean4CalcStep>),
1813 ByTactic(Vec<std::string::String>),
1815 Ascription(Box<Lean4Expr>, Box<Lean4Type>),
1817 Tuple(Vec<Lean4Expr>),
1819 AnonymousCtor(Vec<Lean4Expr>),
1821 Proj(Box<Lean4Expr>, std::string::String),
1823 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#[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}