#![allow(dead_code)]
use crate::kernel::{Complex, Float};
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[derive(Debug, Clone)]
pub struct Bucket<T: Float> {
pub index: usize,
pub value: Complex<T>,
pub count: usize,
pub detected_freq: Option<usize>,
}
impl<T: Float> Bucket<T> {
pub fn new(index: usize) -> Self {
Self {
index,
value: Complex::<T>::zero(),
count: 0,
detected_freq: None,
}
}
pub fn is_zeroton(&self, threshold: T) -> bool {
self.value.norm_sqr() < threshold * threshold
}
pub fn is_singleton(&self) -> bool {
self.count == 1 && self.detected_freq.is_some()
}
pub fn is_multiton(&self) -> bool {
self.count > 1
}
pub fn add(&mut self, value: Complex<T>, freq_idx: usize) {
self.value = self.value + value;
self.count += 1;
if self.count == 1 {
self.detected_freq = Some(freq_idx);
} else {
self.detected_freq = None; }
}
pub fn reset(&mut self) {
self.value = Complex::<T>::zero();
self.count = 0;
self.detected_freq = None;
}
}
#[derive(Debug, Clone)]
pub struct BucketArray<T: Float> {
buckets: Vec<Bucket<T>>,
num_buckets: usize,
subsample_factor: usize,
n: usize,
}
impl<T: Float> BucketArray<T> {
pub fn new(num_buckets: usize, subsample_factor: usize, n: usize) -> Self {
let buckets = (0..num_buckets).map(Bucket::new).collect();
Self {
buckets,
num_buckets,
subsample_factor,
n,
}
}
#[inline]
pub fn bucket_index(&self, freq: usize) -> usize {
freq % self.num_buckets
}
pub fn fill_from_fft(&mut self, subsampled_fft: &[Complex<T>]) {
debug_assert_eq!(subsampled_fft.len(), self.num_buckets);
for (i, &val) in subsampled_fft.iter().enumerate() {
self.buckets[i].value = val;
}
}
pub fn analyze_singletons(
&mut self,
other_stage: &BucketArray<T>,
threshold: T,
) -> Vec<(usize, Complex<T>)> {
let mut singletons = Vec::new();
for (i, bucket) in self.buckets.iter_mut().enumerate() {
if bucket.is_zeroton(threshold) {
bucket.count = 0;
continue;
}
let other_bucket_idx = i % other_stage.num_buckets;
if other_bucket_idx < other_stage.buckets.len() {
let other_val = other_stage.buckets[other_bucket_idx].value;
let ratio = bucket.value.norm_sqr() / (other_val.norm_sqr() + T::from_f64(1e-10));
if ratio > T::from_f64(0.5) && ratio < T::from_f64(2.0) {
if let Some(freq) = estimate_frequency_from_phase(
bucket.value,
other_val,
self.subsample_factor,
other_stage.subsample_factor,
self.n,
) {
bucket.detected_freq = Some(freq);
bucket.count = 1;
singletons.push((freq, bucket.value));
}
}
}
}
singletons
}
pub fn non_zero_buckets(&self, threshold: T) -> Vec<&Bucket<T>> {
self.buckets
.iter()
.filter(|b| !b.is_zeroton(threshold))
.collect()
}
pub fn singleton_buckets(&self) -> Vec<&Bucket<T>> {
self.buckets.iter().filter(|b| b.is_singleton()).collect()
}
pub fn reset(&mut self) {
for bucket in &mut self.buckets {
bucket.reset();
}
}
pub fn get(&self, index: usize) -> Option<&Bucket<T>> {
self.buckets.get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut Bucket<T>> {
self.buckets.get_mut(index)
}
pub fn len(&self) -> usize {
self.num_buckets
}
pub fn is_empty(&self) -> bool {
self.num_buckets == 0
}
}
fn estimate_frequency_from_phase<T: Float>(
val1: Complex<T>,
val2: Complex<T>,
factor1: usize,
factor2: usize,
n: usize,
) -> Option<usize> {
let re1 = val1.re.to_f64().unwrap_or(0.0);
let im1 = val1.im.to_f64().unwrap_or(0.0);
let re2 = val2.re.to_f64().unwrap_or(0.0);
let im2 = val2.im.to_f64().unwrap_or(0.0);
let phase1_f64 = im1.atan2(re1);
let phase2_f64 = im2.atan2(re2);
let _phase_diff = phase2_f64 - phase1_f64;
let gcd = gcd_usize(factor1, factor2);
let lcm = (factor1 * factor2) / gcd;
let magnitude1 = re1 * re1 + im1 * im1;
let magnitude2 = re2 * re2 + im2 * im2;
if magnitude1 < 1e-10 || magnitude2 < 1e-10 {
return None;
}
let two_pi = core::f64::consts::PI * 2.0;
let phase1_abs = phase1_f64.abs();
let freq_estimate = (phase1_abs * (n as f64) / two_pi).round() as usize;
if freq_estimate < n && freq_estimate % lcm < lcm {
Some(freq_estimate % n)
} else {
Some(0)
}
}
fn gcd_usize(a: usize, b: usize) -> usize {
if b == 0 {
a
} else {
gcd_usize(b, a % b)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bucket_creation() {
let bucket: Bucket<f64> = Bucket::new(0);
assert!(bucket.is_zeroton(0.01));
assert!(!bucket.is_singleton());
assert!(!bucket.is_multiton());
}
#[test]
fn test_bucket_add() {
let mut bucket: Bucket<f64> = Bucket::new(0);
bucket.add(Complex::new(1.0, 0.0), 5);
assert!(bucket.is_singleton());
assert_eq!(bucket.detected_freq, Some(5));
bucket.add(Complex::new(0.5, 0.5), 10);
assert!(bucket.is_multiton());
assert_eq!(bucket.detected_freq, None);
}
#[test]
fn test_bucket_array() {
let array: BucketArray<f64> = BucketArray::new(16, 2, 1024);
assert_eq!(array.len(), 16);
assert!(!array.is_empty());
assert_eq!(array.bucket_index(17), 1);
assert_eq!(array.bucket_index(32), 0);
}
#[test]
fn test_gcd() {
assert_eq!(gcd_usize(12, 8), 4);
assert_eq!(gcd_usize(17, 13), 1);
assert_eq!(gcd_usize(100, 25), 25);
}
}