tycho_util/num/
safe_avg.rs1#[derive(Default)]
2pub struct SafeUnsignedAvg {
3 sum: u128,
4 counter: u128,
5}
6
7impl SafeUnsignedAvg {
8 pub fn with_initial<V: Into<u128>>(v: V) -> Self {
9 Self {
10 sum: v.into(),
11 counter: 1,
12 }
13 }
14
15 pub fn accum<V: Into<u128>>(&mut self, v: V) {
16 let v = v.into();
17 self.sum = self.sum.checked_add(v).unwrap_or_else(|| {
18 let partial = self.sum / self.counter;
19 self.counter = 0;
20 partial.saturating_add(v)
21 });
22 self.counter += 1;
23 }
24
25 pub fn get_avg(&self) -> u128 {
26 self.get_avg_checked()
27 .expect("should check first with `get_avg_checked`")
28 }
29
30 pub fn get_avg_checked(&self) -> Option<u128> {
31 if self.counter == 0 {
32 None
33 } else if self.counter == 1 {
34 Some(self.sum)
35 } else {
36 Some(self.sum.saturating_div(self.counter))
37 }
38 }
39}
40
41#[derive(Default)]
42pub struct SafeSignedAvg {
43 sum: i128,
44 counter: i128,
45}
46
47impl SafeSignedAvg {
48 pub fn with_initial<V: Into<i128>>(v: V) -> Self {
49 Self {
50 sum: v.into(),
51 counter: 1,
52 }
53 }
54
55 pub fn accum<V: Into<i128>>(&mut self, v: V) {
56 let v = v.into();
57 self.sum = self.sum.checked_add(v).unwrap_or_else(|| {
58 let partial = self.sum / self.counter;
59 self.counter = 0;
60 partial.saturating_add(v)
61 });
62 self.counter += 1;
63 }
64
65 pub fn get_avg(&self) -> i128 {
66 self.get_avg_checked()
67 .expect("should check first with `get_avg_checked`")
68 }
69
70 pub fn get_avg_checked(&self) -> Option<i128> {
71 if self.counter == 0 {
72 None
73 } else if self.counter == 1 {
74 Some(self.sum)
75 } else {
76 Some(self.sum.saturating_div(self.counter))
77 }
78 }
79}
80
81pub struct SafeUnsignedVecAvg {
82 idx: usize,
83 counters: Vec<u128>,
84 sums: Vec<u128>,
85}
86
87impl SafeUnsignedVecAvg {
88 pub fn new(capacity: usize) -> Self {
89 Self {
90 idx: 0,
91 counters: vec![0; capacity],
92 sums: vec![0; capacity],
93 }
94 }
95
96 pub fn accum<V: Into<u128>>(&mut self, idx: usize, v: V) {
97 self.idx = idx;
98 self.accum_curr(v);
99 }
100
101 pub fn accum_next<V: Into<u128>>(&mut self, v: V) {
102 self.idx += 1;
103 self.accum_curr(v);
104 }
105
106 fn accum_curr<V: Into<u128>>(&mut self, v: V) {
107 let v = v.into();
108 self.sums[self.idx] = self.sums[self.idx].checked_add(v).unwrap_or_else(|| {
109 let partial = self.sums[self.idx] / self.counters[self.idx];
110 self.counters[self.idx] = 0;
111 partial.saturating_add(v)
112 });
113 self.counters[self.idx] += 1;
114 }
115
116 pub fn get_avg(&mut self, idx: usize) -> u128 {
117 self.get_avg_checked(idx)
118 .expect("should check first with `get_avg_checked`")
119 }
120
121 pub fn get_avg_checked(&mut self, idx: usize) -> Option<u128> {
122 self.idx = idx;
123 self.get_avg_curr()
124 }
125
126 pub fn get_avg_next(&mut self) -> u128 {
127 self.get_avg_next_checked().unwrap()
128 }
129
130 pub fn get_avg_next_checked(&mut self) -> Option<u128> {
131 self.idx += 1;
132 self.get_avg_curr()
133 }
134
135 fn get_avg_curr(&self) -> Option<u128> {
136 if self.counters[self.idx] == 0 {
137 None
138 } else {
139 Some(self.sums[self.idx].saturating_div(self.counters[self.idx]))
140 }
141 }
142}