sc_neurocore_engine/neuron/
bitstream_averager.rs1#[derive(Clone, Debug)]
13pub struct BitstreamAverager {
14 buffer: Vec<u8>,
15 index: usize,
16 filled: bool,
17 running_sum: u64,
18}
19
20impl BitstreamAverager {
21 pub fn new(window: usize) -> Self {
22 assert!(window > 0, "window must be > 0");
23 Self {
24 buffer: vec![0; window],
25 index: 0,
26 filled: false,
27 running_sum: 0,
28 }
29 }
30
31 pub fn push(&mut self, bit: u8) {
32 debug_assert!(bit <= 1, "bit must be 0 or 1");
33 let old = self.buffer[self.index];
34 self.buffer[self.index] = bit;
35
36 if self.filled {
37 self.running_sum = self.running_sum - old as u64 + bit as u64;
38 } else {
39 self.running_sum += bit as u64;
40 }
41
42 self.index += 1;
43 if self.index == self.buffer.len() {
44 self.index = 0;
45 self.filled = true;
46 }
47 }
48
49 pub fn estimate(&self) -> f64 {
50 if !self.filled {
51 if self.index == 0 {
52 return 0.0;
53 }
54 return self.running_sum as f64 / self.index as f64;
55 }
56 self.running_sum as f64 / self.buffer.len() as f64
57 }
58
59 pub fn reset(&mut self) {
60 self.buffer.fill(0);
61 self.index = 0;
62 self.filled = false;
63 self.running_sum = 0;
64 }
65
66 pub fn window(&self) -> usize {
67 self.buffer.len()
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::BitstreamAverager;
74
75 #[test]
76 fn all_ones_estimate_one() {
77 let mut avg = BitstreamAverager::new(100);
78 for _ in 0..100 {
79 avg.push(1);
80 }
81 assert!((avg.estimate() - 1.0).abs() < 1e-12);
82 }
83
84 #[test]
85 fn all_zeros_estimate_zero() {
86 let mut avg = BitstreamAverager::new(50);
87 for _ in 0..50 {
88 avg.push(0);
89 }
90 assert!(avg.estimate().abs() < 1e-12);
91 }
92
93 #[test]
94 fn alternating_bits_estimate_half() {
95 let mut avg = BitstreamAverager::new(100);
96 for i in 0..100 {
97 avg.push((i % 2) as u8);
98 }
99 assert!((avg.estimate() - 0.5).abs() < 1e-12);
100 }
101
102 #[test]
103 fn sliding_window_replaces_oldest_bits() {
104 let mut avg = BitstreamAverager::new(4);
105 for &bit in &[1_u8, 1, 0, 0] {
106 avg.push(bit);
107 }
108 assert!((avg.estimate() - 0.5).abs() < 1e-12);
109
110 avg.push(1);
111 assert!((avg.estimate() - 0.5).abs() < 1e-12);
112 avg.push(1);
113 assert!((avg.estimate() - 0.5).abs() < 1e-12);
114 avg.push(1);
115 assert!((avg.estimate() - 0.75).abs() < 1e-12);
116 }
117
118 #[test]
119 fn partial_window_uses_observed_count() {
120 let mut avg = BitstreamAverager::new(100);
121 avg.push(1);
122 avg.push(0);
123 assert!((avg.estimate() - 0.5).abs() < 1e-12);
124 }
125
126 #[test]
127 fn empty_window_estimate_is_zero() {
128 let avg = BitstreamAverager::new(10);
129 assert!(avg.estimate().abs() < 1e-12);
130 }
131
132 #[test]
133 fn reset_clears_window_state() {
134 let mut avg = BitstreamAverager::new(10);
135 for _ in 0..10 {
136 avg.push(1);
137 }
138 avg.reset();
139 assert!(avg.estimate().abs() < 1e-12);
140 }
141}