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 history: Vec<T>,
230}
231#[allow(dead_code)]
232impl<T: Clone> VersionedRecord<T> {
233 pub fn new(initial: T) -> Self {
235 Self {
236 history: vec![initial],
237 }
238 }
239 pub fn update(&mut self, val: T) {
241 self.history.push(val);
242 }
243 pub fn current(&self) -> &T {
245 self.history
246 .last()
247 .expect("VersionedRecord history is always non-empty after construction")
248 }
249 pub fn at_version(&self, n: usize) -> Option<&T> {
251 self.history.get(n)
252 }
253 pub fn version(&self) -> usize {
255 self.history.len() - 1
256 }
257 pub fn has_history(&self) -> bool {
259 self.history.len() > 1
260 }
261}
262#[allow(dead_code)]
264#[allow(missing_docs)]
265pub struct EtaJob {
266 pub id: u64,
268 pub hash: u64,
270 pub prio: u32,
272}
273#[allow(dead_code)]
274impl EtaJob {
275 pub fn new(id: u64, hash: u64, prio: u32) -> Self {
277 Self { id, hash, prio }
278 }
279}
280#[allow(dead_code)]
282#[allow(missing_docs)]
283pub struct EtaRewriteRule {
284 pub lhs: String,
286 pub rhs: String,
288}
289#[allow(dead_code)]
290impl EtaRewriteRule {
291 pub fn new(lhs: impl Into<String>, rhs: impl Into<String>) -> Self {
293 Self {
294 lhs: lhs.into(),
295 rhs: rhs.into(),
296 }
297 }
298}
299#[derive(Debug, Default, Clone)]
301pub struct LambdaStats {
302 pub lambda_count: usize,
304 pub pi_count: usize,
306 pub app_count: usize,
308 pub let_count: usize,
310 pub max_depth: usize,
312}
313impl LambdaStats {
314 pub fn compute(expr: &Expr) -> Self {
316 let mut stats = Self::default();
317 Self::walk(expr, &mut stats, 0);
318 stats
319 }
320 fn walk(expr: &Expr, stats: &mut Self, depth: usize) {
321 if depth > stats.max_depth {
322 stats.max_depth = depth;
323 }
324 match expr {
325 Expr::Lam(_, _, ty, body) => {
326 stats.lambda_count += 1;
327 Self::walk(ty, stats, depth + 1);
328 Self::walk(body, stats, depth + 1);
329 }
330 Expr::Pi(_, _, ty, body) => {
331 stats.pi_count += 1;
332 Self::walk(ty, stats, depth + 1);
333 Self::walk(body, stats, depth + 1);
334 }
335 Expr::App(f, a) => {
336 stats.app_count += 1;
337 Self::walk(f, stats, depth + 1);
338 Self::walk(a, stats, depth + 1);
339 }
340 Expr::Let(_, ty, val, body) => {
341 stats.let_count += 1;
342 Self::walk(ty, stats, depth + 1);
343 Self::walk(val, stats, depth + 1);
344 Self::walk(body, stats, depth + 1);
345 }
346 _ => {}
347 }
348 }
349 pub fn total_binders(&self) -> usize {
351 self.lambda_count + self.pi_count + self.let_count
352 }
353}
354#[allow(dead_code)]
356pub struct TransitiveClosure {
357 adj: Vec<Vec<usize>>,
358 n: usize,
359}
360#[allow(dead_code)]
361impl TransitiveClosure {
362 pub fn new(n: usize) -> Self {
364 Self {
365 adj: vec![Vec::new(); n],
366 n,
367 }
368 }
369 pub fn add_edge(&mut self, from: usize, to: usize) {
371 if from < self.n {
372 self.adj[from].push(to);
373 }
374 }
375 pub fn reachable_from(&self, start: usize) -> Vec<usize> {
377 let mut visited = vec![false; self.n];
378 let mut queue = std::collections::VecDeque::new();
379 queue.push_back(start);
380 while let Some(node) = queue.pop_front() {
381 if node >= self.n || visited[node] {
382 continue;
383 }
384 visited[node] = true;
385 for &next in &self.adj[node] {
386 queue.push_back(next);
387 }
388 }
389 (0..self.n).filter(|&i| visited[i]).collect()
390 }
391 pub fn can_reach(&self, from: usize, to: usize) -> bool {
393 self.reachable_from(from).contains(&to)
394 }
395}
396#[allow(dead_code)]
398pub struct LabelSet {
399 labels: Vec<String>,
400}
401#[allow(dead_code)]
402impl LabelSet {
403 pub fn new() -> Self {
405 Self { labels: Vec::new() }
406 }
407 pub fn add(&mut self, label: impl Into<String>) {
409 let s = label.into();
410 if !self.labels.contains(&s) {
411 self.labels.push(s);
412 }
413 }
414 pub fn has(&self, label: &str) -> bool {
416 self.labels.iter().any(|l| l == label)
417 }
418 pub fn count(&self) -> usize {
420 self.labels.len()
421 }
422 pub fn all(&self) -> &[String] {
424 &self.labels
425 }
426}
427#[allow(dead_code)]
429pub struct EtaNormalCache {
430 cache: std::collections::HashMap<u64, bool>,
431}
432#[allow(dead_code)]
433impl EtaNormalCache {
434 pub fn new() -> Self {
436 Self {
437 cache: std::collections::HashMap::new(),
438 }
439 }
440 pub fn insert(&mut self, hash: u64, is_normal: bool) {
442 self.cache.insert(hash, is_normal);
443 }
444 pub fn query(&self, hash: u64) -> Option<bool> {
446 self.cache.get(&hash).copied()
447 }
448 pub fn clear(&mut self) {
450 self.cache.clear();
451 }
452 pub fn len(&self) -> usize {
454 self.cache.len()
455 }
456 pub fn is_empty(&self) -> bool {
458 self.len() == 0
459 }
460}
461#[allow(dead_code)]
463pub struct TransformStat {
464 before: StatSummary,
465 after: StatSummary,
466}
467#[allow(dead_code)]
468impl TransformStat {
469 pub fn new() -> Self {
471 Self {
472 before: StatSummary::new(),
473 after: StatSummary::new(),
474 }
475 }
476 pub fn record_before(&mut self, v: f64) {
478 self.before.record(v);
479 }
480 pub fn record_after(&mut self, v: f64) {
482 self.after.record(v);
483 }
484 pub fn mean_ratio(&self) -> Option<f64> {
486 let b = self.before.mean()?;
487 let a = self.after.mean()?;
488 if b.abs() < f64::EPSILON {
489 return None;
490 }
491 Some(a / b)
492 }
493}
494#[allow(dead_code)]
496pub struct TokenBucket {
497 capacity: u64,
498 tokens: u64,
499 refill_per_ms: u64,
500 last_refill: std::time::Instant,
501}
502#[allow(dead_code)]
503impl TokenBucket {
504 pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
506 Self {
507 capacity,
508 tokens: capacity,
509 refill_per_ms,
510 last_refill: std::time::Instant::now(),
511 }
512 }
513 pub fn try_consume(&mut self, n: u64) -> bool {
515 self.refill();
516 if self.tokens >= n {
517 self.tokens -= n;
518 true
519 } else {
520 false
521 }
522 }
523 fn refill(&mut self) {
524 let now = std::time::Instant::now();
525 let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
526 if elapsed_ms > 0 {
527 let new_tokens = elapsed_ms * self.refill_per_ms;
528 self.tokens = (self.tokens + new_tokens).min(self.capacity);
529 self.last_refill = now;
530 }
531 }
532 pub fn available(&self) -> u64 {
534 self.tokens
535 }
536 pub fn capacity(&self) -> u64 {
538 self.capacity
539 }
540}
541#[allow(dead_code)]
543pub struct NonEmptyVec<T> {
544 head: T,
545 tail: Vec<T>,
546}
547#[allow(dead_code)]
548impl<T> NonEmptyVec<T> {
549 pub fn singleton(val: T) -> Self {
551 Self {
552 head: val,
553 tail: Vec::new(),
554 }
555 }
556 pub fn push(&mut self, val: T) {
558 self.tail.push(val);
559 }
560 pub fn first(&self) -> &T {
562 &self.head
563 }
564 pub fn last(&self) -> &T {
566 self.tail.last().unwrap_or(&self.head)
567 }
568 pub fn len(&self) -> usize {
570 1 + self.tail.len()
571 }
572 pub fn is_empty(&self) -> bool {
574 self.len() == 0
575 }
576 pub fn to_vec(&self) -> Vec<&T> {
578 let mut v = vec![&self.head];
579 v.extend(self.tail.iter());
580 v
581 }
582}
583#[allow(dead_code)]
585pub struct RawFnPtr {
586 ptr: usize,
588 pub(super) arity: usize,
589 name: String,
590}
591#[allow(dead_code)]
592impl RawFnPtr {
593 pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
595 Self {
596 ptr,
597 arity,
598 name: name.into(),
599 }
600 }
601 pub fn arity(&self) -> usize {
603 self.arity
604 }
605 pub fn name(&self) -> &str {
607 &self.name
608 }
609 pub fn raw(&self) -> usize {
611 self.ptr
612 }
613}
614#[allow(dead_code)]
616pub struct FocusStack<T> {
617 items: Vec<T>,
618}
619#[allow(dead_code)]
620impl<T> FocusStack<T> {
621 pub fn new() -> Self {
623 Self { items: Vec::new() }
624 }
625 pub fn focus(&mut self, item: T) {
627 self.items.push(item);
628 }
629 pub fn blur(&mut self) -> Option<T> {
631 self.items.pop()
632 }
633 pub fn current(&self) -> Option<&T> {
635 self.items.last()
636 }
637 pub fn depth(&self) -> usize {
639 self.items.len()
640 }
641 pub fn is_empty(&self) -> bool {
643 self.items.is_empty()
644 }
645}
646#[allow(dead_code)]
648#[derive(Debug, Clone)]
649pub struct EtaNormInfo {
650 pub normalized: Expr,
652 pub contractions: usize,
654 pub already_normal: bool,
656}
657impl EtaNormInfo {
658 pub fn already_normal(expr: Expr) -> Self {
660 Self {
661 normalized: expr,
662 contractions: 0,
663 already_normal: true,
664 }
665 }
666 pub fn contracted(expr: Expr, n: usize) -> Self {
668 Self {
669 normalized: expr,
670 contractions: n,
671 already_normal: false,
672 }
673 }
674}
675#[allow(dead_code)]
677pub struct SimpleDag {
678 edges: Vec<Vec<usize>>,
680}
681#[allow(dead_code)]
682impl SimpleDag {
683 pub fn new(n: usize) -> Self {
685 Self {
686 edges: vec![Vec::new(); n],
687 }
688 }
689 pub fn add_edge(&mut self, from: usize, to: usize) {
691 if from < self.edges.len() {
692 self.edges[from].push(to);
693 }
694 }
695 pub fn successors(&self, node: usize) -> &[usize] {
697 self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
698 }
699 pub fn can_reach(&self, from: usize, to: usize) -> bool {
701 let mut visited = vec![false; self.edges.len()];
702 self.dfs(from, to, &mut visited)
703 }
704 fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
705 if cur == target {
706 return true;
707 }
708 if cur >= visited.len() || visited[cur] {
709 return false;
710 }
711 visited[cur] = true;
712 for &next in self.successors(cur) {
713 if self.dfs(next, target, visited) {
714 return true;
715 }
716 }
717 false
718 }
719 pub fn topological_sort(&self) -> Option<Vec<usize>> {
721 let n = self.edges.len();
722 let mut in_degree = vec![0usize; n];
723 for succs in &self.edges {
724 for &s in succs {
725 if s < n {
726 in_degree[s] += 1;
727 }
728 }
729 }
730 let mut queue: std::collections::VecDeque<usize> =
731 (0..n).filter(|&i| in_degree[i] == 0).collect();
732 let mut order = Vec::new();
733 while let Some(node) = queue.pop_front() {
734 order.push(node);
735 for &s in self.successors(node) {
736 if s < n {
737 in_degree[s] -= 1;
738 if in_degree[s] == 0 {
739 queue.push_back(s);
740 }
741 }
742 }
743 }
744 if order.len() == n {
745 Some(order)
746 } else {
747 None
748 }
749 }
750 pub fn num_nodes(&self) -> usize {
752 self.edges.len()
753 }
754}
755#[allow(dead_code)]
757pub struct SlidingSum {
758 window: Vec<f64>,
759 capacity: usize,
760 pos: usize,
761 sum: f64,
762 count: usize,
763}
764#[allow(dead_code)]
765impl SlidingSum {
766 pub fn new(capacity: usize) -> Self {
768 Self {
769 window: vec![0.0; capacity],
770 capacity,
771 pos: 0,
772 sum: 0.0,
773 count: 0,
774 }
775 }
776 pub fn push(&mut self, val: f64) {
778 let oldest = self.window[self.pos];
779 self.sum -= oldest;
780 self.sum += val;
781 self.window[self.pos] = val;
782 self.pos = (self.pos + 1) % self.capacity;
783 if self.count < self.capacity {
784 self.count += 1;
785 }
786 }
787 pub fn sum(&self) -> f64 {
789 self.sum
790 }
791 pub fn mean(&self) -> Option<f64> {
793 if self.count == 0 {
794 None
795 } else {
796 Some(self.sum / self.count as f64)
797 }
798 }
799 pub fn count(&self) -> usize {
801 self.count
802 }
803}
804#[allow(dead_code)]
806#[allow(missing_docs)]
807pub struct EtaReductionStats {
808 pub reductions: u64,
810 pub examined: u64,
812 pub expansions: u64,
814 pub max_depth: usize,
816}
817#[allow(dead_code)]
818impl EtaReductionStats {
819 pub fn new() -> Self {
821 Self {
822 reductions: 0,
823 examined: 0,
824 expansions: 0,
825 max_depth: 0,
826 }
827 }
828 pub fn ratio(&self) -> f64 {
830 if self.examined == 0 {
831 return 0.0;
832 }
833 self.reductions as f64 / self.examined as f64
834 }
835}
836#[allow(dead_code)]
838pub struct PathBuf {
839 components: Vec<String>,
840}
841#[allow(dead_code)]
842impl PathBuf {
843 pub fn new() -> Self {
845 Self {
846 components: Vec::new(),
847 }
848 }
849 pub fn push(&mut self, comp: impl Into<String>) {
851 self.components.push(comp.into());
852 }
853 pub fn pop(&mut self) {
855 self.components.pop();
856 }
857 pub fn as_str(&self) -> String {
859 self.components.join("/")
860 }
861 pub fn depth(&self) -> usize {
863 self.components.len()
864 }
865 pub fn clear(&mut self) {
867 self.components.clear();
868 }
869}
870#[allow(dead_code)]
872pub struct EtaJobQueue {
873 jobs: Vec<EtaJob>,
874}
875#[allow(dead_code)]
876impl EtaJobQueue {
877 pub fn new() -> Self {
879 Self { jobs: Vec::new() }
880 }
881 pub fn enqueue(&mut self, job: EtaJob) {
883 let pos = self.jobs.partition_point(|j| j.prio >= job.prio);
884 self.jobs.insert(pos, job);
885 }
886 pub fn dequeue(&mut self) -> Option<EtaJob> {
888 if self.jobs.is_empty() {
889 None
890 } else {
891 Some(self.jobs.remove(0))
892 }
893 }
894 pub fn len(&self) -> usize {
896 self.jobs.len()
897 }
898 pub fn is_empty(&self) -> bool {
900 self.jobs.is_empty()
901 }
902}
903#[allow(dead_code)]
905pub struct ConfigNode {
906 key: String,
907 value: Option<String>,
908 children: Vec<ConfigNode>,
909}
910#[allow(dead_code)]
911impl ConfigNode {
912 pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
914 Self {
915 key: key.into(),
916 value: Some(value.into()),
917 children: Vec::new(),
918 }
919 }
920 pub fn section(key: impl Into<String>) -> Self {
922 Self {
923 key: key.into(),
924 value: None,
925 children: Vec::new(),
926 }
927 }
928 pub fn add_child(&mut self, child: ConfigNode) {
930 self.children.push(child);
931 }
932 pub fn key(&self) -> &str {
934 &self.key
935 }
936 pub fn value(&self) -> Option<&str> {
938 self.value.as_deref()
939 }
940 pub fn num_children(&self) -> usize {
942 self.children.len()
943 }
944 pub fn lookup(&self, path: &str) -> Option<&str> {
946 let mut parts = path.splitn(2, '.');
947 let head = parts.next()?;
948 let tail = parts.next();
949 if head != self.key {
950 return None;
951 }
952 match tail {
953 None => self.value.as_deref(),
954 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
955 }
956 }
957 fn lookup_relative(&self, path: &str) -> Option<&str> {
958 let mut parts = path.splitn(2, '.');
959 let head = parts.next()?;
960 let tail = parts.next();
961 if head != self.key {
962 return None;
963 }
964 match tail {
965 None => self.value.as_deref(),
966 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
967 }
968 }
969}
970#[allow(dead_code)]
972pub struct EtaLog {
973 entries: Vec<(std::time::Instant, EtaOutcome)>,
974}
975#[allow(dead_code)]
976impl EtaLog {
977 pub fn new() -> Self {
979 Self {
980 entries: Vec::new(),
981 }
982 }
983 pub fn record(&mut self, outcome: EtaOutcome) {
985 self.entries.push((std::time::Instant::now(), outcome));
986 }
987 pub fn len(&self) -> usize {
989 self.entries.len()
990 }
991 pub fn is_empty(&self) -> bool {
993 self.len() == 0
994 }
995 pub fn count(&self, target: EtaOutcome) -> usize {
997 self.entries.iter().filter(|(_, o)| *o == target).count()
998 }
999 pub fn success_rate(&self) -> f64 {
1001 if self.entries.is_empty() {
1002 return 0.0;
1003 }
1004 let successes = self.entries.iter().filter(|(_, o)| o.is_success()).count();
1005 successes as f64 / self.entries.len() as f64
1006 }
1007}
1008#[allow(dead_code)]
1010pub struct SmallMap<K: Ord + Clone, V: Clone> {
1011 entries: Vec<(K, V)>,
1012}
1013#[allow(dead_code)]
1014impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
1015 pub fn new() -> Self {
1017 Self {
1018 entries: Vec::new(),
1019 }
1020 }
1021 pub fn insert(&mut self, key: K, val: V) {
1023 match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
1024 Ok(i) => self.entries[i].1 = val,
1025 Err(i) => self.entries.insert(i, (key, val)),
1026 }
1027 }
1028 pub fn get(&self, key: &K) -> Option<&V> {
1030 self.entries
1031 .binary_search_by_key(&key, |(k, _)| k)
1032 .ok()
1033 .map(|i| &self.entries[i].1)
1034 }
1035 pub fn len(&self) -> usize {
1037 self.entries.len()
1038 }
1039 pub fn is_empty(&self) -> bool {
1041 self.entries.is_empty()
1042 }
1043 pub fn keys(&self) -> Vec<&K> {
1045 self.entries.iter().map(|(k, _)| k).collect()
1046 }
1047 pub fn values(&self) -> Vec<&V> {
1049 self.entries.iter().map(|(_, v)| v).collect()
1050 }
1051}
1052#[allow(dead_code)]
1054pub struct EtaStatCounter {
1055 count: u64,
1056 sum: i64,
1057 min: i64,
1058 max: i64,
1059}
1060#[allow(dead_code)]
1061impl EtaStatCounter {
1062 pub fn new() -> Self {
1064 Self {
1065 count: 0,
1066 sum: 0,
1067 min: i64::MAX,
1068 max: i64::MIN,
1069 }
1070 }
1071 pub fn record(&mut self, v: i64) {
1073 self.count += 1;
1074 self.sum += v;
1075 if v < self.min {
1076 self.min = v;
1077 }
1078 if v > self.max {
1079 self.max = v;
1080 }
1081 }
1082 pub fn mean(&self) -> Option<f64> {
1084 if self.count == 0 {
1085 None
1086 } else {
1087 Some(self.sum as f64 / self.count as f64)
1088 }
1089 }
1090 pub fn count(&self) -> u64 {
1092 self.count
1093 }
1094}
1095#[allow(dead_code)]
1097pub struct EtaOpCounter {
1098 counts: std::collections::HashMap<String, u64>,
1099}
1100#[allow(dead_code)]
1101impl EtaOpCounter {
1102 pub fn new() -> Self {
1104 Self {
1105 counts: std::collections::HashMap::new(),
1106 }
1107 }
1108 pub fn inc(&mut self, op: &str) {
1110 *self.counts.entry(op.to_string()).or_insert(0) += 1;
1111 }
1112 pub fn get(&self, op: &str) -> u64 {
1114 self.counts.get(op).copied().unwrap_or(0)
1115 }
1116 pub fn total(&self) -> u64 {
1118 self.counts.values().sum()
1119 }
1120}