#![allow(dead_code)]
use super::BitArray;
#[derive(PartialEq, Eq, Debug, Hash, Copy, Clone)]
pub struct Pattern<const PATTERN_SIZE: usize>([usize; PATTERN_SIZE]);
impl<const PATTERN_SIZE: usize> Pattern<PATTERN_SIZE> {
pub fn calculate_variance(
&self,
reference: &Pattern<PATTERN_SIZE>,
max_individual_variance: f32,
) -> Option<f32> {
let total: f32 = reference.0.iter().sum::<usize>() as f32;
let pattern_length: usize = self.0.iter().sum::<usize>();
if total < pattern_length as f32 {
return None;
}
let unit_bar_width = total / pattern_length as f32;
let max_individual_variance = max_individual_variance * unit_bar_width;
let mut total_variance = 0.0;
for (&counter, scaled_pattern) in reference.0.iter().zip(
self.0
.iter()
.take(PATTERN_SIZE)
.map(|&p| (p as f32) * unit_bar_width),
) {
let variance = if (counter as f32) > scaled_pattern {
counter as f32 - scaled_pattern
} else {
scaled_pattern - counter as f32
};
if variance > max_individual_variance {
return None;
}
total_variance += variance;
}
Some(total_variance / total)
}
}
#[derive(Copy, Clone)]
pub struct PatternReader<'a, const PATTERN_SIZE: usize> {
source: &'a BitArray,
position: bool,
stored_pattern: [usize; PATTERN_SIZE],
cache_internal_position: usize,
cache_last_set_state: bool,
}
impl<'a, const PATTERN_SIZE: usize> PatternReader<'a, PATTERN_SIZE> {
pub fn new(source: &'a BitArray) -> PatternReader<'a, PATTERN_SIZE> {
let BuildInitialPatternReturn {
stored_pattern,
cache_internal_position,
cache_last_set_state,
} = build_initial_pattern(source);
PatternReader {
source,
position: false,
stored_pattern,
cache_internal_position,
cache_last_set_state,
}
}
}
struct BuildInitialPatternReturn<const PATTERN_SIZE: usize> {
stored_pattern: [usize; PATTERN_SIZE],
cache_internal_position: usize,
cache_last_set_state: bool,
}
fn build_initial_pattern<const PATTERN_SIZE: usize>(
source: &BitArray,
) -> BuildInitialPatternReturn<PATTERN_SIZE> {
let mut buffer = [0; PATTERN_SIZE];
let total_length = source.get_size();
let mut current = source.get(0);
let mut position = 0;
for pattern_position in buffer.iter_mut() {
let next = if current {
source.getNextUnset(position)
} else {
source.getNextSet(position)
};
*pattern_position = next - position;
current = !current;
position = next;
if next >= total_length {
break;
}
}
BuildInitialPatternReturn {
stored_pattern: buffer,
cache_internal_position: position,
cache_last_set_state: current,
}
}
impl<const PATTERN_SIZE: usize> PatternReader<'_, PATTERN_SIZE> {
fn read_next_pattern(&mut self) -> bool {
if self.cache_internal_position >= self.source.get_size() {
return false;
}
self.stored_pattern.rotate_left(1);
self.position = true;
let next = if self.cache_last_set_state {
self.source.getNextUnset(self.cache_internal_position)
} else {
self.source.getNextSet(self.cache_internal_position)
};
let val = next - self.cache_internal_position;
self.stored_pattern[PATTERN_SIZE - 1] = val;
self.cache_internal_position = next;
self.cache_last_set_state = !self.cache_last_set_state;
true
}
}
impl<const PATTERN_SIZE: usize> Iterator for PatternReader<'_, PATTERN_SIZE> {
type Item = Pattern<PATTERN_SIZE>;
fn next(&mut self) -> Option<Self::Item> {
let ok = if self.position {
self.read_next_pattern()
} else {
self.position = true;
true
};
if ok {
Some(Pattern(self.stored_pattern))
} else {
None
}
}
}
#[cfg(test)]
mod test {
use crate::common::BitArray;
use super::PatternReader;
#[test]
fn bluesky_case() {
let data = 0b11110000111000110010;
let mut bit_array = BitArray::with_capacity(20);
bit_array
.appendBits(data, 20)
.expect("must build bit_array");
let mut pattern_reader = PatternReader::new(&bit_array);
assert_eq!(pattern_reader.stored_pattern, [4, 4, 3, 3]);
assert!(pattern_reader.read_next_pattern());
assert_eq!(pattern_reader.stored_pattern, [4, 3, 3, 2]);
assert!(pattern_reader.read_next_pattern());
assert_eq!(pattern_reader.stored_pattern, [3, 3, 2, 2]);
assert!(pattern_reader.read_next_pattern());
assert_eq!(pattern_reader.stored_pattern, [3, 2, 2, 1]);
assert!(pattern_reader.read_next_pattern());
assert_eq!(pattern_reader.stored_pattern, [2, 2, 1, 1]);
assert!(!pattern_reader.read_next_pattern())
}
#[test]
fn iterator_case() {
let data = 0b11110000111000110010;
let mut bit_array = BitArray::with_capacity(20);
bit_array
.appendBits(data, 20)
.expect("must build bit_array");
let mut pattern_reader = PatternReader::new(&bit_array);
assert_eq!(pattern_reader.next().unwrap().0, [4, 4, 3, 3]);
assert_eq!(pattern_reader.next().unwrap().0, [4, 3, 3, 2]);
assert_eq!(pattern_reader.next().unwrap().0, [3, 3, 2, 2]);
assert_eq!(pattern_reader.next().unwrap().0, [3, 2, 2, 1]);
assert_eq!(pattern_reader.next().unwrap().0, [2, 2, 1, 1]);
assert_eq!(pattern_reader.next(), None);
}
}
#[cfg(test)]
mod more_tests {
use super::{Pattern, PatternReader};
use crate::common::BitArray;
#[test]
fn calculate_variance_uniform() {
let p = Pattern([1, 1, 1, 1]);
let r = Pattern([1, 1, 1, 1]);
assert_eq!(p.calculate_variance(&r, 1.0), Some(0.0));
}
#[test]
fn calculate_variance_some_variance() {
let p = Pattern([2, 1, 1, 0]);
let r = Pattern([1, 1, 1, 1]);
assert_eq!(p.calculate_variance(&r, 1.0), Some(0.5));
}
#[test]
fn calculate_variance_threshold_exceeded() {
let p = Pattern([2, 1, 1, 0]);
let r = Pattern([1, 1, 1, 1]);
assert_eq!(p.calculate_variance(&r, 0.2), None);
}
#[test]
fn calculate_variance_reference_smaller() {
let p = Pattern([2, 2, 2, 2]);
let r = Pattern([1, 1, 1, 1]);
assert_eq!(p.calculate_variance(&r, 1.0), None);
}
#[test]
fn pattern_reader_all_unset() {
let mut bits = BitArray::with_capacity(8);
bits.appendBits(0, 8).expect("build all-zero array");
let mut reader = PatternReader::<4>::new(&bits);
assert_eq!(reader.next().unwrap().0, [8, 0, 0, 0]);
assert!(reader.next().is_none());
}
#[test]
fn pattern_reader_alternating_bits() {
let data = 0b1010_1010u8;
let mut bits = BitArray::with_capacity(8);
bits.appendBits(data as usize, 8).unwrap();
let patterns: Vec<_> = PatternReader::<3>::new(&bits).map(|p| p.0).collect();
let expected = vec![[1, 1, 1]; 6];
assert_eq!(patterns, expected);
}
}
#[cfg(test)]
mod noisy_data_tests {
use super::{Pattern, PatternReader};
use crate::common::BitArray;
#[test]
fn calc_variance_one_pixel_noise_allowed() {
let reference = Pattern([4, 4, 4, 4]);
let noisy = Pattern([3, 5, 4, 4]); assert_eq!(noisy.calculate_variance(&reference, 1.0), Some(2.0 / 16.0));
}
#[test]
fn calc_variance_one_pixel_noise_rejected_if_threshold_too_strict() {
let reference = Pattern([4, 4, 4, 4]);
let noisy = Pattern([3, 5, 4, 4]);
assert_eq!(noisy.calculate_variance(&reference, 0.9), None);
}
#[test]
fn calc_variance_two_pixel_noise_rejected() {
let reference = Pattern([4, 4, 4, 4]);
let noisy = Pattern([2, 6, 4, 4]); assert_eq!(noisy.calculate_variance(&reference, 1.0), None);
}
#[test]
fn reader_initial_pattern_noise_allowed() {
let mut bits = BitArray::with_capacity(12);
bits.appendBits(0b111, 3).unwrap(); bits.appendBits(0b0, 1).unwrap(); bits.appendBits(0b1111, 4).unwrap(); bits.appendBits(0, 4).unwrap();
let reader = PatternReader::<4>::new(&bits);
let observed = Pattern(reader.stored_pattern);
let reference = Pattern([4, 4, 4, 4]);
assert!(
observed.calculate_variance(&reference, 3.0).is_some(),
"should accept single-pixel noise at threshold=3.0"
);
}
#[test]
fn reader_initial_pattern_noise_rejected_if_threshold_strict() {
let mut bits = BitArray::with_capacity(12);
bits.appendBits(0b111, 3).unwrap();
bits.appendBits(0b0, 1).unwrap();
bits.appendBits(0b1111, 4).unwrap();
bits.appendBits(0, 4).unwrap();
let reader = PatternReader::<4>::new(&bits);
let observed = Pattern(reader.stored_pattern);
let reference = Pattern([4, 4, 4, 4]);
assert!(
observed.calculate_variance(&reference, 1.5).is_none(),
"should reject single-pixel noise at threshold=1.5"
);
}
}