1use crate::{BinderInfo, Expr, Name};
6
7use std::collections::HashMap;
8
9#[allow(dead_code)]
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum EtaOutcome {
13 AlreadyNormal,
15 Reduced,
17 NotApplicable,
19 DepthLimitExceeded,
21}
22#[allow(dead_code)]
23impl EtaOutcome {
24 pub fn is_success(self) -> bool {
26 matches!(self, EtaOutcome::Reduced | EtaOutcome::AlreadyNormal)
27 }
28 pub fn label(self) -> &'static str {
30 match self {
31 EtaOutcome::AlreadyNormal => "already_normal",
32 EtaOutcome::Reduced => "reduced",
33 EtaOutcome::NotApplicable => "not_applicable",
34 EtaOutcome::DepthLimitExceeded => "depth_limit",
35 }
36 }
37}
38#[allow(dead_code)]
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub enum EtaStructure {
42 Atomic,
44 Lambda(usize),
46 App(usize),
48 EtaExpandable,
50}
51#[allow(dead_code)]
52impl EtaStructure {
53 pub fn arity(&self) -> usize {
55 match self {
56 EtaStructure::Lambda(n) | EtaStructure::App(n) => *n,
57 _ => 0,
58 }
59 }
60}
61#[allow(dead_code)]
63pub struct WindowIterator<'a, T> {
64 pub(super) data: &'a [T],
65 pub(super) pos: usize,
66 pub(super) window: usize,
67}
68#[allow(dead_code)]
69impl<'a, T> WindowIterator<'a, T> {
70 pub fn new(data: &'a [T], window: usize) -> Self {
72 Self {
73 data,
74 pos: 0,
75 window,
76 }
77 }
78}
79#[allow(dead_code)]
81pub struct StringPool {
82 free: Vec<String>,
83}
84#[allow(dead_code)]
85impl StringPool {
86 pub fn new() -> Self {
88 Self { free: Vec::new() }
89 }
90 pub fn take(&mut self) -> String {
92 self.free.pop().unwrap_or_default()
93 }
94 pub fn give(&mut self, mut s: String) {
96 s.clear();
97 self.free.push(s);
98 }
99 pub fn free_count(&self) -> usize {
101 self.free.len()
102 }
103}
104#[derive(Clone, Debug)]
106pub struct RewriteRule {
107 pub lhs: Expr,
109 pub rhs: Expr,
111 pub name: String,
113}
114impl RewriteRule {
115 pub fn new(name: impl Into<String>, lhs: Expr, rhs: Expr) -> Self {
117 Self {
118 lhs,
119 rhs,
120 name: name.into(),
121 }
122 }
123 pub fn apply_top(&self, expr: &Expr) -> Option<Expr> {
125 if expr == &self.lhs {
126 Some(self.rhs.clone())
127 } else {
128 None
129 }
130 }
131}
132#[allow(dead_code)]
134pub struct StatSummary {
135 count: u64,
136 sum: f64,
137 min: f64,
138 max: f64,
139}
140#[allow(dead_code)]
141impl StatSummary {
142 pub fn new() -> Self {
144 Self {
145 count: 0,
146 sum: 0.0,
147 min: f64::INFINITY,
148 max: f64::NEG_INFINITY,
149 }
150 }
151 pub fn record(&mut self, val: f64) {
153 self.count += 1;
154 self.sum += val;
155 if val < self.min {
156 self.min = val;
157 }
158 if val > self.max {
159 self.max = val;
160 }
161 }
162 pub fn mean(&self) -> Option<f64> {
164 if self.count == 0 {
165 None
166 } else {
167 Some(self.sum / self.count as f64)
168 }
169 }
170 pub fn min(&self) -> Option<f64> {
172 if self.count == 0 {
173 None
174 } else {
175 Some(self.min)
176 }
177 }
178 pub fn max(&self) -> Option<f64> {
180 if self.count == 0 {
181 None
182 } else {
183 Some(self.max)
184 }
185 }
186 pub fn count(&self) -> u64 {
188 self.count
189 }
190}
191#[allow(dead_code)]
193pub struct EtaChecker {
194 cache: EtaNormalCache,
195 log: EtaLog,
196}
197#[allow(dead_code)]
198impl EtaChecker {
199 pub fn new() -> Self {
201 Self {
202 cache: EtaNormalCache::new(),
203 log: EtaLog::new(),
204 }
205 }
206 pub fn is_eta_normal(&mut self, hash: u64) -> EtaOutcome {
208 if let Some(is_normal) = self.cache.query(hash) {
209 let outcome = if is_normal {
210 EtaOutcome::AlreadyNormal
211 } else {
212 EtaOutcome::NotApplicable
213 };
214 self.log.record(outcome);
215 return outcome;
216 }
217 self.cache.insert(hash, false);
218 self.log.record(EtaOutcome::NotApplicable);
219 EtaOutcome::NotApplicable
220 }
221 pub fn success_rate(&self) -> f64 {
223 self.log.success_rate()
224 }
225}
226#[allow(dead_code)]
228pub struct VersionedRecord<T: Clone> {
229 earlier: Vec<T>,
231 current: T,
233}
234#[allow(dead_code)]
235impl<T: Clone> VersionedRecord<T> {
236 pub fn new(initial: T) -> Self {
238 Self {
239 earlier: Vec::new(),
240 current: initial,
241 }
242 }
243 pub fn update(&mut self, val: T) {
245 let previous = std::mem::replace(&mut self.current, val);
246 self.earlier.push(previous);
247 }
248 pub fn current(&self) -> &T {
250 &self.current
251 }
252 pub fn at_version(&self, n: usize) -> Option<&T> {
254 match self.earlier.get(n) {
255 Some(value) => Some(value),
256 None if n == self.earlier.len() => Some(&self.current),
257 None => None,
258 }
259 }
260 pub fn version(&self) -> usize {
262 self.earlier.len()
263 }
264 pub fn has_history(&self) -> bool {
266 !self.earlier.is_empty()
267 }
268}
269#[allow(dead_code)]
271#[allow(missing_docs)]
272pub struct EtaJob {
273 pub id: u64,
275 pub hash: u64,
277 pub prio: u32,
279}
280#[allow(dead_code)]
281impl EtaJob {
282 pub fn new(id: u64, hash: u64, prio: u32) -> Self {
284 Self { id, hash, prio }
285 }
286}
287#[allow(dead_code)]
289#[allow(missing_docs)]
290pub struct EtaRewriteRule {
291 pub lhs: String,
293 pub rhs: String,
295}
296#[allow(dead_code)]
297impl EtaRewriteRule {
298 pub fn new(lhs: impl Into<String>, rhs: impl Into<String>) -> Self {
300 Self {
301 lhs: lhs.into(),
302 rhs: rhs.into(),
303 }
304 }
305}
306#[derive(Debug, Default, Clone)]
308pub struct LambdaStats {
309 pub lambda_count: usize,
311 pub pi_count: usize,
313 pub app_count: usize,
315 pub let_count: usize,
317 pub max_depth: usize,
319}
320impl LambdaStats {
321 pub fn compute(expr: &Expr) -> Self {
323 let mut stats = Self::default();
324 Self::walk(expr, &mut stats, 0);
325 stats
326 }
327 fn walk(expr: &Expr, stats: &mut Self, depth: usize) {
328 if depth > stats.max_depth {
329 stats.max_depth = depth;
330 }
331 match expr {
332 Expr::Lam(_, _, ty, body) => {
333 stats.lambda_count += 1;
334 Self::walk(ty, stats, depth + 1);
335 Self::walk(body, stats, depth + 1);
336 }
337 Expr::Pi(_, _, ty, body) => {
338 stats.pi_count += 1;
339 Self::walk(ty, stats, depth + 1);
340 Self::walk(body, stats, depth + 1);
341 }
342 Expr::App(f, a) => {
343 stats.app_count += 1;
344 Self::walk(f, stats, depth + 1);
345 Self::walk(a, stats, depth + 1);
346 }
347 Expr::Let(_, ty, val, body) => {
348 stats.let_count += 1;
349 Self::walk(ty, stats, depth + 1);
350 Self::walk(val, stats, depth + 1);
351 Self::walk(body, stats, depth + 1);
352 }
353 _ => {}
354 }
355 }
356 pub fn total_binders(&self) -> usize {
358 self.lambda_count + self.pi_count + self.let_count
359 }
360}
361#[allow(dead_code)]
363pub struct TransitiveClosure {
364 adj: Vec<Vec<usize>>,
365 n: usize,
366}
367#[allow(dead_code)]
368impl TransitiveClosure {
369 pub fn new(n: usize) -> Self {
371 Self {
372 adj: vec![Vec::new(); n],
373 n,
374 }
375 }
376 pub fn add_edge(&mut self, from: usize, to: usize) {
378 if from < self.n {
379 self.adj[from].push(to);
380 }
381 }
382 pub fn reachable_from(&self, start: usize) -> Vec<usize> {
384 let mut visited = vec![false; self.n];
385 let mut queue = std::collections::VecDeque::new();
386 queue.push_back(start);
387 while let Some(node) = queue.pop_front() {
388 if node >= self.n || visited[node] {
389 continue;
390 }
391 visited[node] = true;
392 for &next in &self.adj[node] {
393 queue.push_back(next);
394 }
395 }
396 (0..self.n).filter(|&i| visited[i]).collect()
397 }
398 pub fn can_reach(&self, from: usize, to: usize) -> bool {
400 self.reachable_from(from).contains(&to)
401 }
402}
403#[allow(dead_code)]
405pub struct LabelSet {
406 labels: Vec<String>,
407}
408#[allow(dead_code)]
409impl LabelSet {
410 pub fn new() -> Self {
412 Self { labels: Vec::new() }
413 }
414 pub fn add(&mut self, label: impl Into<String>) {
416 let s = label.into();
417 if !self.labels.contains(&s) {
418 self.labels.push(s);
419 }
420 }
421 pub fn has(&self, label: &str) -> bool {
423 self.labels.iter().any(|l| l == label)
424 }
425 pub fn count(&self) -> usize {
427 self.labels.len()
428 }
429 pub fn all(&self) -> &[String] {
431 &self.labels
432 }
433}
434#[allow(dead_code)]
436pub struct EtaNormalCache {
437 cache: std::collections::HashMap<u64, bool>,
438}
439#[allow(dead_code)]
440impl EtaNormalCache {
441 pub fn new() -> Self {
443 Self {
444 cache: std::collections::HashMap::new(),
445 }
446 }
447 pub fn insert(&mut self, hash: u64, is_normal: bool) {
449 self.cache.insert(hash, is_normal);
450 }
451 pub fn query(&self, hash: u64) -> Option<bool> {
453 self.cache.get(&hash).copied()
454 }
455 pub fn clear(&mut self) {
457 self.cache.clear();
458 }
459 pub fn len(&self) -> usize {
461 self.cache.len()
462 }
463 pub fn is_empty(&self) -> bool {
465 self.len() == 0
466 }
467}
468#[allow(dead_code)]
470pub struct TransformStat {
471 before: StatSummary,
472 after: StatSummary,
473}
474#[allow(dead_code)]
475impl TransformStat {
476 pub fn new() -> Self {
478 Self {
479 before: StatSummary::new(),
480 after: StatSummary::new(),
481 }
482 }
483 pub fn record_before(&mut self, v: f64) {
485 self.before.record(v);
486 }
487 pub fn record_after(&mut self, v: f64) {
489 self.after.record(v);
490 }
491 pub fn mean_ratio(&self) -> Option<f64> {
493 let b = self.before.mean()?;
494 let a = self.after.mean()?;
495 if b.abs() < f64::EPSILON {
496 return None;
497 }
498 Some(a / b)
499 }
500}
501#[allow(dead_code)]
503pub struct TokenBucket {
504 capacity: u64,
505 tokens: u64,
506 refill_per_ms: u64,
507 last_refill: crate::wall_clock::Instant,
508}
509#[allow(dead_code)]
510impl TokenBucket {
511 pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
513 Self {
514 capacity,
515 tokens: capacity,
516 refill_per_ms,
517 last_refill: crate::wall_clock::Instant::now(),
518 }
519 }
520 pub fn try_consume(&mut self, n: u64) -> bool {
522 self.refill();
523 if self.tokens >= n {
524 self.tokens -= n;
525 true
526 } else {
527 false
528 }
529 }
530 fn refill(&mut self) {
531 let now = crate::wall_clock::Instant::now();
532 let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
533 if elapsed_ms > 0 {
534 let new_tokens = elapsed_ms * self.refill_per_ms;
535 self.tokens = (self.tokens + new_tokens).min(self.capacity);
536 self.last_refill = now;
537 }
538 }
539 pub fn available(&self) -> u64 {
541 self.tokens
542 }
543 pub fn capacity(&self) -> u64 {
545 self.capacity
546 }
547}
548#[allow(dead_code)]
550pub struct NonEmptyVec<T> {
551 head: T,
552 tail: Vec<T>,
553}
554#[allow(dead_code)]
555impl<T> NonEmptyVec<T> {
556 pub fn singleton(val: T) -> Self {
558 Self {
559 head: val,
560 tail: Vec::new(),
561 }
562 }
563 pub fn push(&mut self, val: T) {
565 self.tail.push(val);
566 }
567 pub fn first(&self) -> &T {
569 &self.head
570 }
571 pub fn last(&self) -> &T {
573 self.tail.last().unwrap_or(&self.head)
574 }
575 pub fn len(&self) -> usize {
577 1 + self.tail.len()
578 }
579 pub fn is_empty(&self) -> bool {
581 self.len() == 0
582 }
583 pub fn to_vec(&self) -> Vec<&T> {
585 let mut v = vec![&self.head];
586 v.extend(self.tail.iter());
587 v
588 }
589}
590#[allow(dead_code)]
592pub struct RawFnPtr {
593 ptr: usize,
595 pub(super) arity: usize,
596 name: String,
597}
598#[allow(dead_code)]
599impl RawFnPtr {
600 pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
602 Self {
603 ptr,
604 arity,
605 name: name.into(),
606 }
607 }
608 pub fn arity(&self) -> usize {
610 self.arity
611 }
612 pub fn name(&self) -> &str {
614 &self.name
615 }
616 pub fn raw(&self) -> usize {
618 self.ptr
619 }
620}
621#[allow(dead_code)]
623pub struct FocusStack<T> {
624 items: Vec<T>,
625}
626#[allow(dead_code)]
627impl<T> FocusStack<T> {
628 pub fn new() -> Self {
630 Self { items: Vec::new() }
631 }
632 pub fn focus(&mut self, item: T) {
634 self.items.push(item);
635 }
636 pub fn blur(&mut self) -> Option<T> {
638 self.items.pop()
639 }
640 pub fn current(&self) -> Option<&T> {
642 self.items.last()
643 }
644 pub fn depth(&self) -> usize {
646 self.items.len()
647 }
648 pub fn is_empty(&self) -> bool {
650 self.items.is_empty()
651 }
652}
653#[allow(dead_code)]
655#[derive(Debug, Clone)]
656pub struct EtaNormInfo {
657 pub normalized: Expr,
659 pub contractions: usize,
661 pub already_normal: bool,
663}
664impl EtaNormInfo {
665 pub fn already_normal(expr: Expr) -> Self {
667 Self {
668 normalized: expr,
669 contractions: 0,
670 already_normal: true,
671 }
672 }
673 pub fn contracted(expr: Expr, n: usize) -> Self {
675 Self {
676 normalized: expr,
677 contractions: n,
678 already_normal: false,
679 }
680 }
681}
682#[allow(dead_code)]
684pub struct SimpleDag {
685 edges: Vec<Vec<usize>>,
687}
688#[allow(dead_code)]
689impl SimpleDag {
690 pub fn new(n: usize) -> Self {
692 Self {
693 edges: vec![Vec::new(); n],
694 }
695 }
696 pub fn add_edge(&mut self, from: usize, to: usize) {
698 if from < self.edges.len() {
699 self.edges[from].push(to);
700 }
701 }
702 pub fn successors(&self, node: usize) -> &[usize] {
704 self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
705 }
706 pub fn can_reach(&self, from: usize, to: usize) -> bool {
708 let mut visited = vec![false; self.edges.len()];
709 self.dfs(from, to, &mut visited)
710 }
711 fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
712 if cur == target {
713 return true;
714 }
715 if cur >= visited.len() || visited[cur] {
716 return false;
717 }
718 visited[cur] = true;
719 for &next in self.successors(cur) {
720 if self.dfs(next, target, visited) {
721 return true;
722 }
723 }
724 false
725 }
726 pub fn topological_sort(&self) -> Option<Vec<usize>> {
728 let n = self.edges.len();
729 let mut in_degree = vec![0usize; n];
730 for succs in &self.edges {
731 for &s in succs {
732 if s < n {
733 in_degree[s] += 1;
734 }
735 }
736 }
737 let mut queue: std::collections::VecDeque<usize> =
738 (0..n).filter(|&i| in_degree[i] == 0).collect();
739 let mut order = Vec::new();
740 while let Some(node) = queue.pop_front() {
741 order.push(node);
742 for &s in self.successors(node) {
743 if s < n {
744 in_degree[s] -= 1;
745 if in_degree[s] == 0 {
746 queue.push_back(s);
747 }
748 }
749 }
750 }
751 if order.len() == n {
752 Some(order)
753 } else {
754 None
755 }
756 }
757 pub fn num_nodes(&self) -> usize {
759 self.edges.len()
760 }
761}
762#[allow(dead_code)]
764pub struct SlidingSum {
765 window: Vec<f64>,
766 capacity: usize,
767 pos: usize,
768 sum: f64,
769 count: usize,
770}
771#[allow(dead_code)]
772impl SlidingSum {
773 pub fn new(capacity: usize) -> Self {
775 Self {
776 window: vec![0.0; capacity],
777 capacity,
778 pos: 0,
779 sum: 0.0,
780 count: 0,
781 }
782 }
783 pub fn push(&mut self, val: f64) {
785 let oldest = self.window[self.pos];
786 self.sum -= oldest;
787 self.sum += val;
788 self.window[self.pos] = val;
789 self.pos = (self.pos + 1) % self.capacity;
790 if self.count < self.capacity {
791 self.count += 1;
792 }
793 }
794 pub fn sum(&self) -> f64 {
796 self.sum
797 }
798 pub fn mean(&self) -> Option<f64> {
800 if self.count == 0 {
801 None
802 } else {
803 Some(self.sum / self.count as f64)
804 }
805 }
806 pub fn count(&self) -> usize {
808 self.count
809 }
810}
811#[allow(dead_code)]
813#[allow(missing_docs)]
814pub struct EtaReductionStats {
815 pub reductions: u64,
817 pub examined: u64,
819 pub expansions: u64,
821 pub max_depth: usize,
823}
824#[allow(dead_code)]
825impl EtaReductionStats {
826 pub fn new() -> Self {
828 Self {
829 reductions: 0,
830 examined: 0,
831 expansions: 0,
832 max_depth: 0,
833 }
834 }
835 pub fn ratio(&self) -> f64 {
837 if self.examined == 0 {
838 return 0.0;
839 }
840 self.reductions as f64 / self.examined as f64
841 }
842}
843#[allow(dead_code)]
845pub struct PathBuf {
846 components: Vec<String>,
847}
848#[allow(dead_code)]
849impl PathBuf {
850 pub fn new() -> Self {
852 Self {
853 components: Vec::new(),
854 }
855 }
856 pub fn push(&mut self, comp: impl Into<String>) {
858 self.components.push(comp.into());
859 }
860 pub fn pop(&mut self) {
862 self.components.pop();
863 }
864 pub fn as_str(&self) -> String {
866 self.components.join("/")
867 }
868 pub fn depth(&self) -> usize {
870 self.components.len()
871 }
872 pub fn clear(&mut self) {
874 self.components.clear();
875 }
876}
877#[allow(dead_code)]
879pub struct EtaJobQueue {
880 jobs: Vec<EtaJob>,
881}
882#[allow(dead_code)]
883impl EtaJobQueue {
884 pub fn new() -> Self {
886 Self { jobs: Vec::new() }
887 }
888 pub fn enqueue(&mut self, job: EtaJob) {
890 let pos = self.jobs.partition_point(|j| j.prio >= job.prio);
891 self.jobs.insert(pos, job);
892 }
893 pub fn dequeue(&mut self) -> Option<EtaJob> {
895 if self.jobs.is_empty() {
896 None
897 } else {
898 Some(self.jobs.remove(0))
899 }
900 }
901 pub fn len(&self) -> usize {
903 self.jobs.len()
904 }
905 pub fn is_empty(&self) -> bool {
907 self.jobs.is_empty()
908 }
909}
910#[allow(dead_code)]
912pub struct ConfigNode {
913 key: String,
914 value: Option<String>,
915 children: Vec<ConfigNode>,
916}
917#[allow(dead_code)]
918impl ConfigNode {
919 pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
921 Self {
922 key: key.into(),
923 value: Some(value.into()),
924 children: Vec::new(),
925 }
926 }
927 pub fn section(key: impl Into<String>) -> Self {
929 Self {
930 key: key.into(),
931 value: None,
932 children: Vec::new(),
933 }
934 }
935 pub fn add_child(&mut self, child: ConfigNode) {
937 self.children.push(child);
938 }
939 pub fn key(&self) -> &str {
941 &self.key
942 }
943 pub fn value(&self) -> Option<&str> {
945 self.value.as_deref()
946 }
947 pub fn num_children(&self) -> usize {
949 self.children.len()
950 }
951 pub fn lookup(&self, path: &str) -> Option<&str> {
953 let mut parts = path.splitn(2, '.');
954 let head = parts.next()?;
955 let tail = parts.next();
956 if head != self.key {
957 return None;
958 }
959 match tail {
960 None => self.value.as_deref(),
961 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
962 }
963 }
964 fn lookup_relative(&self, path: &str) -> Option<&str> {
965 let mut parts = path.splitn(2, '.');
966 let head = parts.next()?;
967 let tail = parts.next();
968 if head != self.key {
969 return None;
970 }
971 match tail {
972 None => self.value.as_deref(),
973 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
974 }
975 }
976}
977#[allow(dead_code)]
979pub struct EtaLog {
980 entries: Vec<(crate::wall_clock::Instant, EtaOutcome)>,
981}
982#[allow(dead_code)]
983impl EtaLog {
984 pub fn new() -> Self {
986 Self {
987 entries: Vec::new(),
988 }
989 }
990 pub fn record(&mut self, outcome: EtaOutcome) {
992 self.entries
993 .push((crate::wall_clock::Instant::now(), outcome));
994 }
995 pub fn len(&self) -> usize {
997 self.entries.len()
998 }
999 pub fn is_empty(&self) -> bool {
1001 self.len() == 0
1002 }
1003 pub fn count(&self, target: EtaOutcome) -> usize {
1005 self.entries.iter().filter(|(_, o)| *o == target).count()
1006 }
1007 pub fn success_rate(&self) -> f64 {
1009 if self.entries.is_empty() {
1010 return 0.0;
1011 }
1012 let successes = self.entries.iter().filter(|(_, o)| o.is_success()).count();
1013 successes as f64 / self.entries.len() as f64
1014 }
1015}
1016#[allow(dead_code)]
1018pub struct SmallMap<K: Ord + Clone, V: Clone> {
1019 entries: Vec<(K, V)>,
1020}
1021#[allow(dead_code)]
1022impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
1023 pub fn new() -> Self {
1025 Self {
1026 entries: Vec::new(),
1027 }
1028 }
1029 pub fn insert(&mut self, key: K, val: V) {
1031 match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
1032 Ok(i) => self.entries[i].1 = val,
1033 Err(i) => self.entries.insert(i, (key, val)),
1034 }
1035 }
1036 pub fn get(&self, key: &K) -> Option<&V> {
1038 self.entries
1039 .binary_search_by_key(&key, |(k, _)| k)
1040 .ok()
1041 .map(|i| &self.entries[i].1)
1042 }
1043 pub fn len(&self) -> usize {
1045 self.entries.len()
1046 }
1047 pub fn is_empty(&self) -> bool {
1049 self.entries.is_empty()
1050 }
1051 pub fn keys(&self) -> Vec<&K> {
1053 self.entries.iter().map(|(k, _)| k).collect()
1054 }
1055 pub fn values(&self) -> Vec<&V> {
1057 self.entries.iter().map(|(_, v)| v).collect()
1058 }
1059}
1060#[allow(dead_code)]
1062pub struct EtaStatCounter {
1063 count: u64,
1064 sum: i64,
1065 min: i64,
1066 max: i64,
1067}
1068#[allow(dead_code)]
1069impl EtaStatCounter {
1070 pub fn new() -> Self {
1072 Self {
1073 count: 0,
1074 sum: 0,
1075 min: i64::MAX,
1076 max: i64::MIN,
1077 }
1078 }
1079 pub fn record(&mut self, v: i64) {
1081 self.count += 1;
1082 self.sum += v;
1083 if v < self.min {
1084 self.min = v;
1085 }
1086 if v > self.max {
1087 self.max = v;
1088 }
1089 }
1090 pub fn mean(&self) -> Option<f64> {
1092 if self.count == 0 {
1093 None
1094 } else {
1095 Some(self.sum as f64 / self.count as f64)
1096 }
1097 }
1098 pub fn count(&self) -> u64 {
1100 self.count
1101 }
1102}
1103#[allow(dead_code)]
1105pub struct EtaOpCounter {
1106 counts: std::collections::HashMap<String, u64>,
1107}
1108#[allow(dead_code)]
1109impl EtaOpCounter {
1110 pub fn new() -> Self {
1112 Self {
1113 counts: std::collections::HashMap::new(),
1114 }
1115 }
1116 pub fn inc(&mut self, op: &str) {
1118 *self.counts.entry(op.to_string()).or_insert(0) += 1;
1119 }
1120 pub fn get(&self, op: &str) -> u64 {
1122 self.counts.get(op).copied().unwrap_or(0)
1123 }
1124 pub fn total(&self) -> u64 {
1126 self.counts.values().sum()
1127 }
1128}