1use super::functions::*;
6use crate::{Environment, Expr, Level, Name};
7
8#[allow(dead_code)]
10pub struct ProjectionRewriteSet {
11 rules: Vec<ProjectionRewrite>,
12}
13#[allow(dead_code)]
14impl ProjectionRewriteSet {
15 pub fn new() -> Self {
17 ProjectionRewriteSet { rules: Vec::new() }
18 }
19 pub fn add(&mut self, rule: ProjectionRewrite) {
21 self.rules.push(rule);
22 }
23 pub fn find_by_projector(&self, projector: &str) -> Option<&ProjectionRewrite> {
25 self.rules.iter().find(|r| r.projector_name == projector)
26 }
27 pub fn rules_for_ctor(&self, ctor: &str) -> Vec<&ProjectionRewrite> {
29 self.rules.iter().filter(|r| r.ctor_name == ctor).collect()
30 }
31 pub fn len(&self) -> usize {
33 self.rules.len()
34 }
35 pub fn is_empty(&self) -> bool {
37 self.rules.is_empty()
38 }
39 pub fn projector_names(&self) -> Vec<&str> {
41 self.rules
42 .iter()
43 .map(|r| r.projector_name.as_str())
44 .collect()
45 }
46}
47#[allow(dead_code)]
49#[derive(Debug, Clone)]
50pub struct EtaExpanded {
51 pub ctor: String,
53 pub expr_id: u64,
55 pub field_ids: Vec<u64>,
57}
58#[allow(dead_code)]
59impl EtaExpanded {
60 pub fn new(ctor: impl Into<String>, expr_id: u64, field_ids: Vec<u64>) -> Self {
62 EtaExpanded {
63 ctor: ctor.into(),
64 expr_id,
65 field_ids,
66 }
67 }
68 pub fn arity(&self) -> usize {
70 self.field_ids.len()
71 }
72}
73#[allow(dead_code)]
75pub struct EtaRedexCollector {
76 redexes: Vec<EtaRedex>,
77 max_depth: usize,
78}
79#[allow(dead_code)]
80impl EtaRedexCollector {
81 pub fn new() -> Self {
83 EtaRedexCollector {
84 redexes: Vec::new(),
85 max_depth: usize::MAX,
86 }
87 }
88 pub fn with_max_depth(max_depth: usize) -> Self {
90 EtaRedexCollector {
91 redexes: Vec::new(),
92 max_depth,
93 }
94 }
95 pub fn add(&mut self, redex: EtaRedex) {
97 if redex.depth() <= self.max_depth {
98 self.redexes.push(redex);
99 }
100 }
101 pub fn redexes(&self) -> &[EtaRedex] {
103 &self.redexes
104 }
105 pub fn count(&self) -> usize {
107 self.redexes.len()
108 }
109 pub fn has_top_level(&self) -> bool {
111 self.redexes.iter().any(|r| r.is_top_level())
112 }
113 pub fn sorted_by_depth(&self) -> Vec<&EtaRedex> {
115 let mut v: Vec<&EtaRedex> = self.redexes.iter().collect();
116 v.sort_by_key(|r| r.depth());
117 v
118 }
119}
120pub struct SingletonKReducer<'a> {
131 env: &'a Environment,
132}
133impl<'a> SingletonKReducer<'a> {
134 pub fn new(env: &'a Environment) -> Self {
136 SingletonKReducer { env }
137 }
138 pub fn is_singleton_type(&self, ty: &Expr) -> bool {
141 let name = match head_const(ty) {
142 Some(n) => n,
143 None => return false,
144 };
145 let iv = match self.env.get_inductive_val(name) {
146 Some(v) => v,
147 None => return false,
148 };
149 if iv.ctors.len() != 1 {
150 return false;
151 }
152 let ctor_name = &iv.ctors[0];
153 match self.env.get_constructor_val(ctor_name) {
154 Some(cv) => cv.num_fields == 0,
155 None => false,
156 }
157 }
158 pub fn get_unique_ctor(&self, type_name: &Name) -> Option<Name> {
160 let iv = self.env.get_inductive_val(type_name)?;
161 if iv.ctors.len() != 1 {
162 return None;
163 }
164 let ctor_name = iv.ctors[0].clone();
165 let cv = self.env.get_constructor_val(&ctor_name)?;
166 if cv.num_fields == 0 {
167 Some(ctor_name)
168 } else {
169 None
170 }
171 }
172 pub fn k_reduce(&self, _expr: &Expr, ty: &Expr) -> Option<Expr> {
178 if !self.is_singleton_type(ty) {
179 return None;
180 }
181 let type_name = head_const(ty)?;
182 let ctor_name = self.get_unique_ctor(type_name)?;
183 Some(Expr::Const(ctor_name, vec![]))
184 }
185 pub fn apply_k_reduction(motive: &Expr, proof: &Expr) -> Option<Expr> {
191 let simplified = Expr::App(Box::new(motive.clone()), Box::new(proof.clone()));
192 Some(simplified)
193 }
194}
195#[allow(dead_code)]
196#[derive(Debug, Clone)]
197pub struct StructureDef {
198 pub name: String,
199 pub ctor_name: String,
200 pub fields: Vec<FieldDescriptor>,
201}
202#[allow(dead_code)]
204#[derive(Debug, Clone, Default)]
205pub struct EtaStats {
206 pub expansions: u64,
207 pub reductions: u64,
208 pub failed_expansions: u64,
209 pub failed_reductions: u64,
210}
211#[allow(dead_code)]
212impl EtaStats {
213 pub fn new() -> Self {
215 EtaStats::default()
216 }
217 pub fn record_expansion(&mut self) {
219 self.expansions += 1;
220 }
221 pub fn record_reduction(&mut self) {
223 self.reductions += 1;
224 }
225 pub fn record_failed_expansion(&mut self) {
227 self.failed_expansions += 1;
228 }
229 pub fn record_failed_reduction(&mut self) {
231 self.failed_reductions += 1;
232 }
233 pub fn expansion_rate(&self) -> f64 {
235 let total = self.expansions + self.failed_expansions;
236 if total == 0 {
237 1.0
238 } else {
239 self.expansions as f64 / total as f64
240 }
241 }
242 pub fn reduction_rate(&self) -> f64 {
244 let total = self.reductions + self.failed_reductions;
245 if total == 0 {
246 1.0
247 } else {
248 self.reductions as f64 / total as f64
249 }
250 }
251 pub fn summary(&self) -> String {
253 format!(
254 "expansions={} (fail={}) reductions={} (fail={})",
255 self.expansions, self.failed_expansions, self.reductions, self.failed_reductions
256 )
257 }
258}
259#[allow(dead_code)]
260#[derive(Debug, Clone, PartialEq, Eq)]
261enum EtaState {
262 Idle,
263 Expanding,
264 Done,
265 Failed,
266}
267#[allow(dead_code)]
269#[derive(Debug, Clone)]
270pub struct EtaUnificationHint {
271 pub lhs_id: u64,
272 pub rhs_id: u64,
273 pub suggested_struct: String,
274}
275#[allow(dead_code)]
276impl EtaUnificationHint {
277 pub fn new(lhs_id: u64, rhs_id: u64, suggested_struct: impl Into<String>) -> Self {
279 EtaUnificationHint {
280 lhs_id,
281 rhs_id,
282 suggested_struct: suggested_struct.into(),
283 }
284 }
285}
286#[allow(dead_code)]
288#[derive(Debug, Clone)]
289pub struct EtaReduction {
290 pub inner_id: u64,
292 pub ctor: String,
294 pub is_valid: bool,
296}
297#[allow(dead_code)]
298impl EtaReduction {
299 pub fn valid(inner_id: u64, ctor: impl Into<String>) -> Self {
301 EtaReduction {
302 inner_id,
303 ctor: ctor.into(),
304 is_valid: true,
305 }
306 }
307 pub fn invalid(ctor: impl Into<String>) -> Self {
309 EtaReduction {
310 inner_id: 0,
311 ctor: ctor.into(),
312 is_valid: false,
313 }
314 }
315}
316#[allow(dead_code)]
318#[derive(Debug, Clone, Default)]
319pub struct EtaNormRunSummary {
320 pub total_expressions: u64,
321 pub eta_expansions: u64,
322 pub eta_reductions: u64,
323 pub k_reductions: u64,
324 pub proj_rewrites: u64,
325 pub unchanged: u64,
326}
327#[allow(dead_code)]
328impl EtaNormRunSummary {
329 pub fn new() -> Self {
331 EtaNormRunSummary::default()
332 }
333 pub fn change_rate(&self) -> f64 {
335 if self.total_expressions == 0 {
336 return 0.0;
337 }
338 self.unchanged as f64 / self.total_expressions as f64
339 }
340 pub fn total_changes(&self) -> u64 {
342 self.eta_expansions + self.eta_reductions + self.k_reductions + self.proj_rewrites
343 }
344 pub fn format(&self) -> String {
346 format!(
347 "total={} expansions={} reductions={} k_red={} proj_rew={} unchanged={}",
348 self.total_expressions,
349 self.eta_expansions,
350 self.eta_reductions,
351 self.k_reductions,
352 self.proj_rewrites,
353 self.unchanged
354 )
355 }
356}
357#[allow(dead_code)]
359#[derive(Debug, Clone)]
360pub struct EtaPassConfig {
361 pub do_expand: bool,
362 pub do_reduce: bool,
363 pub do_k_reduce: bool,
364 pub do_proj_rewrite: bool,
365 pub max_passes: u32,
366 pub verbose: bool,
367}
368#[allow(dead_code)]
369impl EtaPassConfig {
370 pub fn default_config() -> Self {
372 EtaPassConfig {
373 do_expand: true,
374 do_reduce: true,
375 do_k_reduce: true,
376 do_proj_rewrite: true,
377 max_passes: 10,
378 verbose: false,
379 }
380 }
381 pub fn proj_only() -> Self {
383 EtaPassConfig {
384 do_expand: false,
385 do_reduce: false,
386 do_k_reduce: false,
387 do_proj_rewrite: true,
388 max_passes: 1,
389 verbose: false,
390 }
391 }
392 pub fn any_enabled(&self) -> bool {
394 self.do_expand || self.do_reduce || self.do_k_reduce || self.do_proj_rewrite
395 }
396}
397#[allow(dead_code)]
399pub struct EtaNormalFormChecker {
400 registry: StructureRegistry,
401}
402#[allow(dead_code)]
403impl EtaNormalFormChecker {
404 pub fn new(registry: StructureRegistry) -> Self {
406 EtaNormalFormChecker { registry }
407 }
408 pub fn knows_structure(&self, name: &str) -> bool {
410 self.registry.find(name).is_some()
411 }
412 pub fn expected_arity(&self, structure_name: &str) -> Option<usize> {
414 self.registry.find(structure_name).map(|s| s.fields.len())
415 }
416 pub fn check_expansion(&self, exp: &EtaExpanded) -> bool {
418 match self.expected_arity(&exp.ctor) {
419 Some(arity) => exp.field_ids.len() == arity,
420 None => false,
421 }
422 }
423}
424#[allow(dead_code)]
425#[derive(Debug, Clone, Copy, PartialEq, Eq)]
426pub enum EtaChangeKind {
427 Expanded,
428 Reduced,
429 KReduced,
430 ProjRewritten,
431}
432#[allow(dead_code)]
434#[derive(Debug, Clone, PartialEq, Eq)]
435pub enum CoherenceResult {
436 Coherent,
437 Incoherent { reason: String },
438 Unknown,
439}
440#[allow(dead_code)]
441impl CoherenceResult {
442 pub fn ok() -> Self {
444 CoherenceResult::Coherent
445 }
446 pub fn fail(reason: impl Into<String>) -> Self {
448 CoherenceResult::Incoherent {
449 reason: reason.into(),
450 }
451 }
452 pub fn is_coherent(&self) -> bool {
454 matches!(self, CoherenceResult::Coherent)
455 }
456}
457#[allow(dead_code)]
459pub struct EtaNormalizationPass {
460 pub stats: EtaStats,
461 worklist: Vec<u64>,
462}
463#[allow(dead_code)]
464impl EtaNormalizationPass {
465 pub fn new() -> Self {
467 EtaNormalizationPass {
468 stats: EtaStats::new(),
469 worklist: Vec::new(),
470 }
471 }
472 pub fn schedule(&mut self, id: u64) {
474 if !self.worklist.contains(&id) {
475 self.worklist.push(id);
476 }
477 }
478 #[allow(clippy::should_implement_trait)]
480 pub fn next(&mut self) -> Option<u64> {
481 if self.worklist.is_empty() {
482 None
483 } else {
484 Some(self.worklist.remove(0))
485 }
486 }
487 pub fn is_done(&self) -> bool {
489 self.worklist.is_empty()
490 }
491 pub fn pending(&self) -> usize {
493 self.worklist.len()
494 }
495}
496#[allow(dead_code)]
498pub struct KReductionTable {
499 entries: Vec<(String, bool)>,
500}
501#[allow(dead_code)]
502impl KReductionTable {
503 pub fn new() -> Self {
505 KReductionTable {
506 entries: Vec::new(),
507 }
508 }
509 pub fn set(&mut self, name: &str, reducible: bool) {
511 if let Some(e) = self.entries.iter_mut().find(|(n, _)| n == name) {
512 e.1 = reducible;
513 } else {
514 self.entries.push((name.to_string(), reducible));
515 }
516 }
517 pub fn is_k_reducible(&self, name: &str) -> bool {
519 self.entries
520 .iter()
521 .find(|(n, _)| n == name)
522 .is_some_and(|(_, r)| *r)
523 }
524 pub fn k_reducible_names(&self) -> Vec<&str> {
526 self.entries
527 .iter()
528 .filter(|(_, r)| *r)
529 .map(|(n, _)| n.as_str())
530 .collect()
531 }
532 pub fn len(&self) -> usize {
534 self.entries.len()
535 }
536 pub fn is_empty(&self) -> bool {
538 self.entries.is_empty()
539 }
540}
541#[allow(dead_code)]
543pub struct ShapeEquivalence {
544 registry: StructureRegistry,
545}
546#[allow(dead_code)]
547impl ShapeEquivalence {
548 pub fn new(registry: StructureRegistry) -> Self {
550 ShapeEquivalence { registry }
551 }
552 pub fn may_be_equal(&self, a: &StructShape, b: &StructShape) -> bool {
554 match (a, b) {
555 (
556 StructShape::Ctor {
557 name: n1,
558 arity: ar1,
559 },
560 StructShape::Ctor {
561 name: n2,
562 arity: ar2,
563 },
564 ) => n1 == n2 && ar1 == ar2,
565 (StructShape::Proj { field_index: i }, StructShape::Proj { field_index: j }) => i == j,
566 (StructShape::Other, StructShape::Other) => true,
567 _ => false,
568 }
569 }
570 pub fn is_expandable(&self, shape: &StructShape) -> bool {
572 match shape {
573 StructShape::Ctor { name, .. } => self.registry.find(name).is_some(),
574 _ => false,
575 }
576 }
577}
578#[allow(dead_code)]
580pub struct RecordUpdateBatch {
581 updates: Vec<RecordUpdate>,
582}
583#[allow(dead_code)]
584impl RecordUpdateBatch {
585 pub fn new() -> Self {
587 RecordUpdateBatch {
588 updates: Vec::new(),
589 }
590 }
591 pub fn add(&mut self, update: RecordUpdate) {
593 self.updates.push(update);
594 }
595 pub fn updates(&self) -> &[RecordUpdate] {
597 &self.updates
598 }
599 pub fn len(&self) -> usize {
601 self.updates.len()
602 }
603 pub fn is_empty(&self) -> bool {
605 self.updates.is_empty()
606 }
607 pub fn clear(&mut self) {
609 self.updates.clear();
610 }
611 pub fn updates_for_expr(&self, expr_id: u64) -> Vec<&RecordUpdate> {
613 self.updates
614 .iter()
615 .filter(|u| u.expr_id == expr_id)
616 .collect()
617 }
618}
619#[allow(dead_code)]
621pub struct EtaEqualityOracle {
622 canon_map: EtaCanonMap,
623}
624#[allow(dead_code)]
625impl EtaEqualityOracle {
626 pub fn new(canon_map: EtaCanonMap) -> Self {
628 EtaEqualityOracle { canon_map }
629 }
630 pub fn are_eta_equal(&self, a: u64, b: u64) -> bool {
632 self.canon_map.canonical(a) == self.canon_map.canonical(b)
633 }
634 pub fn canonical(&self, id: u64) -> u64 {
636 self.canon_map.canonical(id)
637 }
638 pub fn class_count(&self) -> usize {
640 let mut canons: Vec<u64> = self.canon_map.map.iter().map(|(_, c)| *c).collect();
641 canons.sort_unstable();
642 canons.dedup();
643 canons.len()
644 }
645}
646#[allow(dead_code)]
648pub struct FieldBoundsChecker;
649#[allow(dead_code)]
650impl FieldBoundsChecker {
651 pub fn check(arity: u32, field_index: u32) -> Result<(), String> {
653 if field_index < arity {
654 Ok(())
655 } else {
656 Err(format!(
657 "field index {} out of bounds for arity {}",
658 field_index, arity
659 ))
660 }
661 }
662 pub fn validate_set(set: &ProjectionRewriteSet, reg: &StructureRegistry) -> Vec<String> {
664 let mut errors = Vec::new();
665 for rule in &set.rules {
666 let arity = reg.field_count(&rule.ctor_name) as u32;
667 if arity == 0 {
668 errors.push(format!("unknown structure: {}", rule.ctor_name));
669 } else if let Err(e) = Self::check(arity, rule.field_index) {
670 errors.push(format!("rule '{}': {}", rule.projector_name, e));
671 }
672 }
673 errors
674 }
675}
676#[allow(dead_code)]
678pub struct EtaLongChecker {
679 results: Vec<(u64, EtaLongStatus)>,
680}
681#[allow(dead_code)]
682impl EtaLongChecker {
683 pub fn new() -> Self {
685 EtaLongChecker {
686 results: Vec::new(),
687 }
688 }
689 pub fn record(&mut self, id: u64, status: EtaLongStatus) {
691 self.results.push((id, status));
692 }
693 pub fn status(&self, id: u64) -> Option<EtaLongStatus> {
695 self.results.iter().find(|(i, _)| *i == id).map(|(_, s)| *s)
696 }
697 pub fn summary(&self) -> (usize, usize, usize) {
699 let eta_long = self
700 .results
701 .iter()
702 .filter(|(_, s)| *s == EtaLongStatus::EtaLong)
703 .count();
704 let not_eta_long = self
705 .results
706 .iter()
707 .filter(|(_, s)| *s == EtaLongStatus::NotEtaLong)
708 .count();
709 let unknown = self
710 .results
711 .iter()
712 .filter(|(_, s)| *s == EtaLongStatus::Unknown)
713 .count();
714 (eta_long, not_eta_long, unknown)
715 }
716 pub fn len(&self) -> usize {
718 self.results.len()
719 }
720 pub fn is_empty(&self) -> bool {
722 self.results.is_empty()
723 }
724}
725#[allow(dead_code)]
727pub struct EtaChangeLog {
728 entries: Vec<EtaChangeEntry>,
729}
730#[allow(dead_code)]
731impl EtaChangeLog {
732 pub fn new() -> Self {
734 EtaChangeLog {
735 entries: Vec::new(),
736 }
737 }
738 pub fn record(&mut self, expr_id: u64, kind: EtaChangeKind, pass_num: u32) {
740 self.entries.push(EtaChangeEntry {
741 expr_id,
742 kind,
743 pass_num,
744 });
745 }
746 pub fn changes_of_kind(&self, kind: EtaChangeKind) -> Vec<&EtaChangeEntry> {
748 self.entries.iter().filter(|e| e.kind == kind).collect()
749 }
750 pub fn changes_for_expr(&self, id: u64) -> Vec<&EtaChangeEntry> {
752 self.entries.iter().filter(|e| e.expr_id == id).collect()
753 }
754 pub fn len(&self) -> usize {
756 self.entries.len()
757 }
758 pub fn is_empty(&self) -> bool {
760 self.entries.is_empty()
761 }
762 pub fn changes_in_pass(&self, pass: u32) -> Vec<&EtaChangeEntry> {
764 self.entries.iter().filter(|e| e.pass_num == pass).collect()
765 }
766}
767#[allow(dead_code)]
769pub struct StructureRegistry {
770 structures: Vec<StructureDef>,
771}
772#[allow(dead_code)]
773impl StructureRegistry {
774 pub fn new() -> Self {
776 StructureRegistry {
777 structures: Vec::new(),
778 }
779 }
780 pub fn register(
782 &mut self,
783 name: impl Into<String>,
784 ctor_name: impl Into<String>,
785 fields: Vec<FieldDescriptor>,
786 ) {
787 self.structures.push(StructureDef {
788 name: name.into(),
789 ctor_name: ctor_name.into(),
790 fields,
791 });
792 }
793 pub fn find(&self, name: &str) -> Option<&StructureDef> {
795 self.structures.iter().find(|s| s.name == name)
796 }
797 pub fn len(&self) -> usize {
799 self.structures.len()
800 }
801 pub fn is_empty(&self) -> bool {
803 self.structures.is_empty()
804 }
805 pub fn names(&self) -> Vec<&str> {
807 self.structures.iter().map(|s| s.name.as_str()).collect()
808 }
809 pub fn field_count(&self, name: &str) -> usize {
811 self.find(name).map_or(0, |s| s.fields.len())
812 }
813 pub fn has_prop_fields(&self, name: &str) -> bool {
815 self.find(name)
816 .is_some_and(|s| s.fields.iter().any(|f| f.is_prop))
817 }
818 pub fn projectors(&self, name: &str) -> Vec<String> {
820 self.find(name).map_or(Vec::new(), |s| {
821 s.fields
822 .iter()
823 .map(|f| format!("{}.{}", s.name, f.name))
824 .collect()
825 })
826 }
827}
828#[allow(dead_code)]
830#[derive(Debug, Clone, Copy, PartialEq, Eq)]
831pub enum EtaCategory {
832 Record,
833 Function,
834 Inductive,
835 Proposition,
836 Primitive,
837}
838#[allow(dead_code)]
839impl EtaCategory {
840 pub fn needs_eta(&self) -> bool {
842 matches!(self, EtaCategory::Record | EtaCategory::Function)
843 }
844 pub fn label(&self) -> &'static str {
846 match self {
847 EtaCategory::Record => "record",
848 EtaCategory::Function => "function",
849 EtaCategory::Inductive => "inductive",
850 EtaCategory::Proposition => "proposition",
851 EtaCategory::Primitive => "primitive",
852 }
853 }
854}
855pub struct StructureEta<'a> {
864 env: &'a Environment,
865}
866impl<'a> StructureEta<'a> {
867 pub fn new(env: &'a Environment) -> Self {
869 StructureEta { env }
870 }
871 pub fn is_structure_type(&self, ty: &Expr) -> bool {
874 let name = match ty {
875 Expr::Const(n, _) => n,
876 _ => {
877 if let Some(n) = head_const(ty) {
878 return self.env.is_structure_like(n);
879 }
880 return false;
881 }
882 };
883 self.env.is_structure_like(name)
884 }
885 pub fn collect_field_types(&self, struct_name: &Name) -> Vec<(Name, Expr)> {
895 let iv = match self.env.get_inductive_val(struct_name) {
896 Some(v) => v,
897 None => return vec![],
898 };
899 let ctor_name = match iv.ctors.first() {
900 Some(n) => n.clone(),
901 None => return vec![],
902 };
903 let cv = match self.env.get_constructor_val(&ctor_name) {
904 Some(v) => v,
905 None => return vec![],
906 };
907 let ctor_ty = match self.env.get_type(&ctor_name) {
908 Some(t) => t.clone(),
909 None => return vec![],
910 };
911 let num_params = cv.num_params as usize;
912 let num_fields = cv.num_fields as usize;
913 let mut current = &ctor_ty;
914 let mut skipped = 0usize;
915 let mut fields = Vec::with_capacity(num_fields);
916 while let Expr::Pi(_, name, domain, codomain) = current {
917 if skipped < num_params {
918 skipped += 1;
919 current = codomain;
920 } else if fields.len() < num_fields {
921 fields.push((name.clone(), *domain.clone()));
922 current = codomain;
923 } else {
924 break;
925 }
926 }
927 fields
928 }
929 pub fn make_proj_chain(&self, expr: &Expr, struct_name: &Name, num_fields: usize) -> Vec<Expr> {
932 (0..num_fields)
933 .map(|i| Expr::Proj(struct_name.clone(), i as u32, Box::new(expr.clone())))
934 .collect()
935 }
936 pub fn eta_expand_struct(&self, expr: &Expr, ty: &Expr) -> Option<Expr> {
942 let struct_name = head_const(ty)?.clone();
943 if !self.env.is_structure_like(&struct_name) {
944 return None;
945 }
946 let iv = self.env.get_inductive_val(&struct_name)?;
947 let ctor_name = iv.ctors.first()?.clone();
948 let cv = self.env.get_constructor_val(&ctor_name)?;
949 let num_fields = cv.num_fields as usize;
950 let ctor_levels: Vec<Level> = vec![];
951 let ctor_const = Expr::Const(ctor_name, ctor_levels);
952 let projections = self.make_proj_chain(expr, &struct_name, num_fields);
953 if projections.is_empty() {
954 return Some(ctor_const);
955 }
956 let result = projections.into_iter().fold(ctor_const, |acc, proj| {
957 Expr::App(Box::new(acc), Box::new(proj))
958 });
959 Some(result)
960 }
961}
962#[allow(dead_code)]
964#[derive(Debug, Clone, PartialEq, Eq)]
965pub enum StructShape {
966 Ctor { name: String, arity: u32 },
968 Proj { field_index: u32 },
970 Other,
972}
973#[allow(dead_code)]
974impl StructShape {
975 pub fn ctor(name: impl Into<String>, arity: u32) -> Self {
977 StructShape::Ctor {
978 name: name.into(),
979 arity,
980 }
981 }
982 pub fn proj(field_index: u32) -> Self {
984 StructShape::Proj { field_index }
985 }
986 pub fn is_ctor(&self) -> bool {
988 matches!(self, StructShape::Ctor { .. })
989 }
990 pub fn is_proj(&self) -> bool {
992 matches!(self, StructShape::Proj { .. })
993 }
994 pub fn arity(&self) -> Option<u32> {
996 match self {
997 StructShape::Ctor { arity, .. } => Some(*arity),
998 _ => None,
999 }
1000 }
1001}
1002#[allow(dead_code)]
1004pub struct EtaRewriteEngine {
1005 proj_rules: ProjectionRewriteSet,
1006 max_steps: u32,
1007 steps_taken: u32,
1008}
1009#[allow(dead_code)]
1010impl EtaRewriteEngine {
1011 pub fn new(proj_rules: ProjectionRewriteSet, max_steps: u32) -> Self {
1013 EtaRewriteEngine {
1014 proj_rules,
1015 max_steps,
1016 steps_taken: 0,
1017 }
1018 }
1019 pub fn steps_taken(&self) -> u32 {
1021 self.steps_taken
1022 }
1023 pub fn is_exhausted(&self) -> bool {
1025 self.steps_taken >= self.max_steps
1026 }
1027 pub fn apply_proj(&mut self, projector: &str) -> Option<u32> {
1030 if self.is_exhausted() {
1031 return None;
1032 }
1033 if let Some(rule) = self.proj_rules.find_by_projector(projector) {
1034 let idx = rule.field_index;
1035 self.steps_taken += 1;
1036 Some(idx)
1037 } else {
1038 None
1039 }
1040 }
1041 pub fn reset(&mut self) {
1043 self.steps_taken = 0;
1044 }
1045 pub fn rule_count(&self) -> usize {
1047 self.proj_rules.len()
1048 }
1049}
1050#[allow(dead_code)]
1052#[derive(Debug, Clone)]
1053pub struct ProjectionDescriptor {
1054 pub structure_name: String,
1055 pub projector_name: String,
1056 pub field_index: u32,
1057}
1058#[allow(dead_code)]
1059impl ProjectionDescriptor {
1060 pub fn new(
1062 structure_name: impl Into<String>,
1063 projector_name: impl Into<String>,
1064 field_index: u32,
1065 ) -> Self {
1066 ProjectionDescriptor {
1067 structure_name: structure_name.into(),
1068 projector_name: projector_name.into(),
1069 field_index,
1070 }
1071 }
1072}
1073#[allow(dead_code)]
1075#[derive(Debug, Clone)]
1076pub struct ProjectionRewrite {
1077 pub ctor_name: String,
1078 pub projector_name: String,
1079 pub field_index: u32,
1080}
1081#[allow(dead_code)]
1082impl ProjectionRewrite {
1083 pub fn new(
1085 ctor_name: impl Into<String>,
1086 projector_name: impl Into<String>,
1087 field_index: u32,
1088 ) -> Self {
1089 ProjectionRewrite {
1090 ctor_name: ctor_name.into(),
1091 projector_name: projector_name.into(),
1092 field_index,
1093 }
1094 }
1095 pub fn as_rule(&self) -> String {
1097 format!(
1098 "{} ({}.mk f0 ... f{} ...) → f{}",
1099 self.projector_name, self.ctor_name, self.field_index, self.field_index
1100 )
1101 }
1102}
1103#[allow(dead_code)]
1105pub struct EtaCategorizer {
1106 entries: Vec<(u64, EtaCategory)>,
1107}
1108#[allow(dead_code)]
1109impl EtaCategorizer {
1110 pub fn new() -> Self {
1112 EtaCategorizer {
1113 entries: Vec::new(),
1114 }
1115 }
1116 pub fn assign(&mut self, id: u64, cat: EtaCategory) {
1118 self.entries.push((id, cat));
1119 }
1120 pub fn get(&self, id: u64) -> Option<EtaCategory> {
1122 self.entries.iter().find(|(i, _)| *i == id).map(|(_, c)| *c)
1123 }
1124 pub fn needs_eta_ids(&self) -> Vec<u64> {
1126 self.entries
1127 .iter()
1128 .filter(|(_, c)| c.needs_eta())
1129 .map(|(i, _)| *i)
1130 .collect()
1131 }
1132 pub fn len(&self) -> usize {
1134 self.entries.len()
1135 }
1136 pub fn is_empty(&self) -> bool {
1138 self.entries.is_empty()
1139 }
1140 pub fn count_by_category(&self) -> [(EtaCategory, usize); 5] {
1142 let cats = [
1143 EtaCategory::Record,
1144 EtaCategory::Function,
1145 EtaCategory::Inductive,
1146 EtaCategory::Proposition,
1147 EtaCategory::Primitive,
1148 ];
1149 cats.map(|c| {
1150 let count = self.entries.iter().filter(|(_, cat)| *cat == c).count();
1151 (c, count)
1152 })
1153 }
1154}
1155#[allow(dead_code)]
1157pub struct EtaCanonMap {
1158 map: Vec<(u64, u64)>,
1159}
1160#[allow(dead_code)]
1161impl EtaCanonMap {
1162 pub fn new() -> Self {
1164 EtaCanonMap { map: Vec::new() }
1165 }
1166 pub fn insert(&mut self, original: u64, canon: u64) {
1168 if let Some(e) = self.map.iter_mut().find(|(o, _)| *o == original) {
1169 e.1 = canon;
1170 } else {
1171 self.map.push((original, canon));
1172 }
1173 }
1174 pub fn canonical(&self, original: u64) -> u64 {
1176 self.map
1177 .iter()
1178 .find(|(o, _)| *o == original)
1179 .map_or(original, |(_, c)| *c)
1180 }
1181 pub fn len(&self) -> usize {
1183 self.map.len()
1184 }
1185 pub fn is_empty(&self) -> bool {
1187 self.map.is_empty()
1188 }
1189 pub fn originals_of(&self, canon: u64) -> Vec<u64> {
1191 self.map
1192 .iter()
1193 .filter(|(_, c)| *c == canon)
1194 .map(|(o, _)| *o)
1195 .collect()
1196 }
1197}
1198#[allow(dead_code)]
1200pub struct EtaGraph {
1201 edges: Vec<(u64, u64)>,
1202}
1203#[allow(dead_code)]
1204impl EtaGraph {
1205 pub fn new() -> Self {
1207 EtaGraph { edges: Vec::new() }
1208 }
1209 pub fn add_edge(&mut self, from: u64, to: u64) {
1211 if !self.has_edge(from, to) {
1212 self.edges.push((from, to));
1213 }
1214 }
1215 pub fn has_edge(&self, from: u64, to: u64) -> bool {
1217 self.edges.iter().any(|&(f, t)| f == from && t == to)
1218 }
1219 pub fn dependents_of(&self, id: u64) -> Vec<u64> {
1221 self.edges
1222 .iter()
1223 .filter(|(_, t)| *t == id)
1224 .map(|(f, _)| *f)
1225 .collect()
1226 }
1227 pub fn dependencies_of(&self, id: u64) -> Vec<u64> {
1229 self.edges
1230 .iter()
1231 .filter(|(f, _)| *f == id)
1232 .map(|(_, t)| *t)
1233 .collect()
1234 }
1235 pub fn edge_count(&self) -> usize {
1237 self.edges.len()
1238 }
1239 pub fn is_empty(&self) -> bool {
1241 self.edges.is_empty()
1242 }
1243 pub fn remove_node(&mut self, id: u64) {
1245 self.edges.retain(|(f, t)| *f != id && *t != id);
1246 }
1247}
1248#[allow(dead_code)]
1250#[derive(Debug, Clone)]
1251pub struct EtaRedex {
1252 pub path: Vec<u32>,
1254 pub struct_name: String,
1256 pub inner_id: u64,
1258}
1259#[allow(dead_code)]
1260impl EtaRedex {
1261 pub fn new(path: Vec<u32>, struct_name: impl Into<String>, inner_id: u64) -> Self {
1263 EtaRedex {
1264 path,
1265 struct_name: struct_name.into(),
1266 inner_id,
1267 }
1268 }
1269 pub fn is_top_level(&self) -> bool {
1271 self.path.is_empty()
1272 }
1273 pub fn depth(&self) -> usize {
1275 self.path.len()
1276 }
1277}
1278#[allow(dead_code)]
1280pub struct EtaDepthTracker {
1281 stack: Vec<String>,
1282}
1283#[allow(dead_code)]
1284impl EtaDepthTracker {
1285 pub fn new() -> Self {
1287 EtaDepthTracker { stack: Vec::new() }
1288 }
1289 pub fn push(&mut self, struct_name: &str) {
1291 self.stack.push(struct_name.to_string());
1292 }
1293 pub fn pop(&mut self) -> Option<String> {
1295 self.stack.pop()
1296 }
1297 pub fn depth(&self) -> usize {
1299 self.stack.len()
1300 }
1301 pub fn is_nested(&self) -> bool {
1303 !self.stack.is_empty()
1304 }
1305 pub fn current(&self) -> Option<&str> {
1307 self.stack.last().map(|s| s.as_str())
1308 }
1309 pub fn path(&self) -> String {
1311 self.stack.join(".")
1312 }
1313 pub fn contains(&self, name: &str) -> bool {
1315 self.stack.iter().any(|s| s == name)
1316 }
1317}
1318#[allow(dead_code)]
1320#[derive(Debug, Clone)]
1321pub struct RecordUpdate {
1322 pub expr_id: u64,
1323 pub struct_name: String,
1324 pub field_index: u32,
1325 pub new_value_id: u64,
1326}
1327#[allow(dead_code)]
1328impl RecordUpdate {
1329 pub fn new(
1331 expr_id: u64,
1332 struct_name: impl Into<String>,
1333 field_index: u32,
1334 new_value_id: u64,
1335 ) -> Self {
1336 RecordUpdate {
1337 expr_id,
1338 struct_name: struct_name.into(),
1339 field_index,
1340 new_value_id,
1341 }
1342 }
1343 pub fn describe(&self) -> String {
1345 format!(
1346 "update {}.{} of expr #{} with expr #{}",
1347 self.struct_name, self.field_index, self.expr_id, self.new_value_id
1348 )
1349 }
1350}
1351#[allow(dead_code)]
1352#[derive(Debug, Clone)]
1353pub struct EtaChangeEntry {
1354 pub expr_id: u64,
1355 pub kind: EtaChangeKind,
1356 pub pass_num: u32,
1357}
1358#[allow(dead_code)]
1360pub struct EtaStateMachine {
1361 state: EtaState,
1362 structure_name: Option<String>,
1363 field_count: u32,
1364 processed_fields: u32,
1365}
1366#[allow(dead_code)]
1367impl EtaStateMachine {
1368 pub fn new() -> Self {
1370 EtaStateMachine {
1371 state: EtaState::Idle,
1372 structure_name: None,
1373 field_count: 0,
1374 processed_fields: 0,
1375 }
1376 }
1377 pub fn start(&mut self, name: &str, field_count: u32) {
1379 self.state = EtaState::Expanding;
1380 self.structure_name = Some(name.to_string());
1381 self.field_count = field_count;
1382 self.processed_fields = 0;
1383 }
1384 pub fn process_field(&mut self) -> bool {
1386 if self.state != EtaState::Expanding {
1387 return false;
1388 }
1389 self.processed_fields += 1;
1390 if self.processed_fields >= self.field_count {
1391 self.state = EtaState::Done;
1392 }
1393 true
1394 }
1395 pub fn fail(&mut self) {
1397 self.state = EtaState::Failed;
1398 }
1399 pub fn is_done(&self) -> bool {
1401 self.state == EtaState::Done
1402 }
1403 pub fn is_failed(&self) -> bool {
1405 self.state == EtaState::Failed
1406 }
1407 pub fn is_expanding(&self) -> bool {
1409 self.state == EtaState::Expanding
1410 }
1411 pub fn remaining(&self) -> u32 {
1413 self.field_count.saturating_sub(self.processed_fields)
1414 }
1415}
1416#[allow(dead_code)]
1418#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1419pub enum EtaLongStatus {
1420 EtaLong,
1422 NotEtaLong,
1424 Unknown,
1426}
1427#[allow(dead_code)]
1428impl EtaLongStatus {
1429 pub fn is_eta_long(&self) -> bool {
1431 *self == EtaLongStatus::EtaLong
1432 }
1433}
1434#[allow(dead_code)]
1436#[derive(Debug, Clone)]
1437pub struct EtaPassResult {
1438 pub summary: EtaNormRunSummary,
1439 pub changed_ids: Vec<u64>,
1440}
1441#[allow(dead_code)]
1442impl EtaPassResult {
1443 pub fn new() -> Self {
1445 EtaPassResult {
1446 summary: EtaNormRunSummary::new(),
1447 changed_ids: Vec::new(),
1448 }
1449 }
1450 pub fn any_changes(&self) -> bool {
1452 !self.changed_ids.is_empty()
1453 }
1454}
1455#[allow(dead_code)]
1457pub struct StructFlatteningPass {
1458 pub processed: u64,
1459 pub flattened: u64,
1460}
1461#[allow(dead_code)]
1462impl StructFlatteningPass {
1463 pub fn new() -> Self {
1465 StructFlatteningPass {
1466 processed: 0,
1467 flattened: 0,
1468 }
1469 }
1470 pub fn record_processed(&mut self) {
1472 self.processed += 1;
1473 }
1474 pub fn record_flattened(&mut self) {
1476 self.flattened += 1;
1477 }
1478 pub fn flatten_rate(&self) -> f64 {
1480 if self.processed == 0 {
1481 0.0
1482 } else {
1483 self.flattened as f64 / self.processed as f64
1484 }
1485 }
1486}
1487#[allow(dead_code)]
1489#[derive(Debug, Clone)]
1490pub struct FieldDescriptor {
1491 pub name: String,
1492 pub index: u32,
1493 pub is_prop: bool,
1494}
1495#[allow(dead_code)]
1496impl FieldDescriptor {
1497 pub fn new(name: impl Into<String>, index: u32, is_prop: bool) -> Self {
1499 FieldDescriptor {
1500 name: name.into(),
1501 index,
1502 is_prop,
1503 }
1504 }
1505 pub fn is_data(&self) -> bool {
1507 !self.is_prop
1508 }
1509}
1510#[allow(dead_code)]
1512pub struct InjectivityChecker {
1513 injective: Vec<String>,
1514}
1515#[allow(dead_code)]
1516impl InjectivityChecker {
1517 pub fn new() -> Self {
1519 InjectivityChecker {
1520 injective: Vec::new(),
1521 }
1522 }
1523 pub fn mark_injective(&mut self, ctor: &str) {
1525 if !self.injective.contains(&ctor.to_string()) {
1526 self.injective.push(ctor.to_string());
1527 }
1528 }
1529 pub fn is_injective(&self, ctor: &str) -> bool {
1531 self.injective.iter().any(|s| s == ctor)
1532 }
1533 pub fn injective_ctors(&self) -> &[String] {
1535 &self.injective
1536 }
1537 pub fn count(&self) -> usize {
1539 self.injective.len()
1540 }
1541}