1use crate::{Expr, Reducer};
6
7use std::collections::HashMap;
8
9#[allow(dead_code)]
11pub struct NonEmptyVec<T> {
12 head: T,
13 tail: Vec<T>,
14}
15#[allow(dead_code)]
16impl<T> NonEmptyVec<T> {
17 pub fn singleton(val: T) -> Self {
19 Self {
20 head: val,
21 tail: Vec::new(),
22 }
23 }
24 pub fn push(&mut self, val: T) {
26 self.tail.push(val);
27 }
28 pub fn first(&self) -> &T {
30 &self.head
31 }
32 pub fn last(&self) -> &T {
34 self.tail.last().unwrap_or(&self.head)
35 }
36 pub fn len(&self) -> usize {
38 1 + self.tail.len()
39 }
40 pub fn is_empty(&self) -> bool {
42 self.len() == 0
43 }
44 pub fn to_vec(&self) -> Vec<&T> {
46 let mut v = vec![&self.head];
47 v.extend(self.tail.iter());
48 v
49 }
50}
51#[derive(Debug, Clone, PartialEq, Eq)]
53pub enum RedexKind {
54 Beta,
56 Let,
58 Proj,
60 Delta(crate::Name),
62}
63impl RedexKind {
64 pub fn description(&self) -> String {
66 match self {
67 RedexKind::Beta => "β-redex".to_string(),
68 RedexKind::Let => "let-redex".to_string(),
69 RedexKind::Proj => "proj-redex".to_string(),
70 RedexKind::Delta(name) => format!("δ-redex ({})", name),
71 }
72 }
73}
74#[allow(dead_code)]
76pub struct StatSummary {
77 count: u64,
78 sum: f64,
79 min: f64,
80 max: f64,
81}
82#[allow(dead_code)]
83impl StatSummary {
84 pub fn new() -> Self {
86 Self {
87 count: 0,
88 sum: 0.0,
89 min: f64::INFINITY,
90 max: f64::NEG_INFINITY,
91 }
92 }
93 pub fn record(&mut self, val: f64) {
95 self.count += 1;
96 self.sum += val;
97 if val < self.min {
98 self.min = val;
99 }
100 if val > self.max {
101 self.max = val;
102 }
103 }
104 pub fn mean(&self) -> Option<f64> {
106 if self.count == 0 {
107 None
108 } else {
109 Some(self.sum / self.count as f64)
110 }
111 }
112 pub fn min(&self) -> Option<f64> {
114 if self.count == 0 {
115 None
116 } else {
117 Some(self.min)
118 }
119 }
120 pub fn max(&self) -> Option<f64> {
122 if self.count == 0 {
123 None
124 } else {
125 Some(self.max)
126 }
127 }
128 pub fn count(&self) -> u64 {
130 self.count
131 }
132}
133#[allow(dead_code)]
135pub struct SlidingSum {
136 window: Vec<f64>,
137 capacity: usize,
138 pos: usize,
139 sum: f64,
140 count: usize,
141}
142#[allow(dead_code)]
143impl SlidingSum {
144 pub fn new(capacity: usize) -> Self {
146 Self {
147 window: vec![0.0; capacity],
148 capacity,
149 pos: 0,
150 sum: 0.0,
151 count: 0,
152 }
153 }
154 pub fn push(&mut self, val: f64) {
156 let oldest = self.window[self.pos];
157 self.sum -= oldest;
158 self.sum += val;
159 self.window[self.pos] = val;
160 self.pos = (self.pos + 1) % self.capacity;
161 if self.count < self.capacity {
162 self.count += 1;
163 }
164 }
165 pub fn sum(&self) -> f64 {
167 self.sum
168 }
169 pub fn mean(&self) -> Option<f64> {
171 if self.count == 0 {
172 None
173 } else {
174 Some(self.sum / self.count as f64)
175 }
176 }
177 pub fn count(&self) -> usize {
179 self.count
180 }
181}
182#[allow(dead_code)]
184pub struct StringPool {
185 free: Vec<String>,
186}
187#[allow(dead_code)]
188impl StringPool {
189 pub fn new() -> Self {
191 Self { free: Vec::new() }
192 }
193 pub fn take(&mut self) -> String {
195 self.free.pop().unwrap_or_default()
196 }
197 pub fn give(&mut self, mut s: String) {
199 s.clear();
200 self.free.push(s);
201 }
202 pub fn free_count(&self) -> usize {
204 self.free.len()
205 }
206}
207#[allow(dead_code)]
209#[allow(missing_docs)]
210pub struct RewriteRule {
211 pub name: String,
213 pub lhs: String,
215 pub rhs: String,
217 pub conditional: bool,
219}
220#[allow(dead_code)]
221impl RewriteRule {
222 pub fn unconditional(
224 name: impl Into<String>,
225 lhs: impl Into<String>,
226 rhs: impl Into<String>,
227 ) -> Self {
228 Self {
229 name: name.into(),
230 lhs: lhs.into(),
231 rhs: rhs.into(),
232 conditional: false,
233 }
234 }
235 pub fn conditional(
237 name: impl Into<String>,
238 lhs: impl Into<String>,
239 rhs: impl Into<String>,
240 ) -> Self {
241 Self {
242 name: name.into(),
243 lhs: lhs.into(),
244 rhs: rhs.into(),
245 conditional: true,
246 }
247 }
248 pub fn display(&self) -> String {
250 format!("{}: {} → {}", self.name, self.lhs, self.rhs)
251 }
252}
253#[allow(dead_code)]
255pub struct FlatSubstitution {
256 pairs: Vec<(String, String)>,
257}
258#[allow(dead_code)]
259impl FlatSubstitution {
260 pub fn new() -> Self {
262 Self { pairs: Vec::new() }
263 }
264 pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
266 self.pairs.push((from.into(), to.into()));
267 }
268 pub fn apply(&self, s: &str) -> String {
270 let mut result = s.to_string();
271 for (from, to) in &self.pairs {
272 result = result.replace(from.as_str(), to.as_str());
273 }
274 result
275 }
276 pub fn len(&self) -> usize {
278 self.pairs.len()
279 }
280 pub fn is_empty(&self) -> bool {
282 self.pairs.is_empty()
283 }
284}
285#[allow(dead_code)]
287pub enum Either2<A, B> {
288 First(A),
290 Second(B),
292}
293#[allow(dead_code)]
294impl<A, B> Either2<A, B> {
295 pub fn is_first(&self) -> bool {
297 matches!(self, Either2::First(_))
298 }
299 pub fn is_second(&self) -> bool {
301 matches!(self, Either2::Second(_))
302 }
303 pub fn first(self) -> Option<A> {
305 match self {
306 Either2::First(a) => Some(a),
307 _ => None,
308 }
309 }
310 pub fn second(self) -> Option<B> {
312 match self {
313 Either2::Second(b) => Some(b),
314 _ => None,
315 }
316 }
317 pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
319 match self {
320 Either2::First(a) => Either2::First(f(a)),
321 Either2::Second(b) => Either2::Second(b),
322 }
323 }
324}
325#[allow(dead_code)]
327pub struct Stopwatch {
328 start: crate::wall_clock::Instant,
329 splits: Vec<f64>,
330}
331#[allow(dead_code)]
332impl Stopwatch {
333 pub fn start() -> Self {
335 Self {
336 start: crate::wall_clock::Instant::now(),
337 splits: Vec::new(),
338 }
339 }
340 pub fn split(&mut self) {
342 self.splits.push(self.elapsed_ms());
343 }
344 pub fn elapsed_ms(&self) -> f64 {
346 self.start.elapsed().as_secs_f64() * 1000.0
347 }
348 pub fn splits(&self) -> &[f64] {
350 &self.splits
351 }
352 pub fn num_splits(&self) -> usize {
354 self.splits.len()
355 }
356}
357#[allow(dead_code)]
359pub struct ConfigNode {
360 key: String,
361 value: Option<String>,
362 children: Vec<ConfigNode>,
363}
364#[allow(dead_code)]
365impl ConfigNode {
366 pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
368 Self {
369 key: key.into(),
370 value: Some(value.into()),
371 children: Vec::new(),
372 }
373 }
374 pub fn section(key: impl Into<String>) -> Self {
376 Self {
377 key: key.into(),
378 value: None,
379 children: Vec::new(),
380 }
381 }
382 pub fn add_child(&mut self, child: ConfigNode) {
384 self.children.push(child);
385 }
386 pub fn key(&self) -> &str {
388 &self.key
389 }
390 pub fn value(&self) -> Option<&str> {
392 self.value.as_deref()
393 }
394 pub fn num_children(&self) -> usize {
396 self.children.len()
397 }
398 pub fn lookup(&self, path: &str) -> Option<&str> {
400 let mut parts = path.splitn(2, '.');
401 let head = parts.next()?;
402 let tail = parts.next();
403 if head != self.key {
404 return None;
405 }
406 match tail {
407 None => self.value.as_deref(),
408 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
409 }
410 }
411 fn lookup_relative(&self, path: &str) -> Option<&str> {
412 let mut parts = path.splitn(2, '.');
413 let head = parts.next()?;
414 let tail = parts.next();
415 if head != self.key {
416 return None;
417 }
418 match tail {
419 None => self.value.as_deref(),
420 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
421 }
422 }
423}
424#[allow(dead_code)]
426pub struct TokenBucket {
427 capacity: u64,
428 tokens: u64,
429 refill_per_ms: u64,
430 last_refill: crate::wall_clock::Instant,
431}
432#[allow(dead_code)]
433impl TokenBucket {
434 pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
436 Self {
437 capacity,
438 tokens: capacity,
439 refill_per_ms,
440 last_refill: crate::wall_clock::Instant::now(),
441 }
442 }
443 pub fn try_consume(&mut self, n: u64) -> bool {
445 self.refill();
446 if self.tokens >= n {
447 self.tokens -= n;
448 true
449 } else {
450 false
451 }
452 }
453 fn refill(&mut self) {
454 let now = crate::wall_clock::Instant::now();
455 let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
456 if elapsed_ms > 0 {
457 let new_tokens = elapsed_ms * self.refill_per_ms;
458 self.tokens = (self.tokens + new_tokens).min(self.capacity);
459 self.last_refill = now;
460 }
461 }
462 pub fn available(&self) -> u64 {
464 self.tokens
465 }
466 pub fn capacity(&self) -> u64 {
468 self.capacity
469 }
470}
471#[allow(dead_code)]
473pub struct MinHeap<T: Ord> {
474 data: Vec<T>,
475}
476#[allow(dead_code)]
477impl<T: Ord> MinHeap<T> {
478 pub fn new() -> Self {
480 Self { data: Vec::new() }
481 }
482 pub fn push(&mut self, val: T) {
484 self.data.push(val);
485 self.sift_up(self.data.len() - 1);
486 }
487 pub fn pop(&mut self) -> Option<T> {
489 if self.data.is_empty() {
490 return None;
491 }
492 let n = self.data.len();
493 self.data.swap(0, n - 1);
494 let min = self.data.pop();
495 if !self.data.is_empty() {
496 self.sift_down(0);
497 }
498 min
499 }
500 pub fn peek(&self) -> Option<&T> {
502 self.data.first()
503 }
504 pub fn len(&self) -> usize {
506 self.data.len()
507 }
508 pub fn is_empty(&self) -> bool {
510 self.data.is_empty()
511 }
512 fn sift_up(&mut self, mut i: usize) {
513 while i > 0 {
514 let parent = (i - 1) / 2;
515 if self.data[i] < self.data[parent] {
516 self.data.swap(i, parent);
517 i = parent;
518 } else {
519 break;
520 }
521 }
522 }
523 fn sift_down(&mut self, mut i: usize) {
524 let n = self.data.len();
525 loop {
526 let left = 2 * i + 1;
527 let right = 2 * i + 2;
528 let mut smallest = i;
529 if left < n && self.data[left] < self.data[smallest] {
530 smallest = left;
531 }
532 if right < n && self.data[right] < self.data[smallest] {
533 smallest = right;
534 }
535 if smallest == i {
536 break;
537 }
538 self.data.swap(i, smallest);
539 i = smallest;
540 }
541 }
542}
543#[allow(dead_code)]
545pub struct StackCalc {
546 stack: Vec<i64>,
547}
548#[allow(dead_code)]
549impl StackCalc {
550 pub fn new() -> Self {
552 Self { stack: Vec::new() }
553 }
554 pub fn push(&mut self, n: i64) {
556 self.stack.push(n);
557 }
558 fn apply_binary(&mut self, op: impl FnOnce(i64, i64) -> i64) -> Option<i64> {
562 let (rest_len, a, b) = match self.stack[..] {
563 [ref rest @ .., a, b] => (rest.len(), a, b),
564 _ => return None,
565 };
566 let value = op(a, b);
567 self.stack.truncate(rest_len);
568 self.stack.push(value);
569 Some(value)
570 }
571 pub fn add(&mut self) -> Option<i64> {
575 self.apply_binary(|a, b| a + b)
576 }
577 pub fn sub(&mut self) -> Option<i64> {
581 self.apply_binary(|a, b| a - b)
582 }
583 pub fn mul(&mut self) -> Option<i64> {
587 self.apply_binary(|a, b| a * b)
588 }
589 pub fn peek(&self) -> Option<i64> {
591 self.stack.last().copied()
592 }
593 pub fn depth(&self) -> usize {
595 self.stack.len()
596 }
597}
598#[allow(dead_code)]
600pub struct WindowIterator<'a, T> {
601 pub(super) data: &'a [T],
602 pub(super) pos: usize,
603 pub(super) window: usize,
604}
605#[allow(dead_code)]
606impl<'a, T> WindowIterator<'a, T> {
607 pub fn new(data: &'a [T], window: usize) -> Self {
609 Self {
610 data,
611 pos: 0,
612 window,
613 }
614 }
615}
616#[allow(dead_code)]
618pub struct WriteOnce<T> {
619 value: std::cell::Cell<Option<T>>,
620}
621#[allow(dead_code)]
622impl<T: Copy> WriteOnce<T> {
623 pub fn new() -> Self {
625 Self {
626 value: std::cell::Cell::new(None),
627 }
628 }
629 pub fn write(&self, val: T) -> bool {
631 if self.value.get().is_some() {
632 return false;
633 }
634 self.value.set(Some(val));
635 true
636 }
637 pub fn read(&self) -> Option<T> {
639 self.value.get()
640 }
641 pub fn is_written(&self) -> bool {
643 self.value.get().is_some()
644 }
645}
646#[derive(Debug, Clone, PartialEq)]
648pub enum ReductionResult {
649 Reduced(Expr),
651 Normal(Expr),
653 Aborted(Expr),
655}
656impl ReductionResult {
657 pub fn expr(&self) -> &Expr {
659 match self {
660 ReductionResult::Reduced(e)
661 | ReductionResult::Normal(e)
662 | ReductionResult::Aborted(e) => e,
663 }
664 }
665 pub fn was_reduced(&self) -> bool {
667 matches!(self, ReductionResult::Reduced(_))
668 }
669 pub fn is_normal(&self) -> bool {
671 matches!(self, ReductionResult::Normal(_))
672 }
673 pub fn into_expr(self) -> Expr {
675 match self {
676 ReductionResult::Reduced(e)
677 | ReductionResult::Normal(e)
678 | ReductionResult::Aborted(e) => e,
679 }
680 }
681}
682#[allow(dead_code)]
684pub struct PathBuf {
685 components: Vec<String>,
686}
687#[allow(dead_code)]
688impl PathBuf {
689 pub fn new() -> Self {
691 Self {
692 components: Vec::new(),
693 }
694 }
695 pub fn push(&mut self, comp: impl Into<String>) {
697 self.components.push(comp.into());
698 }
699 pub fn pop(&mut self) {
701 self.components.pop();
702 }
703 pub fn as_str(&self) -> String {
705 self.components.join("/")
706 }
707 pub fn depth(&self) -> usize {
709 self.components.len()
710 }
711 pub fn clear(&mut self) {
713 self.components.clear();
714 }
715}
716#[allow(dead_code)]
718pub struct PrefixCounter {
719 children: std::collections::HashMap<char, PrefixCounter>,
720 count: usize,
721}
722#[allow(dead_code)]
723impl PrefixCounter {
724 pub fn new() -> Self {
726 Self {
727 children: std::collections::HashMap::new(),
728 count: 0,
729 }
730 }
731 pub fn record(&mut self, s: &str) {
733 self.count += 1;
734 let mut node = self;
735 for c in s.chars() {
736 node = node.children.entry(c).or_default();
737 node.count += 1;
738 }
739 }
740 pub fn count_with_prefix(&self, prefix: &str) -> usize {
742 let mut node = self;
743 for c in prefix.chars() {
744 match node.children.get(&c) {
745 Some(n) => node = n,
746 None => return 0,
747 }
748 }
749 node.count
750 }
751}
752#[allow(dead_code)]
754#[allow(missing_docs)]
755pub enum DecisionNode {
756 Leaf(String),
758 Branch {
760 key: String,
761 val: String,
762 yes_branch: Box<DecisionNode>,
763 no_branch: Box<DecisionNode>,
764 },
765}
766#[allow(dead_code)]
767impl DecisionNode {
768 pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
770 match self {
771 DecisionNode::Leaf(action) => action.as_str(),
772 DecisionNode::Branch {
773 key,
774 val,
775 yes_branch,
776 no_branch,
777 } => {
778 let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
779 if actual == val.as_str() {
780 yes_branch.evaluate(ctx)
781 } else {
782 no_branch.evaluate(ctx)
783 }
784 }
785 }
786 }
787 pub fn depth(&self) -> usize {
789 match self {
790 DecisionNode::Leaf(_) => 0,
791 DecisionNode::Branch {
792 yes_branch,
793 no_branch,
794 ..
795 } => 1 + yes_branch.depth().max(no_branch.depth()),
796 }
797 }
798}
799#[allow(dead_code)]
801pub struct TransitiveClosure {
802 adj: Vec<Vec<usize>>,
803 n: usize,
804}
805#[allow(dead_code)]
806impl TransitiveClosure {
807 pub fn new(n: usize) -> Self {
809 Self {
810 adj: vec![Vec::new(); n],
811 n,
812 }
813 }
814 pub fn add_edge(&mut self, from: usize, to: usize) {
816 if from < self.n {
817 self.adj[from].push(to);
818 }
819 }
820 pub fn reachable_from(&self, start: usize) -> Vec<usize> {
822 let mut visited = vec![false; self.n];
823 let mut queue = std::collections::VecDeque::new();
824 queue.push_back(start);
825 while let Some(node) = queue.pop_front() {
826 if node >= self.n || visited[node] {
827 continue;
828 }
829 visited[node] = true;
830 for &next in &self.adj[node] {
831 queue.push_back(next);
832 }
833 }
834 (0..self.n).filter(|&i| visited[i]).collect()
835 }
836 pub fn can_reach(&self, from: usize, to: usize) -> bool {
838 self.reachable_from(from).contains(&to)
839 }
840}
841#[allow(dead_code)]
843pub struct LabelSet {
844 labels: Vec<String>,
845}
846#[allow(dead_code)]
847impl LabelSet {
848 pub fn new() -> Self {
850 Self { labels: Vec::new() }
851 }
852 pub fn add(&mut self, label: impl Into<String>) {
854 let s = label.into();
855 if !self.labels.contains(&s) {
856 self.labels.push(s);
857 }
858 }
859 pub fn has(&self, label: &str) -> bool {
861 self.labels.iter().any(|l| l == label)
862 }
863 pub fn count(&self) -> usize {
865 self.labels.len()
866 }
867 pub fn all(&self) -> &[String] {
869 &self.labels
870 }
871}
872#[derive(Debug, Clone)]
874pub struct RedexInfo {
875 pub kind: RedexKind,
877 pub depth: usize,
879 pub size: usize,
881}
882#[allow(dead_code)]
884pub struct VersionedRecord<T: Clone> {
885 earlier: Vec<T>,
887 current: T,
889}
890#[allow(dead_code)]
891impl<T: Clone> VersionedRecord<T> {
892 pub fn new(initial: T) -> Self {
894 Self {
895 earlier: Vec::new(),
896 current: initial,
897 }
898 }
899 pub fn update(&mut self, val: T) {
901 let previous = std::mem::replace(&mut self.current, val);
902 self.earlier.push(previous);
903 }
904 pub fn current(&self) -> &T {
906 &self.current
907 }
908 pub fn at_version(&self, n: usize) -> Option<&T> {
910 match self.earlier.get(n) {
911 Some(value) => Some(value),
912 None if n == self.earlier.len() => Some(&self.current),
913 None => None,
914 }
915 }
916 pub fn version(&self) -> usize {
918 self.earlier.len()
919 }
920 pub fn has_history(&self) -> bool {
922 !self.earlier.is_empty()
923 }
924}
925#[allow(dead_code)]
927pub struct SparseVec<T: Default + Clone + PartialEq> {
928 entries: std::collections::HashMap<usize, T>,
929 default_: T,
930 logical_len: usize,
931}
932#[allow(dead_code)]
933impl<T: Default + Clone + PartialEq> SparseVec<T> {
934 pub fn new(len: usize) -> Self {
936 Self {
937 entries: std::collections::HashMap::new(),
938 default_: T::default(),
939 logical_len: len,
940 }
941 }
942 pub fn set(&mut self, idx: usize, val: T) {
944 if val == self.default_ {
945 self.entries.remove(&idx);
946 } else {
947 self.entries.insert(idx, val);
948 }
949 }
950 pub fn get(&self, idx: usize) -> &T {
952 self.entries.get(&idx).unwrap_or(&self.default_)
953 }
954 pub fn len(&self) -> usize {
956 self.logical_len
957 }
958 pub fn is_empty(&self) -> bool {
960 self.len() == 0
961 }
962 pub fn nnz(&self) -> usize {
964 self.entries.len()
965 }
966}
967#[derive(Debug, Clone)]
969pub struct ReductionStep {
970 pub before: Expr,
972 pub after: Expr,
974 pub rule: String,
976}
977#[derive(Debug, Default)]
982pub struct ReductionMemo {
983 entries: std::collections::HashMap<(ReductionStrategy, String), Expr>,
984 hits: usize,
985 misses: usize,
986}
987impl ReductionMemo {
988 pub fn new() -> Self {
990 Self::default()
991 }
992 pub fn insert(&mut self, strategy: ReductionStrategy, expr: &Expr, result: Expr) {
994 let key = (strategy, format!("{:?}", expr));
995 self.entries.insert(key, result);
996 }
997 pub fn get(&mut self, strategy: ReductionStrategy, expr: &Expr) -> Option<&Expr> {
999 let key = (strategy, format!("{:?}", expr));
1000 if self.entries.contains_key(&key) {
1001 self.hits += 1;
1002 self.entries.get(&key)
1003 } else {
1004 self.misses += 1;
1005 None
1006 }
1007 }
1008 pub fn hit_rate(&self) -> f64 {
1010 let total = self.hits + self.misses;
1011 if total == 0 {
1012 0.0
1013 } else {
1014 self.hits as f64 / total as f64
1015 }
1016 }
1017 pub fn len(&self) -> usize {
1019 self.entries.len()
1020 }
1021 pub fn is_empty(&self) -> bool {
1023 self.entries.is_empty()
1024 }
1025 pub fn clear(&mut self) {
1027 self.entries.clear();
1028 self.hits = 0;
1029 self.misses = 0;
1030 }
1031}
1032#[allow(dead_code)]
1034pub struct Fixture {
1035 data: std::collections::HashMap<String, String>,
1036}
1037#[allow(dead_code)]
1038impl Fixture {
1039 pub fn new() -> Self {
1041 Self {
1042 data: std::collections::HashMap::new(),
1043 }
1044 }
1045 pub fn set(&mut self, key: impl Into<String>, val: impl Into<String>) {
1047 self.data.insert(key.into(), val.into());
1048 }
1049 pub fn get(&self, key: &str) -> Option<&str> {
1051 self.data.get(key).map(|s| s.as_str())
1052 }
1053 pub fn len(&self) -> usize {
1055 self.data.len()
1056 }
1057 pub fn is_empty(&self) -> bool {
1059 self.len() == 0
1060 }
1061}
1062#[derive(Debug, Clone, PartialEq, Eq)]
1064pub enum HeadForm {
1065 BVar(u32),
1067 FVar,
1069 Const(crate::Name),
1071 Sort,
1073 Lambda,
1075 Pi,
1077 Lit,
1079 Let,
1081 Proj,
1083 BetaRedex,
1085}
1086impl HeadForm {
1087 pub fn is_reducible(&self) -> bool {
1089 matches!(self, HeadForm::Let | HeadForm::BetaRedex)
1090 }
1091 pub fn is_variable(&self) -> bool {
1093 matches!(self, HeadForm::BVar(_) | HeadForm::FVar)
1094 }
1095 pub fn is_neutral(&self) -> bool {
1097 matches!(
1098 self,
1099 HeadForm::BVar(_) | HeadForm::FVar | HeadForm::Const(_)
1100 )
1101 }
1102}
1103#[allow(dead_code)]
1105pub struct SmallMap<K: Ord + Clone, V: Clone> {
1106 entries: Vec<(K, V)>,
1107}
1108#[allow(dead_code)]
1109impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
1110 pub fn new() -> Self {
1112 Self {
1113 entries: Vec::new(),
1114 }
1115 }
1116 pub fn insert(&mut self, key: K, val: V) {
1118 match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
1119 Ok(i) => self.entries[i].1 = val,
1120 Err(i) => self.entries.insert(i, (key, val)),
1121 }
1122 }
1123 pub fn get(&self, key: &K) -> Option<&V> {
1125 self.entries
1126 .binary_search_by_key(&key, |(k, _)| k)
1127 .ok()
1128 .map(|i| &self.entries[i].1)
1129 }
1130 pub fn len(&self) -> usize {
1132 self.entries.len()
1133 }
1134 pub fn is_empty(&self) -> bool {
1136 self.entries.is_empty()
1137 }
1138 pub fn keys(&self) -> Vec<&K> {
1140 self.entries.iter().map(|(k, _)| k).collect()
1141 }
1142 pub fn values(&self) -> Vec<&V> {
1144 self.entries.iter().map(|(_, v)| v).collect()
1145 }
1146}
1147#[allow(dead_code)]
1149pub struct TransformStat {
1150 before: StatSummary,
1151 after: StatSummary,
1152}
1153#[allow(dead_code)]
1154impl TransformStat {
1155 pub fn new() -> Self {
1157 Self {
1158 before: StatSummary::new(),
1159 after: StatSummary::new(),
1160 }
1161 }
1162 pub fn record_before(&mut self, v: f64) {
1164 self.before.record(v);
1165 }
1166 pub fn record_after(&mut self, v: f64) {
1168 self.after.record(v);
1169 }
1170 pub fn mean_ratio(&self) -> Option<f64> {
1172 let b = self.before.mean()?;
1173 let a = self.after.mean()?;
1174 if b.abs() < f64::EPSILON {
1175 return None;
1176 }
1177 Some(a / b)
1178 }
1179}
1180#[allow(dead_code)]
1182pub struct FocusStack<T> {
1183 items: Vec<T>,
1184}
1185#[allow(dead_code)]
1186impl<T> FocusStack<T> {
1187 pub fn new() -> Self {
1189 Self { items: Vec::new() }
1190 }
1191 pub fn focus(&mut self, item: T) {
1193 self.items.push(item);
1194 }
1195 pub fn blur(&mut self) -> Option<T> {
1197 self.items.pop()
1198 }
1199 pub fn current(&self) -> Option<&T> {
1201 self.items.last()
1202 }
1203 pub fn depth(&self) -> usize {
1205 self.items.len()
1206 }
1207 pub fn is_empty(&self) -> bool {
1209 self.items.is_empty()
1210 }
1211}
1212#[derive(Debug, Clone, Default)]
1214pub struct ReductionTrace {
1215 pub steps: Vec<ReductionStep>,
1217 pub reached_normal: bool,
1219 pub truncated: bool,
1221}
1222impl ReductionTrace {
1223 pub fn new() -> Self {
1225 Self::default()
1226 }
1227 pub fn len(&self) -> usize {
1229 self.steps.len()
1230 }
1231 pub fn is_empty(&self) -> bool {
1233 self.steps.is_empty()
1234 }
1235 pub fn final_expr(&self) -> Option<&Expr> {
1237 self.steps.last().map(|s| &s.after)
1238 }
1239}
1240#[allow(dead_code)]
1242pub struct RawFnPtr {
1243 ptr: usize,
1245 arity: usize,
1246 name: String,
1247}
1248#[allow(dead_code)]
1249impl RawFnPtr {
1250 pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
1252 Self {
1253 ptr,
1254 arity,
1255 name: name.into(),
1256 }
1257 }
1258 pub fn arity(&self) -> usize {
1260 self.arity
1261 }
1262 pub fn name(&self) -> &str {
1264 &self.name
1265 }
1266 pub fn raw(&self) -> usize {
1268 self.ptr
1269 }
1270}
1271#[allow(dead_code)]
1273pub struct RewriteRuleSet {
1274 rules: Vec<RewriteRule>,
1275}
1276#[allow(dead_code)]
1277impl RewriteRuleSet {
1278 pub fn new() -> Self {
1280 Self { rules: Vec::new() }
1281 }
1282 pub fn add(&mut self, rule: RewriteRule) {
1284 self.rules.push(rule);
1285 }
1286 pub fn len(&self) -> usize {
1288 self.rules.len()
1289 }
1290 pub fn is_empty(&self) -> bool {
1292 self.rules.is_empty()
1293 }
1294 pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
1296 self.rules.iter().filter(|r| r.conditional).collect()
1297 }
1298 pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
1300 self.rules.iter().filter(|r| !r.conditional).collect()
1301 }
1302 pub fn get(&self, name: &str) -> Option<&RewriteRule> {
1304 self.rules.iter().find(|r| r.name == name)
1305 }
1306}
1307#[derive(Debug, Clone, Default)]
1309pub struct ReductionStats {
1310 pub beta_steps: usize,
1312 pub delta_steps: usize,
1314 pub let_steps: usize,
1316 pub proj_steps: usize,
1318 pub total_steps: usize,
1320 pub aborted: bool,
1322}
1323impl ReductionStats {
1324 pub fn new() -> Self {
1326 Self::default()
1327 }
1328 pub fn total(&self) -> usize {
1330 self.total_steps
1331 }
1332 pub fn any_reductions(&self) -> bool {
1334 self.total_steps > 0
1335 }
1336 pub fn summary(&self) -> String {
1338 format!(
1339 "β:{} δ:{} let:{} proj:{} total:{}{}",
1340 self.beta_steps,
1341 self.delta_steps,
1342 self.let_steps,
1343 self.proj_steps,
1344 self.total_steps,
1345 if self.aborted { " (aborted)" } else { "" }
1346 )
1347 }
1348}
1349#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1351pub enum ReductionBound {
1352 Unlimited,
1354 Steps(usize),
1356 Size(usize),
1358}
1359impl ReductionBound {
1360 pub fn exceeded(&self, steps: usize, size: usize) -> bool {
1362 match self {
1363 ReductionBound::Unlimited => false,
1364 ReductionBound::Steps(n) => steps >= *n,
1365 ReductionBound::Size(n) => size >= *n,
1366 }
1367 }
1368}
1369#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1375pub enum ReductionStrategy {
1376 WHNF,
1381 NF,
1385 OneStep,
1389 CBV,
1393 CBN,
1397 HeadOnly,
1401}
1402impl ReductionStrategy {
1403 pub fn name(&self) -> &'static str {
1405 match self {
1406 ReductionStrategy::WHNF => "whnf",
1407 ReductionStrategy::NF => "nf",
1408 ReductionStrategy::OneStep => "one-step",
1409 ReductionStrategy::CBV => "cbv",
1410 ReductionStrategy::CBN => "cbn",
1411 ReductionStrategy::HeadOnly => "head-only",
1412 }
1413 }
1414 pub fn is_complete(&self) -> bool {
1416 matches!(self, ReductionStrategy::NF)
1417 }
1418 pub fn is_lazy(&self) -> bool {
1420 matches!(
1421 self,
1422 ReductionStrategy::WHNF | ReductionStrategy::CBN | ReductionStrategy::HeadOnly
1423 )
1424 }
1425 pub fn is_eager(&self) -> bool {
1427 matches!(self, ReductionStrategy::CBV)
1428 }
1429}
1430#[allow(dead_code)]
1432pub struct SimpleDag {
1433 edges: Vec<Vec<usize>>,
1435}
1436#[allow(dead_code)]
1437impl SimpleDag {
1438 pub fn new(n: usize) -> Self {
1440 Self {
1441 edges: vec![Vec::new(); n],
1442 }
1443 }
1444 pub fn add_edge(&mut self, from: usize, to: usize) {
1446 if from < self.edges.len() {
1447 self.edges[from].push(to);
1448 }
1449 }
1450 pub fn successors(&self, node: usize) -> &[usize] {
1452 self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
1453 }
1454 pub fn can_reach(&self, from: usize, to: usize) -> bool {
1456 let mut visited = vec![false; self.edges.len()];
1457 self.dfs(from, to, &mut visited)
1458 }
1459 fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
1460 if cur == target {
1461 return true;
1462 }
1463 if cur >= visited.len() || visited[cur] {
1464 return false;
1465 }
1466 visited[cur] = true;
1467 for &next in self.successors(cur) {
1468 if self.dfs(next, target, visited) {
1469 return true;
1470 }
1471 }
1472 false
1473 }
1474 pub fn topological_sort(&self) -> Option<Vec<usize>> {
1476 let n = self.edges.len();
1477 let mut in_degree = vec![0usize; n];
1478 for succs in &self.edges {
1479 for &s in succs {
1480 if s < n {
1481 in_degree[s] += 1;
1482 }
1483 }
1484 }
1485 let mut queue: std::collections::VecDeque<usize> =
1486 (0..n).filter(|&i| in_degree[i] == 0).collect();
1487 let mut order = Vec::new();
1488 while let Some(node) = queue.pop_front() {
1489 order.push(node);
1490 for &s in self.successors(node) {
1491 if s < n {
1492 in_degree[s] -= 1;
1493 if in_degree[s] == 0 {
1494 queue.push_back(s);
1495 }
1496 }
1497 }
1498 }
1499 if order.len() == n {
1500 Some(order)
1501 } else {
1502 None
1503 }
1504 }
1505 pub fn num_nodes(&self) -> usize {
1507 self.edges.len()
1508 }
1509}