1use super::functions::*;
6use crate::Expr;
7use std::collections::{HashMap, HashSet, VecDeque};
8
9#[allow(dead_code)]
11pub struct EquivalenceTable {
12 hash_to_id: std::collections::HashMap<u64, usize>,
13 uf: UnionFind,
14 next_id: usize,
15}
16#[allow(dead_code)]
17impl EquivalenceTable {
18 pub fn new(capacity: usize) -> Self {
20 Self {
21 hash_to_id: std::collections::HashMap::with_capacity(capacity),
22 uf: UnionFind::new(capacity),
23 next_id: 0,
24 }
25 }
26 pub fn register(&mut self, hash: u64) -> usize {
28 if let Some(&id) = self.hash_to_id.get(&hash) {
29 return id;
30 }
31 let id = self.next_id;
32 self.next_id += 1;
33 self.hash_to_id.insert(hash, id);
34 id
35 }
36 pub fn merge(&mut self, h1: u64, h2: u64) {
38 let id1 = self.register(h1);
39 let id2 = self.register(h2);
40 self.uf.union(id1, id2);
41 }
42 pub fn equiv(&mut self, h1: u64, h2: u64) -> bool {
44 let id1 = self.register(h1);
45 let id2 = self.register(h2);
46 self.uf.same(id1, id2)
47 }
48}
49#[allow(dead_code)]
51#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
52pub struct Timestamp(u64);
53#[allow(dead_code)]
54impl Timestamp {
55 pub const fn from_us(us: u64) -> Self {
57 Self(us)
58 }
59 pub fn as_us(self) -> u64 {
61 self.0
62 }
63 pub fn elapsed_since(self, earlier: Timestamp) -> u64 {
65 self.0.saturating_sub(earlier.0)
66 }
67}
68#[allow(dead_code)]
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub struct Generation(u32);
72#[allow(dead_code)]
73impl Generation {
74 pub const INITIAL: Generation = Generation(0);
76 pub fn advance(self) -> Generation {
78 Generation(self.0 + 1)
79 }
80 pub fn number(self) -> u32 {
82 self.0
83 }
84}
85#[allow(dead_code)]
87pub struct LoopClock {
88 start: crate::wall_clock::Instant,
89 iters: u64,
90}
91#[allow(dead_code)]
92impl LoopClock {
93 pub fn start() -> Self {
95 Self {
96 start: crate::wall_clock::Instant::now(),
97 iters: 0,
98 }
99 }
100 pub fn tick(&mut self) {
102 self.iters += 1;
103 }
104 pub fn elapsed_us(&self) -> f64 {
106 self.start.elapsed().as_secs_f64() * 1e6
107 }
108 pub fn avg_us_per_iter(&self) -> f64 {
110 if self.iters == 0 {
111 return 0.0;
112 }
113 self.elapsed_us() / self.iters as f64
114 }
115 pub fn iters(&self) -> u64 {
117 self.iters
118 }
119}
120#[allow(dead_code)]
122pub struct BiMap<A: std::hash::Hash + Eq + Clone, B: std::hash::Hash + Eq + Clone> {
123 forward: std::collections::HashMap<A, B>,
124 backward: std::collections::HashMap<B, A>,
125}
126#[allow(dead_code)]
127impl<A: std::hash::Hash + Eq + Clone, B: std::hash::Hash + Eq + Clone> BiMap<A, B> {
128 pub fn new() -> Self {
130 Self {
131 forward: std::collections::HashMap::new(),
132 backward: std::collections::HashMap::new(),
133 }
134 }
135 pub fn insert(&mut self, a: A, b: B) {
137 self.forward.insert(a.clone(), b.clone());
138 self.backward.insert(b, a);
139 }
140 pub fn get_b(&self, a: &A) -> Option<&B> {
142 self.forward.get(a)
143 }
144 pub fn get_a(&self, b: &B) -> Option<&A> {
146 self.backward.get(b)
147 }
148 pub fn len(&self) -> usize {
150 self.forward.len()
151 }
152 pub fn is_empty(&self) -> bool {
154 self.forward.is_empty()
155 }
156}
157#[allow(dead_code)]
159#[allow(missing_docs)]
160pub struct BeforeAfter<T> {
161 pub before: T,
163 pub after: T,
165}
166#[allow(dead_code)]
167impl<T: PartialEq> BeforeAfter<T> {
168 pub fn new(before: T, after: T) -> Self {
170 Self { before, after }
171 }
172 pub fn unchanged(&self) -> bool {
174 self.before == self.after
175 }
176}
177#[allow(dead_code)]
179pub struct SimpleLruCache<K: std::hash::Hash + Eq + Clone, V: Clone> {
180 capacity: usize,
181 map: std::collections::HashMap<K, usize>,
182 keys: Vec<K>,
183 vals: Vec<V>,
184 order: Vec<usize>,
185}
186#[allow(dead_code)]
187impl<K: std::hash::Hash + Eq + Clone, V: Clone> SimpleLruCache<K, V> {
188 pub fn new(capacity: usize) -> Self {
190 assert!(capacity > 0);
191 Self {
192 capacity,
193 map: std::collections::HashMap::new(),
194 keys: Vec::new(),
195 vals: Vec::new(),
196 order: Vec::new(),
197 }
198 }
199 pub fn put(&mut self, key: K, val: V) {
201 if let Some(&idx) = self.map.get(&key) {
210 self.vals[idx] = val;
213 self.order.retain(|&x| x != idx);
216 self.order.insert(0, idx);
217 return;
218 }
219 match self.order.last() {
220 Some(&evict_idx) if self.keys.len() >= self.capacity => {
224 let map_key = key.clone();
229 let removed = self.map.remove_entry(&self.keys[evict_idx]);
230 let old_key = std::mem::replace(&mut self.keys[evict_idx], key);
234 let old_val = std::mem::replace(&mut self.vals[evict_idx], val);
235 self.order.rotate_right(1);
236 self.map.insert(map_key, evict_idx);
239 drop((removed, old_key, old_val));
240 }
241 _ => {
244 let map_key = key.clone();
245 let idx = self.keys.len();
246 self.keys.reserve(1);
250 self.vals.reserve(1);
251 self.order.reserve(1);
252 self.keys.push(key);
253 self.vals.push(val);
254 self.order.insert(0, idx);
255 self.map.insert(map_key, idx);
256 }
257 }
258 }
259 pub fn get(&mut self, key: &K) -> Option<&V> {
261 let idx = *self.map.get(key)?;
264 self.order.retain(|&x| x != idx);
265 self.order.insert(0, idx);
266 Some(&self.vals[idx])
267 }
268 pub fn len(&self) -> usize {
270 self.keys.len()
271 }
272 pub fn is_empty(&self) -> bool {
274 self.keys.is_empty()
275 }
276}
277#[allow(dead_code)]
279pub struct SparseBitSet {
280 words: Vec<u64>,
281}
282#[allow(dead_code)]
283impl SparseBitSet {
284 pub fn new(capacity: usize) -> Self {
286 let words = capacity.div_ceil(64);
287 Self {
288 words: vec![0u64; words],
289 }
290 }
291 pub fn set(&mut self, i: usize) {
293 let word = i / 64;
294 let bit = i % 64;
295 if word < self.words.len() {
296 self.words[word] |= 1u64 << bit;
297 }
298 }
299 pub fn clear(&mut self, i: usize) {
301 let word = i / 64;
302 let bit = i % 64;
303 if word < self.words.len() {
304 self.words[word] &= !(1u64 << bit);
305 }
306 }
307 pub fn get(&self, i: usize) -> bool {
309 let word = i / 64;
310 let bit = i % 64;
311 self.words.get(word).is_some_and(|w| w & (1u64 << bit) != 0)
312 }
313 pub fn count_ones(&self) -> u32 {
315 self.words.iter().map(|w| w.count_ones()).sum()
316 }
317 pub fn union(&self, other: &SparseBitSet) -> SparseBitSet {
319 let len = self.words.len().max(other.words.len());
320 let mut result = SparseBitSet {
321 words: vec![0u64; len],
322 };
323 for i in 0..self.words.len() {
324 result.words[i] |= self.words[i];
325 }
326 for i in 0..other.words.len() {
327 result.words[i] |= other.words[i];
328 }
329 result
330 }
331}
332#[allow(dead_code)]
334pub struct FrequencyTable<T: std::hash::Hash + Eq + Clone> {
335 counts: std::collections::HashMap<T, u64>,
336}
337#[allow(dead_code)]
338impl<T: std::hash::Hash + Eq + Clone> FrequencyTable<T> {
339 pub fn new() -> Self {
341 Self {
342 counts: std::collections::HashMap::new(),
343 }
344 }
345 pub fn record(&mut self, item: T) {
347 *self.counts.entry(item).or_insert(0) += 1;
348 }
349 pub fn freq(&self, item: &T) -> u64 {
351 self.counts.get(item).copied().unwrap_or(0)
352 }
353 pub fn most_frequent(&self) -> Option<(&T, u64)> {
355 self.counts
356 .iter()
357 .max_by_key(|(_, &v)| v)
358 .map(|(k, &v)| (k, v))
359 }
360 pub fn total(&self) -> u64 {
362 self.counts.values().sum()
363 }
364 pub fn distinct(&self) -> usize {
366 self.counts.len()
367 }
368}
369#[allow(dead_code)]
371pub struct WorkStack<T> {
372 items: Vec<T>,
373}
374#[allow(dead_code)]
375impl<T> WorkStack<T> {
376 pub fn new() -> Self {
378 Self { items: Vec::new() }
379 }
380 pub fn push(&mut self, item: T) {
382 self.items.push(item);
383 }
384 pub fn pop(&mut self) -> Option<T> {
386 self.items.pop()
387 }
388 pub fn is_empty(&self) -> bool {
390 self.items.is_empty()
391 }
392 pub fn len(&self) -> usize {
394 self.items.len()
395 }
396}
397#[allow(dead_code)]
399pub struct SymmetricRelation {
400 pairs: std::collections::HashSet<(u64, u64)>,
401}
402#[allow(dead_code)]
403impl SymmetricRelation {
404 pub fn new() -> Self {
406 Self {
407 pairs: std::collections::HashSet::new(),
408 }
409 }
410 fn key(a: u64, b: u64) -> (u64, u64) {
411 if a <= b {
412 (a, b)
413 } else {
414 (b, a)
415 }
416 }
417 pub fn add(&mut self, a: u64, b: u64) {
419 self.pairs.insert(Self::key(a, b));
420 }
421 pub fn contains(&self, a: u64, b: u64) -> bool {
423 self.pairs.contains(&Self::key(a, b))
424 }
425 pub fn len(&self) -> usize {
427 self.pairs.len()
428 }
429 pub fn is_empty(&self) -> bool {
431 self.pairs.is_empty()
432 }
433}
434#[allow(dead_code)]
436pub struct MemoSlot<T: Clone> {
437 cached: Option<T>,
438}
439#[allow(dead_code)]
440impl<T: Clone> MemoSlot<T> {
441 pub fn new() -> Self {
443 Self { cached: None }
444 }
445 pub fn get_or_compute(&mut self, f: impl FnOnce() -> T) -> &T {
447 self.cached.get_or_insert_with(f)
448 }
449 pub fn invalidate(&mut self) {
451 self.cached = None;
452 }
453 pub fn is_cached(&self) -> bool {
455 self.cached.is_some()
456 }
457}
458#[allow(dead_code)]
460pub struct CheckedEquiv {
461 lhs: u64,
462 rhs: u64,
463 proof: EquivProofTerm,
464}
465#[allow(dead_code)]
466impl CheckedEquiv {
467 pub fn refl(id: u64) -> Self {
469 Self {
470 lhs: id,
471 rhs: id,
472 proof: EquivProofTerm::Refl(id),
473 }
474 }
475 pub fn by_axiom(lhs: u64, rhs: u64, name: impl Into<String>) -> Self {
477 Self {
478 lhs,
479 rhs,
480 proof: EquivProofTerm::Axiom(name.into()),
481 }
482 }
483 pub fn lhs(&self) -> u64 {
485 self.lhs
486 }
487 pub fn rhs(&self) -> u64 {
489 self.rhs
490 }
491 pub fn proof(&self) -> &EquivProofTerm {
493 &self.proof
494 }
495 pub fn is_trivial(&self) -> bool {
497 self.lhs == self.rhs
498 }
499}
500#[allow(dead_code)]
502pub struct EventCounter {
503 counts: std::collections::HashMap<String, u64>,
504}
505#[allow(dead_code)]
506impl EventCounter {
507 pub fn new() -> Self {
509 Self {
510 counts: std::collections::HashMap::new(),
511 }
512 }
513 pub fn inc(&mut self, event: &str) {
515 *self.counts.entry(event.to_string()).or_insert(0) += 1;
516 }
517 pub fn add(&mut self, event: &str, n: u64) {
519 *self.counts.entry(event.to_string()).or_insert(0) += n;
520 }
521 pub fn get(&self, event: &str) -> u64 {
523 self.counts.get(event).copied().unwrap_or(0)
524 }
525 pub fn total(&self) -> u64 {
527 self.counts.values().sum()
528 }
529 pub fn reset(&mut self) {
531 self.counts.clear();
532 }
533 pub fn event_names(&self) -> Vec<&str> {
535 self.counts.keys().map(|s| s.as_str()).collect()
536 }
537}
538#[allow(dead_code)]
540pub struct AnnotationTable {
541 map: std::collections::HashMap<String, Vec<String>>,
542}
543#[allow(dead_code)]
544impl AnnotationTable {
545 pub fn new() -> Self {
547 Self {
548 map: std::collections::HashMap::new(),
549 }
550 }
551 pub fn annotate(&mut self, key: impl Into<String>, val: impl Into<String>) {
553 self.map.entry(key.into()).or_default().push(val.into());
554 }
555 pub fn get_all(&self, key: &str) -> &[String] {
557 self.map.get(key).map(|v| v.as_slice()).unwrap_or(&[])
558 }
559 pub fn num_keys(&self) -> usize {
561 self.map.len()
562 }
563 pub fn has(&self, key: &str) -> bool {
565 self.map.contains_key(key)
566 }
567}
568#[allow(dead_code)]
570pub struct WorkQueue<T> {
571 items: std::collections::VecDeque<T>,
572}
573#[allow(dead_code)]
574impl<T> WorkQueue<T> {
575 pub fn new() -> Self {
577 Self {
578 items: std::collections::VecDeque::new(),
579 }
580 }
581 pub fn enqueue(&mut self, item: T) {
583 self.items.push_back(item);
584 }
585 pub fn dequeue(&mut self) -> Option<T> {
587 self.items.pop_front()
588 }
589 pub fn is_empty(&self) -> bool {
591 self.items.is_empty()
592 }
593 pub fn len(&self) -> usize {
595 self.items.len()
596 }
597}
598#[allow(dead_code)]
600pub struct IdDispenser<T> {
601 next: u32,
602 _phantom: std::marker::PhantomData<T>,
603}
604#[allow(dead_code)]
605impl<T> IdDispenser<T> {
606 pub fn new() -> Self {
608 Self {
609 next: 0,
610 _phantom: std::marker::PhantomData,
611 }
612 }
613 #[allow(clippy::should_implement_trait)]
615 pub fn next(&mut self) -> TypedId<T> {
616 let id = TypedId::new(self.next);
617 self.next += 1;
618 id
619 }
620 pub fn count(&self) -> u32 {
622 self.next
623 }
624}
625#[allow(dead_code)]
627pub struct Slot<T> {
628 inner: Option<T>,
629}
630#[allow(dead_code)]
631impl<T> Slot<T> {
632 pub fn empty() -> Self {
634 Self { inner: None }
635 }
636 pub fn fill(&mut self, val: T) {
638 assert!(self.inner.is_none(), "Slot: already filled");
639 self.inner = Some(val);
640 }
641 pub fn get(&self) -> Option<&T> {
643 self.inner.as_ref()
644 }
645 pub fn is_filled(&self) -> bool {
647 self.inner.is_some()
648 }
649 pub fn take(&mut self) -> Option<T> {
651 self.inner.take()
652 }
653 pub fn get_or_fill_with(&mut self, f: impl FnOnce() -> T) -> &T {
655 self.inner.get_or_insert_with(f)
656 }
657}
658#[allow(dead_code)]
660#[allow(missing_docs)]
661pub struct EquivClass {
662 pub repr: usize,
664 pub members: Vec<usize>,
666}
667#[allow(dead_code)]
668impl EquivClass {
669 pub fn singleton(id: usize) -> Self {
671 Self {
672 repr: id,
673 members: vec![id],
674 }
675 }
676 pub fn contains(&self, id: usize) -> bool {
678 self.members.contains(&id)
679 }
680 pub fn size(&self) -> usize {
682 self.members.len()
683 }
684}
685#[allow(dead_code)]
687#[derive(Debug, Clone)]
688pub enum EquivProofTerm {
689 Refl(u64),
691 Symm(Box<EquivProofTerm>),
693 Trans(Box<EquivProofTerm>, Box<EquivProofTerm>),
695 Axiom(String),
697}
698#[allow(dead_code)]
699impl EquivProofTerm {
700 pub fn depth(&self) -> usize {
702 match self {
703 EquivProofTerm::Refl(_) => 0,
704 EquivProofTerm::Axiom(_) => 0,
705 EquivProofTerm::Symm(p) => 1 + p.depth(),
706 EquivProofTerm::Trans(p, q) => 1 + p.depth().max(q.depth()),
707 }
708 }
709 pub fn is_refl(&self) -> bool {
711 matches!(self, EquivProofTerm::Refl(_))
712 }
713}
714#[derive(Debug)]
716struct UnionFindEntry {
717 parent: usize,
718 rank: u32,
719}
720#[derive(Clone, Debug, Default)]
722pub struct EquivStats {
723 pub total_queries: u64,
725 pub equiv_hits: u64,
727 pub failure_hits: u64,
729 pub cache_misses: u64,
731 pub equiv_additions: u64,
733 pub failure_additions: u64,
735}
736impl EquivStats {
737 pub fn new() -> Self {
739 Self::default()
740 }
741 pub fn record_equiv_hit(&mut self) {
743 self.total_queries += 1;
744 self.equiv_hits += 1;
745 }
746 pub fn record_failure_hit(&mut self) {
748 self.total_queries += 1;
749 self.failure_hits += 1;
750 }
751 pub fn record_miss(&mut self) {
753 self.total_queries += 1;
754 self.cache_misses += 1;
755 }
756 pub fn record_equiv_addition(&mut self) {
758 self.equiv_additions += 1;
759 }
760 pub fn record_failure_addition(&mut self) {
762 self.failure_additions += 1;
763 }
764 pub fn hit_rate(&self) -> f64 {
766 if self.total_queries == 0 {
767 return 1.0;
768 }
769 (self.equiv_hits + self.failure_hits) as f64 / self.total_queries as f64
770 }
771}
772#[allow(dead_code)]
774pub struct IntervalSet {
775 intervals: Vec<(i64, i64)>,
776}
777#[allow(dead_code)]
778impl IntervalSet {
779 pub fn new() -> Self {
781 Self {
782 intervals: Vec::new(),
783 }
784 }
785 pub fn add(&mut self, lo: i64, hi: i64) {
787 if lo > hi {
788 return;
789 }
790 let mut new_lo = lo;
791 let mut new_hi = hi;
792 let mut i = 0;
793 while i < self.intervals.len() {
794 let (il, ih) = self.intervals[i];
795 if ih < new_lo - 1 {
796 i += 1;
797 continue;
798 }
799 if il > new_hi + 1 {
800 break;
801 }
802 new_lo = new_lo.min(il);
803 new_hi = new_hi.max(ih);
804 self.intervals.remove(i);
805 }
806 self.intervals.insert(i, (new_lo, new_hi));
807 }
808 pub fn contains(&self, x: i64) -> bool {
810 self.intervals.iter().any(|&(lo, hi)| x >= lo && x <= hi)
811 }
812 pub fn num_intervals(&self) -> usize {
814 self.intervals.len()
815 }
816 pub fn cardinality(&self) -> i64 {
818 self.intervals.iter().map(|&(lo, hi)| hi - lo + 1).sum()
819 }
820}
821#[derive(Debug)]
830pub struct EquivManager {
831 equiv_set: HashSet<(Expr, Expr)>,
833 failure_set: HashSet<(Expr, Expr)>,
835 expr_to_idx: HashMap<Expr, usize>,
838 entries: Vec<UnionFindEntry>,
840}
841impl EquivManager {
842 pub fn new() -> Self {
844 EquivManager {
845 equiv_set: HashSet::new(),
846 failure_set: HashSet::new(),
847 expr_to_idx: HashMap::new(),
848 entries: Vec::new(),
849 }
850 }
851 pub fn add_equiv(&mut self, a: &Expr, b: &Expr) {
853 if a == b {
854 return;
855 }
856 let pair = canonicalize_pair(a.clone(), b.clone());
857 self.equiv_set.insert(pair);
858 let idx_a = self.get_or_create_idx(a);
859 let idx_b = self.get_or_create_idx(b);
860 self.union(idx_a, idx_b);
861 }
862 pub fn is_equiv(&mut self, a: &Expr, b: &Expr) -> bool {
864 if a == b {
865 return true;
866 }
867 let pair = canonicalize_pair(a.clone(), b.clone());
868 if self.equiv_set.contains(&pair) {
869 return true;
870 }
871 if let (Some(&idx_a), Some(&idx_b)) = (self.expr_to_idx.get(a), self.expr_to_idx.get(b)) {
872 return self.find(idx_a) == self.find(idx_b);
873 }
874 false
875 }
876 pub fn add_failure(&mut self, a: &Expr, b: &Expr) {
878 let pair = canonicalize_pair(a.clone(), b.clone());
879 self.failure_set.insert(pair);
880 }
881 pub fn is_failure(&self, a: &Expr, b: &Expr) -> bool {
883 let pair = canonicalize_pair(a.clone(), b.clone());
884 self.failure_set.contains(&pair)
885 }
886 pub fn clear(&mut self) {
888 self.equiv_set.clear();
889 self.failure_set.clear();
890 self.expr_to_idx.clear();
891 self.entries.clear();
892 }
893 pub fn num_equiv(&self) -> usize {
895 self.equiv_set.len()
896 }
897 pub fn num_failures(&self) -> usize {
899 self.failure_set.len()
900 }
901 fn get_or_create_idx(&mut self, e: &Expr) -> usize {
902 if let Some(&idx) = self.expr_to_idx.get(e) {
903 return idx;
904 }
905 let idx = self.entries.len();
906 self.entries.push(UnionFindEntry {
907 parent: idx,
908 rank: 0,
909 });
910 self.expr_to_idx.insert(e.clone(), idx);
911 idx
912 }
913 fn find(&mut self, mut x: usize) -> usize {
914 while self.entries[x].parent != x {
915 let grandparent = self.entries[self.entries[x].parent].parent;
916 self.entries[x].parent = grandparent;
917 x = grandparent;
918 }
919 x
920 }
921 fn union(&mut self, x: usize, y: usize) {
922 let rx = self.find(x);
923 let ry = self.find(y);
924 if rx == ry {
925 return;
926 }
927 match self.entries[rx].rank.cmp(&self.entries[ry].rank) {
928 std::cmp::Ordering::Less => {
929 self.entries[rx].parent = ry;
930 }
931 std::cmp::Ordering::Greater => {
932 self.entries[ry].parent = rx;
933 }
934 std::cmp::Ordering::Equal => {
935 self.entries[ry].parent = rx;
936 self.entries[rx].rank += 1;
937 }
938 }
939 }
940}
941#[derive(Clone, Debug, Default)]
943pub struct IndexEquivManager {
944 parent: Vec<usize>,
945 rank: Vec<u32>,
946}
947impl IndexEquivManager {
948 pub fn new(n: usize) -> Self {
950 Self {
951 parent: (0..n).collect(),
952 rank: vec![0; n],
953 }
954 }
955 pub fn find(&mut self, x: usize) -> usize {
957 if self.parent[x] != x {
958 self.parent[x] = self.find(self.parent[x]);
959 }
960 self.parent[x]
961 }
962 pub fn union(&mut self, x: usize, y: usize) {
964 let rx = self.find(x);
965 let ry = self.find(y);
966 if rx == ry {
967 return;
968 }
969 match self.rank[rx].cmp(&self.rank[ry]) {
970 std::cmp::Ordering::Less => self.parent[rx] = ry,
971 std::cmp::Ordering::Greater => self.parent[ry] = rx,
972 std::cmp::Ordering::Equal => {
973 self.parent[ry] = rx;
974 self.rank[rx] += 1;
975 }
976 }
977 }
978 pub fn same_class(&mut self, x: usize, y: usize) -> bool {
980 self.find(x) == self.find(y)
981 }
982 pub fn num_classes(&mut self) -> usize {
984 let n = self.parent.len();
985 let roots: std::collections::HashSet<usize> = (0..n).map(|i| self.find(i)).collect();
986 roots.len()
987 }
988}
989#[allow(dead_code)]
991pub struct DiagMeta {
992 pub(super) entries: Vec<(String, String)>,
993}
994#[allow(dead_code)]
995impl DiagMeta {
996 pub fn new() -> Self {
998 Self {
999 entries: Vec::new(),
1000 }
1001 }
1002 pub fn add(&mut self, key: impl Into<String>, val: impl Into<String>) {
1004 self.entries.push((key.into(), val.into()));
1005 }
1006 pub fn get(&self, key: &str) -> Option<&str> {
1008 self.entries
1009 .iter()
1010 .find(|(k, _)| k == key)
1011 .map(|(_, v)| v.as_str())
1012 }
1013 pub fn len(&self) -> usize {
1015 self.entries.len()
1016 }
1017 pub fn is_empty(&self) -> bool {
1019 self.entries.is_empty()
1020 }
1021}
1022#[allow(dead_code)]
1024#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1025pub struct TypedId<T> {
1026 pub(super) id: u32,
1027 _phantom: std::marker::PhantomData<T>,
1028}
1029#[allow(dead_code)]
1030impl<T> TypedId<T> {
1031 pub const fn new(id: u32) -> Self {
1033 Self {
1034 id,
1035 _phantom: std::marker::PhantomData,
1036 }
1037 }
1038 pub fn raw(&self) -> u32 {
1040 self.id
1041 }
1042}
1043#[allow(dead_code)]
1045#[allow(missing_docs)]
1046pub struct StatCache<K: std::hash::Hash + Eq + Clone, V: Clone> {
1047 pub inner: SimpleLruCache<K, V>,
1049 pub hits: u64,
1051 pub misses: u64,
1053}
1054#[allow(dead_code)]
1055impl<K: std::hash::Hash + Eq + Clone, V: Clone> StatCache<K, V> {
1056 pub fn new(capacity: usize) -> Self {
1058 Self {
1059 inner: SimpleLruCache::new(capacity),
1060 hits: 0,
1061 misses: 0,
1062 }
1063 }
1064 pub fn get(&mut self, key: &K) -> Option<&V> {
1066 let result = self.inner.get(key);
1067 if result.is_some() {
1068 self.hits += 1;
1069 } else {
1070 self.misses += 1;
1071 }
1072 None
1073 }
1074 pub fn put(&mut self, key: K, val: V) {
1076 self.inner.put(key, val);
1077 }
1078 pub fn hit_rate(&self) -> f64 {
1080 let total = self.hits + self.misses;
1081 if total == 0 {
1082 return 0.0;
1083 }
1084 self.hits as f64 / total as f64
1085 }
1086}
1087#[allow(dead_code)]
1089pub struct ScopeStack {
1090 names: Vec<String>,
1091}
1092#[allow(dead_code)]
1093impl ScopeStack {
1094 pub fn new() -> Self {
1096 Self { names: Vec::new() }
1097 }
1098 pub fn push(&mut self, name: impl Into<String>) {
1100 self.names.push(name.into());
1101 }
1102 pub fn pop(&mut self) -> Option<String> {
1104 self.names.pop()
1105 }
1106 pub fn current(&self) -> Option<&str> {
1108 self.names.last().map(|s| s.as_str())
1109 }
1110 pub fn depth(&self) -> usize {
1112 self.names.len()
1113 }
1114 pub fn is_empty(&self) -> bool {
1116 self.names.is_empty()
1117 }
1118 pub fn path(&self) -> String {
1120 self.names.join(".")
1121 }
1122}
1123#[allow(dead_code)]
1125#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1126pub struct SeqNum(u64);
1127#[allow(dead_code)]
1128impl SeqNum {
1129 pub const ZERO: SeqNum = SeqNum(0);
1131 pub fn next(self) -> SeqNum {
1133 SeqNum(self.0 + 1)
1134 }
1135 pub fn value(self) -> u64 {
1137 self.0
1138 }
1139}
1140#[derive(Debug, Default)]
1142pub struct InstrumentedEquivManager {
1143 inner: EquivManager,
1145 stats: EquivStats,
1147}
1148impl InstrumentedEquivManager {
1149 pub fn new() -> Self {
1151 Self::default()
1152 }
1153 pub fn add_equiv(&mut self, a: &Expr, b: &Expr) {
1155 self.stats.record_equiv_addition();
1156 self.inner.add_equiv(a, b);
1157 }
1158 pub fn add_failure(&mut self, a: &Expr, b: &Expr) {
1160 self.stats.record_failure_addition();
1161 self.inner.add_failure(a, b);
1162 }
1163 pub fn is_equiv(&mut self, a: &Expr, b: &Expr) -> bool {
1165 let result = self.inner.is_equiv(a, b);
1166 if result {
1167 self.stats.record_equiv_hit();
1168 } else {
1169 self.stats.record_miss();
1170 }
1171 result
1172 }
1173 pub fn is_failure(&self, a: &Expr, b: &Expr) -> bool {
1175 self.inner.is_failure(a, b)
1176 }
1177 pub fn stats(&self) -> &EquivStats {
1179 &self.stats
1180 }
1181 pub fn clear(&mut self) {
1183 self.inner.clear();
1184 self.stats = EquivStats::new();
1185 }
1186}
1187#[allow(dead_code)]
1189#[allow(missing_docs)]
1190pub struct EquivManagerStats {
1191 pub checks: u64,
1193 pub hits: u64,
1195 pub merges: u64,
1197 pub classes: usize,
1199}
1200#[allow(dead_code)]
1201impl EquivManagerStats {
1202 pub fn new() -> Self {
1204 Self {
1205 checks: 0,
1206 hits: 0,
1207 merges: 0,
1208 classes: 0,
1209 }
1210 }
1211 pub fn hit_rate(&self) -> f64 {
1213 if self.checks == 0 {
1214 return 0.0;
1215 }
1216 self.hits as f64 / self.checks as f64
1217 }
1218}
1219pub struct EquivQuery<'a> {
1221 manager: &'a EquivManager,
1222}
1223impl<'a> EquivQuery<'a> {
1224 pub fn new(manager: &'a EquivManager) -> Self {
1226 Self { manager }
1227 }
1228 pub fn is_known_failure(&self, a: &Expr, b: &Expr) -> bool {
1230 self.manager.is_failure(a, b)
1231 }
1232 pub fn num_equiv(&self) -> usize {
1234 self.manager.num_equiv()
1235 }
1236 pub fn num_failures(&self) -> usize {
1238 self.manager.num_failures()
1239 }
1240}
1241#[derive(Clone, Debug, Default)]
1246pub struct PersistentEquivManager {
1247 pairs: Vec<(String, String)>,
1248}
1249impl PersistentEquivManager {
1250 pub fn new() -> Self {
1252 Self::default()
1253 }
1254 pub fn add_equiv(&mut self, a: &Expr, b: &Expr) {
1256 let ka = format!("{:?}", a);
1257 let kb = format!("{:?}", b);
1258 let pair = if ka <= kb { (ka, kb) } else { (kb, ka) };
1259 if !self.pairs.contains(&pair) {
1260 self.pairs.push(pair);
1261 self.pairs.sort();
1262 }
1263 }
1264 pub fn is_equiv(&self, a: &Expr, b: &Expr) -> bool {
1266 if a == b {
1267 return true;
1268 }
1269 let ka = format!("{:?}", a);
1270 let kb = format!("{:?}", b);
1271 let pair = if ka <= kb {
1272 (ka.clone(), kb.clone())
1273 } else {
1274 (kb.clone(), ka.clone())
1275 };
1276 self.pairs.contains(&pair)
1277 }
1278 pub fn len(&self) -> usize {
1280 self.pairs.len()
1281 }
1282 pub fn is_empty(&self) -> bool {
1284 self.pairs.is_empty()
1285 }
1286 pub fn serialize(&self) -> Vec<(String, String)> {
1288 self.pairs.clone()
1289 }
1290}
1291#[allow(dead_code)]
1293pub struct ExprEquivCache {
1294 proven: std::collections::HashSet<(u64, u64)>,
1295 disproven: std::collections::HashSet<(u64, u64)>,
1296}
1297#[allow(dead_code)]
1298impl ExprEquivCache {
1299 pub fn new() -> Self {
1301 Self {
1302 proven: std::collections::HashSet::new(),
1303 disproven: std::collections::HashSet::new(),
1304 }
1305 }
1306 pub fn mark_equal(&mut self, a: u64, b: u64) {
1308 let key = if a <= b { (a, b) } else { (b, a) };
1309 self.proven.insert(key);
1310 }
1311 pub fn mark_unequal(&mut self, a: u64, b: u64) {
1313 let key = if a <= b { (a, b) } else { (b, a) };
1314 self.disproven.insert(key);
1315 }
1316 pub fn query(&self, a: u64, b: u64) -> Option<bool> {
1318 let key = if a <= b { (a, b) } else { (b, a) };
1319 if self.proven.contains(&key) {
1320 return Some(true);
1321 }
1322 if self.disproven.contains(&key) {
1323 return Some(false);
1324 }
1325 None
1326 }
1327 pub fn proven_count(&self) -> usize {
1329 self.proven.len()
1330 }
1331}
1332#[allow(dead_code)]
1334pub struct StringInterner {
1335 strings: Vec<String>,
1336 map: std::collections::HashMap<String, u32>,
1337}
1338#[allow(dead_code)]
1339impl StringInterner {
1340 pub fn new() -> Self {
1342 Self {
1343 strings: Vec::new(),
1344 map: std::collections::HashMap::new(),
1345 }
1346 }
1347 pub fn intern(&mut self, s: &str) -> u32 {
1349 if let Some(&id) = self.map.get(s) {
1350 return id;
1351 }
1352 let id = self.strings.len() as u32;
1353 self.strings.push(s.to_string());
1354 self.map.insert(s.to_string(), id);
1355 id
1356 }
1357 pub fn get(&self, id: u32) -> Option<&str> {
1359 self.strings.get(id as usize).map(|s| s.as_str())
1360 }
1361 pub fn len(&self) -> usize {
1363 self.strings.len()
1364 }
1365 pub fn is_empty(&self) -> bool {
1367 self.strings.is_empty()
1368 }
1369}
1370#[allow(dead_code)]
1372pub struct UnionFind {
1373 parent: Vec<usize>,
1374 rank: Vec<usize>,
1375 count: usize,
1376}
1377#[allow(dead_code)]
1378impl UnionFind {
1379 pub fn new(n: usize) -> Self {
1381 Self {
1382 parent: (0..n).collect(),
1383 rank: vec![0; n],
1384 count: n,
1385 }
1386 }
1387 pub fn find(&mut self, x: usize) -> usize {
1389 if self.parent[x] != x {
1390 self.parent[x] = self.find(self.parent[x]);
1391 }
1392 self.parent[x]
1393 }
1394 pub fn union(&mut self, x: usize, y: usize) -> bool {
1396 let rx = self.find(x);
1397 let ry = self.find(y);
1398 if rx == ry {
1399 return false;
1400 }
1401 if self.rank[rx] < self.rank[ry] {
1402 self.parent[rx] = ry;
1403 } else if self.rank[rx] > self.rank[ry] {
1404 self.parent[ry] = rx;
1405 } else {
1406 self.parent[ry] = rx;
1407 self.rank[rx] += 1;
1408 }
1409 self.count -= 1;
1410 true
1411 }
1412 pub fn same(&mut self, x: usize, y: usize) -> bool {
1414 self.find(x) == self.find(y)
1415 }
1416 pub fn num_classes(&self) -> usize {
1418 self.count
1419 }
1420 pub fn num_elements(&self) -> usize {
1422 self.parent.len()
1423 }
1424}
1425#[derive(Debug, Default)]
1430pub struct ScopedEquivManager {
1431 scopes: Vec<Vec<(Expr, Expr)>>,
1433 inner: EquivManager,
1435}
1436impl ScopedEquivManager {
1437 pub fn new() -> Self {
1439 Self {
1440 scopes: vec![Vec::new()],
1441 inner: EquivManager::new(),
1442 }
1443 }
1444 pub fn push_scope(&mut self) {
1446 self.scopes.push(Vec::new());
1447 }
1448 pub fn pop_scope(&mut self) {
1450 if self.scopes.len() > 1 {
1451 self.scopes.pop();
1452 self.inner.clear();
1453 for scope in &self.scopes {
1454 for (a, b) in scope {
1455 self.inner.add_equiv(a, b);
1456 }
1457 }
1458 }
1459 }
1460 pub fn add_equiv(&mut self, a: &Expr, b: &Expr) {
1462 if let Some(scope) = self.scopes.last_mut() {
1463 scope.push((a.clone(), b.clone()));
1464 }
1465 self.inner.add_equiv(a, b);
1466 }
1467 pub fn is_equiv(&mut self, a: &Expr, b: &Expr) -> bool {
1469 self.inner.is_equiv(a, b)
1470 }
1471 pub fn scope_depth(&self) -> usize {
1473 self.scopes.len()
1474 }
1475 pub fn total_equivs(&self) -> usize {
1477 self.scopes.iter().map(|s| s.len()).sum()
1478 }
1479}
1480#[allow(dead_code)]
1482pub struct RingBuffer<T> {
1483 data: Vec<Option<T>>,
1484 head: usize,
1485 tail: usize,
1486 count: usize,
1487 capacity: usize,
1488}
1489#[allow(dead_code)]
1490impl<T> RingBuffer<T> {
1491 pub fn new(capacity: usize) -> Self {
1493 let mut data = Vec::with_capacity(capacity);
1494 for _ in 0..capacity {
1495 data.push(None);
1496 }
1497 Self {
1498 data,
1499 head: 0,
1500 tail: 0,
1501 count: 0,
1502 capacity,
1503 }
1504 }
1505 pub fn push(&mut self, val: T) {
1507 if self.count == self.capacity {
1508 self.data[self.head] = Some(val);
1509 self.head = (self.head + 1) % self.capacity;
1510 self.tail = (self.tail + 1) % self.capacity;
1511 } else {
1512 self.data[self.tail] = Some(val);
1513 self.tail = (self.tail + 1) % self.capacity;
1514 self.count += 1;
1515 }
1516 }
1517 pub fn pop(&mut self) -> Option<T> {
1519 if self.count == 0 {
1520 return None;
1521 }
1522 let val = self.data[self.head].take();
1523 self.head = (self.head + 1) % self.capacity;
1524 self.count -= 1;
1525 val
1526 }
1527 pub fn len(&self) -> usize {
1529 self.count
1530 }
1531 pub fn is_empty(&self) -> bool {
1533 self.count == 0
1534 }
1535 pub fn is_full(&self) -> bool {
1537 self.count == self.capacity
1538 }
1539}