use super::*;
#[test]
fn new_zero_tail_partitions_when_p_equals_n() {
let weights = vec![1.0f32; 256];
let state = LinearFftState::new(256, &weights)
.expect("construction should succeed for test-sized buffers");
assert_eq!(state.p, 256);
assert_eq!(state.n, 256);
assert_eq!(state.num_partitions, 0);
assert_eq!(state.num_bins, 257);
assert_eq!(state.h_fdl_re.len(), 0);
assert_eq!(state.fdl_re.len(), 0);
assert_eq!(state.tail_output_buf.len(), 256);
}
#[test]
fn new_one_tail_partition_when_n_equals_2p() {
let weights = vec![1.0f32; 512];
let state = LinearFftState::new(256, &weights)
.expect("construction should succeed for test-sized buffers");
assert_eq!(state.p, 256);
assert_eq!(state.n, 512);
assert_eq!(state.num_partitions, 1);
assert_eq!(state.num_bins, 257);
assert_eq!(state.h_fdl_re.len(), 257);
assert_eq!(state.fdl_re.len(), 257);
}
#[test]
fn new_partitions_for_ir_8192_p_512() {
let weights = vec![0.0f32; 8192];
let state = LinearFftState::new(512, &weights)
.expect("construction should succeed for test-sized buffers");
assert_eq!(state.num_partitions, 15);
assert_eq!(state.num_bins, 513);
assert_eq!(state.h_fdl_re.len(), 7695);
assert_eq!(state.fdl_re.len(), 7695);
assert_eq!(state.tail_output_buf.len(), 512);
assert_eq!(state.input_buf.len(), 1024);
}
#[test]
fn new_partitions_uneven_last_block() {
let weights = vec![0.0f32; 1000];
let state = LinearFftState::new(256, &weights)
.expect("construction should succeed for test-sized buffers");
assert_eq!(state.num_partitions, 3);
}
#[test]
#[should_panic(expected = "power of two")]
fn new_panics_on_non_power_of_two_p() {
let weights = vec![0.0f32; 1024];
LinearFftState::new(300, &weights).expect("construction should succeed for test-sized buffers");
}
#[test]
#[should_panic(expected = "P (1024) must be ≤ N (512)")]
fn new_panics_when_p_greater_than_n() {
let weights = vec![0.0f32; 512];
LinearFftState::new(1024, &weights)
.expect("construction should succeed for test-sized buffers");
}
#[test]
fn reset_zeros_runtime_buffers() {
let mut weights = vec![0.0f32; 256];
weights[0] = 1.0;
let mut state = LinearFftState::new(128, &weights)
.expect("construction should succeed for test-sized buffers");
assert_eq!(state.num_partitions, 1);
state.tail_output_buf[0] = 99.0;
state.fdl_re[0] = 99.0;
state.fdl_im[0] = 99.0;
state.sample_counter = 42;
state.fdl_write_idx = 0;
state.reset();
assert_eq!(state.sample_counter, 0);
assert_eq!(state.fdl_write_idx, state.num_partitions.saturating_sub(1));
for v in state.tail_output_buf.iter() {
assert!((*v).abs() < f32::EPSILON);
}
for v in state.fdl_re.iter() {
assert!((*v).abs() < f32::EPSILON);
}
assert!(!state.h_fdl_re.is_empty());
}
#[test]
fn debug_format_does_not_leak_internal_buffers() {
let weights = vec![1.0f32; 512];
let state = LinearFftState::new(256, &weights)
.expect("construction should succeed for test-sized buffers");
let dbg = format!("{state:?}");
assert!(dbg.contains("LinearFftState"));
assert!(dbg.contains("p"));
assert!(dbg.contains("n"));
assert!(dbg.contains("..")); }
#[test]
fn h_spectra_nonzero_for_nonzero_ir_tail() {
let mut weights = vec![0.0f32; 512];
weights[256] = 1.0;
let state = LinearFftState::new(256, &weights)
.expect("construction should succeed for test-sized buffers");
assert_eq!(state.num_partitions, 1);
assert!((state.h_fdl_re[0] - 1.0).abs() < 1e-5);
}
#[test]
fn weights_head_portion_not_used_for_spectra() {
let weights: Vec<f32> = (1..=8).map(|v| v as f32).collect();
let state = LinearFftState::new(4, &weights)
.expect("construction should succeed for test-sized buffers");
assert_eq!(state.num_partitions, 1);
assert!((state.h_fdl_re[0] - 26.0).abs() < 1e-4);
}
#[test]
fn process_tail_block_noop_when_zero_partitions() {
let weights = vec![1.0f32; 256];
let mut state = LinearFftState::new(256, &weights)
.expect("construction should succeed for test-sized buffers");
assert_eq!(state.num_partitions, 0);
state.process_tail_block(&[0.0f32; 512]);
for &v in state.tail_output_buf.iter() {
assert!((v - 0.0).abs() < f32::EPSILON);
}
}
#[test]
fn process_tail_block_first_call_uses_current_spectrum() {
let weights = vec![1.0, 2.0, 3.0, 4.0];
let mut state = LinearFftState::new(2, &weights)
.expect("construction should succeed for test-sized buffers");
assert_eq!(state.num_partitions, 1);
state.process_tail_block(&[1.0, 2.0, 3.0, 4.0]);
assert!(
(state.tail_output_buf[0] - 17.0).abs() < 1e-4,
"expected 17, got {}",
state.tail_output_buf[0]
);
assert!(
(state.tail_output_buf[1] - 24.0).abs() < 1e-4,
"expected 24, got {}",
state.tail_output_buf[1]
);
}
#[test]
fn process_tail_block_numerical_correctness() {
let ir = vec![0.5, 1.5, 3.0, 4.0];
let p = 2;
let mut state =
LinearFftState::new(p, &ir).expect("construction should succeed for test-sized buffers");
let block1 = [1.0, 2.0, 3.0, 4.0];
state.process_tail_block(&block1);
assert!(
(state.tail_output_buf[0] - 17.0).abs() < 1e-4,
"block 1[0]: expected 17, got {}",
state.tail_output_buf[0]
);
assert!(
(state.tail_output_buf[1] - 24.0).abs() < 1e-4,
"block 1[1]: expected 24, got {}",
state.tail_output_buf[1]
);
let block2 = [5.0, 6.0, 7.0, 8.0];
state.process_tail_block(&block2);
assert!(
(state.tail_output_buf[0] - 45.0).abs() < 1e-4,
"block 2[0]: expected 45, got {}",
state.tail_output_buf[0]
);
assert!(
(state.tail_output_buf[1] - 52.0).abs() < 1e-4,
"block 2[1]: expected 52, got {}",
state.tail_output_buf[1]
);
}
#[test]
fn process_tail_block_multiple_partitions() {
let ir: Vec<f32> = vec![
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.5, 0.5, ];
let p = 2;
let mut state =
LinearFftState::new(p, &ir).expect("construction should succeed for test-sized buffers");
assert_eq!(state.num_partitions, 3);
state.process_tail_block(&[0.0, 0.0, 1.0, 0.0]);
assert!(
(state.tail_output_buf[0] - 1.0).abs() < 1e-4,
"block 1[0]: expected 1, got {}",
state.tail_output_buf[0]
);
assert!(
(state.tail_output_buf[1] - 0.0).abs() < 1e-4,
"block 1[1]: expected 0, got {}",
state.tail_output_buf[1]
);
state.process_tail_block(&[0.0, 1.0, 0.0, 0.0]);
assert!(
(state.tail_output_buf[0] - 0.0).abs() < 1e-4,
"block 2[0]: expected 0, got {}",
state.tail_output_buf[0]
);
assert!(
(state.tail_output_buf[1] - 1.0).abs() < 1e-4,
"block 2[1]: expected 1, got {}",
state.tail_output_buf[1]
);
state.process_tail_block(&[1.0, 0.0, 0.0, 0.0]);
assert!(
(state.tail_output_buf[0] - 1.5).abs() < 1e-4,
"block 3[0]: expected 1.5, got {}",
state.tail_output_buf[0]
);
assert!(
(state.tail_output_buf[1] - 0.5).abs() < 1e-4,
"block 3[1]: expected 0.5, got {}",
state.tail_output_buf[1]
);
}
#[test]
fn process_tail_block_fdl_circular_advance() {
let ir = vec![0.0f32; 8];
let p = 2;
let mut state =
LinearFftState::new(p, &ir).expect("construction should succeed for test-sized buffers");
assert_eq!(state.num_partitions, 3);
assert_eq!(state.fdl_write_idx, 2);
state.process_tail_block(&[1.0; 4]);
assert_eq!(state.fdl_write_idx, 0);
state.process_tail_block(&[2.0; 4]);
assert_eq!(state.fdl_write_idx, 1);
state.process_tail_block(&[3.0; 4]);
assert_eq!(state.fdl_write_idx, 2);
state.process_tail_block(&[4.0; 4]);
assert_eq!(state.fdl_write_idx, 0);
}
#[test]
fn process_tail_block_reset_then_process() {
let ir = vec![1.0, 2.0, 3.0, 4.0];
let mut state =
LinearFftState::new(2, &ir).expect("construction should succeed for test-sized buffers");
state.process_tail_block(&[1.0; 4]);
assert_eq!(state.fdl_write_idx, 0);
state.reset();
assert_eq!(state.fdl_write_idx, 0); assert_eq!(state.sample_counter, 0);
state.process_tail_block(&[5.0, 6.0, 7.0, 8.0]);
assert!(
(state.tail_output_buf[0] - 45.0).abs() < 1e-4,
"expected 45, got {}",
state.tail_output_buf[0]
);
assert!(
(state.tail_output_buf[1] - 52.0).abs() < 1e-4,
"expected 52, got {}",
state.tail_output_buf[1]
);
}