use serde::{Deserialize, Serialize};
const BITMAP_WORDS: usize = 16;
#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(C, align(64))] pub struct DetectorBitmap {
bits: [u64; BITMAP_WORDS],
count: usize,
}
impl Default for DetectorBitmap {
fn default() -> Self {
Self::new(0)
}
}
impl std::fmt::Debug for DetectorBitmap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DetectorBitmap")
.field("count", &self.count)
.field("fired", &self.fired_count())
.finish()
}
}
impl DetectorBitmap {
#[inline]
#[must_use]
pub fn new(count: usize) -> Self {
assert!(count <= BITMAP_WORDS * 64, "count exceeds maximum of 1024");
Self {
bits: [0u64; BITMAP_WORDS],
count,
}
}
#[inline]
#[must_use]
pub const fn from_raw(bits: [u64; BITMAP_WORDS], count: usize) -> Self {
assert!(count <= BITMAP_WORDS * 64, "count exceeds maximum of 1024");
Self { bits, count }
}
#[inline]
#[must_use]
pub const fn raw_bits(&self) -> &[u64; BITMAP_WORDS] {
&self.bits
}
#[inline]
#[must_use]
pub const fn detector_count(&self) -> usize {
self.count
}
#[inline]
pub fn set(&mut self, idx: usize, value: bool) {
assert!(idx < self.count, "detector index {} out of bounds (count: {})", idx, self.count);
let word = idx / 64;
let bit = idx % 64;
if value {
self.bits[word] |= 1u64 << bit;
} else {
self.bits[word] &= !(1u64 << bit);
}
}
#[inline]
#[must_use]
pub fn get(&self, idx: usize) -> bool {
assert!(idx < self.count, "detector index {} out of bounds (count: {})", idx, self.count);
let word = idx / 64;
let bit = idx % 64;
(self.bits[word] >> bit) & 1 == 1
}
#[inline]
#[must_use]
pub fn fired_count(&self) -> usize {
self.popcount()
}
#[inline]
#[must_use]
pub fn popcount(&self) -> usize {
let full_words = self.count / 64;
let remaining_bits = self.count % 64;
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
if is_x86_feature_detected!("avx2") && full_words >= 4 {
unsafe {
return self.popcount_avx2(full_words, remaining_bits);
}
}
}
let mut total = 0usize;
for word in &self.bits[..full_words] {
total += word.count_ones() as usize;
}
if remaining_bits > 0 && full_words < BITMAP_WORDS {
let mask = (1u64 << remaining_bits) - 1;
total += (self.bits[full_words] & mask).count_ones() as usize;
}
total
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
#[inline]
#[target_feature(enable = "avx2")]
unsafe fn popcount_avx2(&self, full_words: usize, remaining_bits: usize) -> usize {
use std::arch::x86_64::*;
let lookup = _mm256_setr_epi8(
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
);
let low_mask = _mm256_set1_epi8(0x0f);
let mut total_vec = _mm256_setzero_si256();
let mut i = 0;
while i + 4 <= full_words {
let data = _mm256_loadu_si256(self.bits.as_ptr().add(i) as *const __m256i);
let lo = _mm256_and_si256(data, low_mask);
let hi = _mm256_and_si256(_mm256_srli_epi16(data, 4), low_mask);
let popcnt_lo = _mm256_shuffle_epi8(lookup, lo);
let popcnt_hi = _mm256_shuffle_epi8(lookup, hi);
let popcnt = _mm256_add_epi8(popcnt_lo, popcnt_hi);
total_vec = _mm256_add_epi64(total_vec, _mm256_sad_epu8(popcnt, _mm256_setzero_si256()));
i += 4;
}
let mut total = 0usize;
let mut buf = [0u64; 4];
_mm256_storeu_si256(buf.as_mut_ptr() as *mut __m256i, total_vec);
total += buf[0] as usize + buf[1] as usize + buf[2] as usize + buf[3] as usize;
while i < full_words {
total += self.bits[i].count_ones() as usize;
i += 1;
}
if remaining_bits > 0 && full_words < BITMAP_WORDS {
let mask = (1u64 << remaining_bits) - 1;
total += (self.bits[full_words] & mask).count_ones() as usize;
}
total
}
#[inline]
pub fn iter_fired(&self) -> FiredIterator<'_> {
FiredIterator {
bitmap: self,
word_idx: 0,
current_word: self.bits[0],
base_idx: 0,
}
}
#[inline]
#[must_use]
pub fn xor(&self, other: &DetectorBitmap) -> DetectorBitmap {
let mut result = DetectorBitmap::new(self.count.max(other.count));
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
if is_x86_feature_detected!("avx2") {
unsafe {
use std::arch::x86_64::*;
let a0 = _mm256_loadu_si256(self.bits.as_ptr() as *const __m256i);
let b0 = _mm256_loadu_si256(other.bits.as_ptr() as *const __m256i);
let r0 = _mm256_xor_si256(a0, b0);
_mm256_storeu_si256(result.bits.as_mut_ptr() as *mut __m256i, r0);
let a1 = _mm256_loadu_si256(self.bits.as_ptr().add(4) as *const __m256i);
let b1 = _mm256_loadu_si256(other.bits.as_ptr().add(4) as *const __m256i);
let r1 = _mm256_xor_si256(a1, b1);
_mm256_storeu_si256(result.bits.as_mut_ptr().add(4) as *mut __m256i, r1);
let a2 = _mm256_loadu_si256(self.bits.as_ptr().add(8) as *const __m256i);
let b2 = _mm256_loadu_si256(other.bits.as_ptr().add(8) as *const __m256i);
let r2 = _mm256_xor_si256(a2, b2);
_mm256_storeu_si256(result.bits.as_mut_ptr().add(8) as *mut __m256i, r2);
let a3 = _mm256_loadu_si256(self.bits.as_ptr().add(12) as *const __m256i);
let b3 = _mm256_loadu_si256(other.bits.as_ptr().add(12) as *const __m256i);
let r3 = _mm256_xor_si256(a3, b3);
_mm256_storeu_si256(result.bits.as_mut_ptr().add(12) as *mut __m256i, r3);
return result;
}
}
}
for i in 0..BITMAP_WORDS {
result.bits[i] = self.bits[i] ^ other.bits[i];
}
result
}
#[inline]
#[must_use]
pub fn and(&self, other: &DetectorBitmap) -> DetectorBitmap {
let mut result = DetectorBitmap::new(self.count.min(other.count));
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
if is_x86_feature_detected!("avx2") {
unsafe {
use std::arch::x86_64::*;
for i in (0..BITMAP_WORDS).step_by(4) {
let a = _mm256_loadu_si256(self.bits.as_ptr().add(i) as *const __m256i);
let b = _mm256_loadu_si256(other.bits.as_ptr().add(i) as *const __m256i);
let r = _mm256_and_si256(a, b);
_mm256_storeu_si256(result.bits.as_mut_ptr().add(i) as *mut __m256i, r);
}
return result;
}
}
}
for i in 0..BITMAP_WORDS {
result.bits[i] = self.bits[i] & other.bits[i];
}
result
}
#[inline]
#[must_use]
pub fn or(&self, other: &DetectorBitmap) -> DetectorBitmap {
let mut result = DetectorBitmap::new(self.count.max(other.count));
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
if is_x86_feature_detected!("avx2") {
unsafe {
use std::arch::x86_64::*;
for i in (0..BITMAP_WORDS).step_by(4) {
let a = _mm256_loadu_si256(self.bits.as_ptr().add(i) as *const __m256i);
let b = _mm256_loadu_si256(other.bits.as_ptr().add(i) as *const __m256i);
let r = _mm256_or_si256(a, b);
_mm256_storeu_si256(result.bits.as_mut_ptr().add(i) as *mut __m256i, r);
}
return result;
}
}
}
for i in 0..BITMAP_WORDS {
result.bits[i] = self.bits[i] | other.bits[i];
}
result
}
#[inline]
#[must_use]
pub fn not(&self) -> DetectorBitmap {
let mut result = DetectorBitmap::new(self.count);
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
if is_x86_feature_detected!("avx2") {
unsafe {
use std::arch::x86_64::*;
let ones = _mm256_set1_epi64x(-1i64);
for i in (0..BITMAP_WORDS).step_by(4) {
let a = _mm256_loadu_si256(self.bits.as_ptr().add(i) as *const __m256i);
let r = _mm256_xor_si256(a, ones);
_mm256_storeu_si256(result.bits.as_mut_ptr().add(i) as *mut __m256i, r);
}
let full_words = self.count / 64;
let remaining_bits = self.count % 64;
if remaining_bits > 0 && full_words < BITMAP_WORDS {
let mask = (1u64 << remaining_bits) - 1;
result.bits[full_words] &= mask;
}
for i in (full_words + 1)..BITMAP_WORDS {
result.bits[i] = 0;
}
return result;
}
}
}
let full_words = self.count / 64;
let remaining_bits = self.count % 64;
for i in 0..full_words {
result.bits[i] = !self.bits[i];
}
if remaining_bits > 0 && full_words < BITMAP_WORDS {
let mask = (1u64 << remaining_bits) - 1;
result.bits[full_words] = (!self.bits[full_words]) & mask;
}
result
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.bits.iter().all(|&w| w == 0)
}
#[inline]
pub fn clear(&mut self) {
self.bits = [0u64; BITMAP_WORDS];
}
}
pub struct FiredIterator<'a> {
bitmap: &'a DetectorBitmap,
word_idx: usize,
current_word: u64,
base_idx: usize,
}
impl<'a> Iterator for FiredIterator<'a> {
type Item = usize;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.current_word != 0 {
let trailing = self.current_word.trailing_zeros() as usize;
let idx = self.base_idx + trailing;
if idx >= self.bitmap.count {
return None;
}
self.current_word &= self.current_word - 1;
return Some(idx);
}
self.word_idx += 1;
if self.word_idx >= BITMAP_WORDS {
return None;
}
self.base_idx = self.word_idx * 64;
if self.base_idx >= self.bitmap.count {
return None;
}
self.current_word = self.bitmap.bits[self.word_idx];
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining_popcount = self.bitmap.popcount();
(0, Some(remaining_popcount))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SyndromeRound {
pub round_id: u64,
pub cycle: u64,
pub timestamp: u64,
pub detectors: DetectorBitmap,
pub source_tile: u8,
}
impl SyndromeRound {
#[inline]
#[must_use]
pub fn new(
round_id: u64,
cycle: u64,
timestamp: u64,
detectors: DetectorBitmap,
source_tile: u8,
) -> Self {
Self {
round_id,
cycle,
timestamp,
detectors,
source_tile,
}
}
#[inline]
#[must_use]
pub fn fired_count(&self) -> usize {
self.detectors.fired_count()
}
#[inline]
pub fn iter_fired(&self) -> FiredIterator<'_> {
self.detectors.iter_fired()
}
#[inline]
#[must_use]
pub fn delta_to(&self, other: &SyndromeRound) -> SyndromeDelta {
SyndromeDelta::compute(self, other)
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct BufferStatistics {
pub total_rounds: u64,
pub current_size: usize,
pub capacity: usize,
pub evicted_rounds: u64,
pub avg_firing_rate: f64,
pub max_firing_count: usize,
pub oldest_round_id: Option<u64>,
pub newest_round_id: Option<u64>,
}
#[derive(Clone, Debug)]
pub struct SyndromeBuffer {
capacity: usize,
rounds: Vec<Option<SyndromeRound>>,
write_index: usize,
valid_count: usize,
watermark: u64,
total_pushed: u64,
firing_sum: u64,
max_firing: usize,
}
impl SyndromeBuffer {
#[must_use]
pub fn new(capacity: usize) -> Self {
assert!(capacity > 0, "buffer capacity must be positive");
Self {
capacity,
rounds: vec![None; capacity],
write_index: 0,
valid_count: 0,
watermark: 0,
total_pushed: 0,
firing_sum: 0,
max_firing: 0,
}
}
#[inline]
#[must_use]
pub const fn capacity(&self) -> usize {
self.capacity
}
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.valid_count
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.valid_count == 0
}
#[inline]
#[must_use]
pub const fn is_full(&self) -> bool {
self.valid_count >= self.capacity
}
#[inline]
#[must_use]
pub const fn watermark(&self) -> u64 {
self.watermark
}
#[inline]
pub fn push(&mut self, round: SyndromeRound) {
let fired = round.fired_count();
self.firing_sum += fired as u64;
self.max_firing = self.max_firing.max(fired);
self.total_pushed += 1;
if self.valid_count >= self.capacity {
if let Some(ref old) = self.rounds[self.write_index] {
self.watermark = old.round_id + 1;
}
}
self.rounds[self.write_index] = Some(round);
self.write_index = (self.write_index + 1) % self.capacity;
if self.valid_count < self.capacity {
self.valid_count += 1;
}
}
#[must_use]
pub fn window(&self, size: usize) -> Vec<&SyndromeRound> {
let actual_size = size.min(self.valid_count);
if actual_size == 0 {
return Vec::new();
}
let mut result = Vec::with_capacity(actual_size);
let start = if self.write_index >= actual_size {
self.write_index - actual_size
} else {
self.capacity - (actual_size - self.write_index)
};
for i in 0..actual_size {
let idx = (start + i) % self.capacity;
if let Some(ref round) = self.rounds[idx] {
result.push(round);
}
}
result
}
#[must_use]
pub fn get(&self, round_id: u64) -> Option<&SyndromeRound> {
if self.valid_count == 0 || round_id < self.watermark {
return None;
}
if let Some(ref newest) = self.rounds[(self.write_index + self.capacity - 1) % self.capacity]
{
if round_id <= newest.round_id {
let offset = (newest.round_id - round_id) as usize;
if offset < self.valid_count {
let idx = if self.write_index > offset {
self.write_index - 1 - offset
} else {
self.capacity - 1 - (offset - self.write_index)
};
if let Some(ref round) = self.rounds[idx] {
if round.round_id == round_id {
return Some(round);
}
}
}
}
}
for i in 0..self.valid_count {
let idx = if self.write_index > i {
self.write_index - 1 - i
} else {
self.capacity - 1 - (i - self.write_index)
};
if let Some(ref round) = self.rounds[idx] {
if round.round_id == round_id {
return Some(round);
}
}
}
None
}
#[must_use]
pub fn statistics(&self) -> BufferStatistics {
let (oldest_id, newest_id) = if self.valid_count > 0 {
let oldest_idx = if self.valid_count < self.capacity {
0
} else {
self.write_index
};
let newest_idx = (self.write_index + self.capacity - 1) % self.capacity;
let oldest = self.rounds[oldest_idx].as_ref().map(|r| r.round_id);
let newest = self.rounds[newest_idx].as_ref().map(|r| r.round_id);
(oldest, newest)
} else {
(None, None)
};
let avg_firing = if self.total_pushed > 0 {
self.firing_sum as f64 / self.total_pushed as f64
} else {
0.0
};
let evicted = if self.total_pushed > self.capacity as u64 {
self.total_pushed - self.capacity as u64
} else {
0
};
BufferStatistics {
total_rounds: self.total_pushed,
current_size: self.valid_count,
capacity: self.capacity,
evicted_rounds: evicted,
avg_firing_rate: avg_firing,
max_firing_count: self.max_firing,
oldest_round_id: oldest_id,
newest_round_id: newest_id,
}
}
pub fn clear(&mut self) {
for round in &mut self.rounds {
*round = None;
}
self.write_index = 0;
self.valid_count = 0;
self.total_pushed = 0;
self.firing_sum = 0;
self.max_firing = 0;
self.watermark = 0;
}
pub fn iter(&self) -> impl Iterator<Item = &SyndromeRound> {
let start = if self.valid_count < self.capacity {
0
} else {
self.write_index
};
(0..self.valid_count)
.map(move |i| (start + i) % self.capacity)
.filter_map(move |idx| self.rounds[idx].as_ref())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SyndromeDelta {
pub from_round: u64,
pub to_round: u64,
pub flipped: DetectorBitmap,
}
impl SyndromeDelta {
#[inline]
#[must_use]
pub fn compute(from: &SyndromeRound, to: &SyndromeRound) -> Self {
Self {
from_round: from.round_id,
to_round: to.round_id,
flipped: from.detectors.xor(&to.detectors),
}
}
#[inline]
#[must_use]
pub const fn new(from_round: u64, to_round: u64, flipped: DetectorBitmap) -> Self {
Self {
from_round,
to_round,
flipped,
}
}
#[inline]
#[must_use]
pub fn is_quiet(&self) -> bool {
self.flipped.is_empty()
}
#[inline]
#[must_use]
pub fn flip_count(&self) -> usize {
self.flipped.popcount()
}
#[inline]
#[must_use]
pub fn activity_level(&self) -> f64 {
let count = self.flipped.detector_count();
if count == 0 {
return 0.0;
}
self.flipped.popcount() as f64 / count as f64
}
#[inline]
pub fn iter_flipped(&self) -> FiredIterator<'_> {
self.flipped.iter_fired()
}
#[inline]
#[must_use]
pub const fn span(&self) -> u64 {
self.to_round.saturating_sub(self.from_round)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bitmap_new() {
let bitmap = DetectorBitmap::new(64);
assert_eq!(bitmap.detector_count(), 64);
assert_eq!(bitmap.fired_count(), 0);
assert!(bitmap.is_empty());
}
#[test]
fn test_bitmap_set_get() {
let mut bitmap = DetectorBitmap::new(128);
bitmap.set(0, true);
bitmap.set(63, true);
bitmap.set(64, true);
bitmap.set(127, true);
assert!(bitmap.get(0));
assert!(bitmap.get(63));
assert!(bitmap.get(64));
assert!(bitmap.get(127));
assert!(!bitmap.get(1));
assert!(!bitmap.get(100));
}
#[test]
fn test_bitmap_fired_count() {
let mut bitmap = DetectorBitmap::new(256);
bitmap.set(0, true);
bitmap.set(10, true);
bitmap.set(100, true);
bitmap.set(200, true);
assert_eq!(bitmap.fired_count(), 4);
assert!(!bitmap.is_empty());
}
#[test]
fn test_bitmap_iter_fired() {
let mut bitmap = DetectorBitmap::new(128);
bitmap.set(5, true);
bitmap.set(64, true);
bitmap.set(100, true);
let fired: Vec<usize> = bitmap.iter_fired().collect();
assert_eq!(fired, vec![5, 64, 100]);
}
#[test]
fn test_bitmap_xor() {
let mut a = DetectorBitmap::new(64);
a.set(0, true);
a.set(5, true);
a.set(10, true);
let mut b = DetectorBitmap::new(64);
b.set(5, true);
b.set(10, true);
b.set(20, true);
let result = a.xor(&b);
assert!(result.get(0));
assert!(!result.get(5));
assert!(!result.get(10));
assert!(result.get(20));
assert_eq!(result.fired_count(), 2);
}
#[test]
fn test_bitmap_and_or() {
let mut a = DetectorBitmap::new(64);
a.set(0, true);
a.set(5, true);
let mut b = DetectorBitmap::new(64);
b.set(5, true);
b.set(10, true);
let and_result = a.and(&b);
assert!(!and_result.get(0));
assert!(and_result.get(5));
assert!(!and_result.get(10));
assert_eq!(and_result.fired_count(), 1);
let or_result = a.or(&b);
assert!(or_result.get(0));
assert!(or_result.get(5));
assert!(or_result.get(10));
assert_eq!(or_result.fired_count(), 3);
}
#[test]
fn test_bitmap_clear() {
let mut bitmap = DetectorBitmap::new(64);
bitmap.set(0, true);
bitmap.set(10, true);
assert_eq!(bitmap.fired_count(), 2);
bitmap.clear();
assert_eq!(bitmap.fired_count(), 0);
assert!(bitmap.is_empty());
}
#[test]
fn test_bitmap_large() {
let mut bitmap = DetectorBitmap::new(1024);
for i in (0..1024).step_by(100) {
bitmap.set(i, true);
}
let fired: Vec<usize> = bitmap.iter_fired().collect();
assert_eq!(fired.len(), 11); }
#[test]
#[should_panic(expected = "count exceeds maximum")]
fn test_bitmap_overflow() {
DetectorBitmap::new(2000);
}
#[test]
fn test_round_new() {
let detectors = DetectorBitmap::new(64);
let round = SyndromeRound::new(1, 100, 1000000, detectors, 5);
assert_eq!(round.round_id, 1);
assert_eq!(round.cycle, 100);
assert_eq!(round.timestamp, 1000000);
assert_eq!(round.source_tile, 5);
assert_eq!(round.fired_count(), 0);
}
#[test]
fn test_round_delta_to() {
let mut d1 = DetectorBitmap::new(64);
d1.set(0, true);
d1.set(5, true);
let mut d2 = DetectorBitmap::new(64);
d2.set(5, true);
d2.set(10, true);
let round1 = SyndromeRound::new(1, 100, 1000, d1, 0);
let round2 = SyndromeRound::new(2, 101, 2000, d2, 0);
let delta = round1.delta_to(&round2);
assert_eq!(delta.from_round, 1);
assert_eq!(delta.to_round, 2);
assert_eq!(delta.flip_count(), 2); }
#[test]
fn test_buffer_new() {
let buffer = SyndromeBuffer::new(100);
assert_eq!(buffer.capacity(), 100);
assert_eq!(buffer.len(), 0);
assert!(buffer.is_empty());
assert!(!buffer.is_full());
}
#[test]
fn test_buffer_push() {
let mut buffer = SyndromeBuffer::new(10);
for i in 0..5 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
assert_eq!(buffer.len(), 5);
assert!(!buffer.is_full());
}
#[test]
fn test_buffer_overflow() {
let mut buffer = SyndromeBuffer::new(5);
for i in 0..10 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
assert_eq!(buffer.len(), 5);
assert!(buffer.is_full());
assert!(buffer.get(4).is_none());
assert!(buffer.get(5).is_some());
assert!(buffer.get(9).is_some());
}
#[test]
fn test_buffer_window() {
let mut buffer = SyndromeBuffer::new(100);
for i in 0..50 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
let window = buffer.window(10);
assert_eq!(window.len(), 10);
assert_eq!(window[0].round_id, 40);
assert_eq!(window[9].round_id, 49);
}
#[test]
fn test_buffer_window_larger_than_buffer() {
let mut buffer = SyndromeBuffer::new(100);
for i in 0..5 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
let window = buffer.window(100);
assert_eq!(window.len(), 5);
}
#[test]
fn test_buffer_get() {
let mut buffer = SyndromeBuffer::new(100);
for i in 0..50 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
assert!(buffer.get(0).is_some());
assert!(buffer.get(49).is_some());
assert!(buffer.get(50).is_none());
assert!(buffer.get(1000).is_none());
}
#[test]
fn test_buffer_statistics() {
let mut buffer = SyndromeBuffer::new(10);
for i in 0..20u64 {
let mut detectors = DetectorBitmap::new(64);
for j in 0..(i % 5) as usize {
detectors.set(j, true);
}
let round = SyndromeRound::new(i, i, i * 1000, detectors, 0);
buffer.push(round);
}
let stats = buffer.statistics();
assert_eq!(stats.total_rounds, 20);
assert_eq!(stats.current_size, 10);
assert_eq!(stats.capacity, 10);
assert_eq!(stats.evicted_rounds, 10);
assert!(stats.avg_firing_rate > 0.0);
}
#[test]
fn test_buffer_clear() {
let mut buffer = SyndromeBuffer::new(10);
for i in 0..5 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
buffer.clear();
assert_eq!(buffer.len(), 0);
assert!(buffer.is_empty());
}
#[test]
fn test_buffer_iter() {
let mut buffer = SyndromeBuffer::new(100);
for i in 0..10 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
let ids: Vec<u64> = buffer.iter().map(|r| r.round_id).collect();
assert_eq!(ids, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
}
#[test]
#[should_panic(expected = "capacity must be positive")]
fn test_buffer_zero_capacity() {
SyndromeBuffer::new(0);
}
#[test]
fn test_delta_compute() {
let mut d1 = DetectorBitmap::new(64);
d1.set(0, true);
d1.set(5, true);
let mut d2 = DetectorBitmap::new(64);
d2.set(5, true);
d2.set(10, true);
let round1 = SyndromeRound::new(1, 100, 1000, d1, 0);
let round2 = SyndromeRound::new(2, 101, 2000, d2, 0);
let delta = SyndromeDelta::compute(&round1, &round2);
assert_eq!(delta.from_round, 1);
assert_eq!(delta.to_round, 2);
assert_eq!(delta.flip_count(), 2);
assert!(!delta.is_quiet());
}
#[test]
fn test_delta_quiet() {
let mut d1 = DetectorBitmap::new(64);
d1.set(5, true);
let mut d2 = DetectorBitmap::new(64);
d2.set(5, true);
let round1 = SyndromeRound::new(1, 100, 1000, d1, 0);
let round2 = SyndromeRound::new(2, 101, 2000, d2, 0);
let delta = SyndromeDelta::compute(&round1, &round2);
assert!(delta.is_quiet());
assert_eq!(delta.flip_count(), 0);
assert_eq!(delta.activity_level(), 0.0);
}
#[test]
fn test_delta_activity_level() {
let mut d1 = DetectorBitmap::new(100);
let mut d2 = DetectorBitmap::new(100);
for i in 0..10 {
d2.set(i, true);
}
let round1 = SyndromeRound::new(1, 100, 1000, d1, 0);
let round2 = SyndromeRound::new(2, 101, 2000, d2, 0);
let delta = SyndromeDelta::compute(&round1, &round2);
assert_eq!(delta.flip_count(), 10);
assert!((delta.activity_level() - 0.1).abs() < 0.001);
}
#[test]
fn test_delta_span() {
let d1 = DetectorBitmap::new(64);
let d2 = DetectorBitmap::new(64);
let round1 = SyndromeRound::new(100, 100, 1000, d1, 0);
let round2 = SyndromeRound::new(110, 110, 2000, d2, 0);
let delta = SyndromeDelta::compute(&round1, &round2);
assert_eq!(delta.span(), 10);
}
#[test]
fn test_delta_iter_flipped() {
let mut d1 = DetectorBitmap::new(64);
d1.set(0, true);
let mut d2 = DetectorBitmap::new(64);
d2.set(10, true);
d2.set(20, true);
let round1 = SyndromeRound::new(1, 100, 1000, d1, 0);
let round2 = SyndromeRound::new(2, 101, 2000, d2, 0);
let delta = SyndromeDelta::compute(&round1, &round2);
let flipped: Vec<usize> = delta.iter_flipped().collect();
assert_eq!(flipped, vec![0, 10, 20]);
}
}