use crate::stego::cost::CostMap;
use crate::stego::permute::{CoeffPos, permute_positions};
fn collect_positions(cost_map: &CostMap) -> Vec<CoeffPos> {
let total_blocks = cost_map.total_blocks();
let bt = cost_map.blocks_tall();
let bw = cost_map.blocks_wide();
let mut positions: Vec<CoeffPos> = Vec::with_capacity(total_blocks * 63);
for br in 0..bt {
for bc in 0..bw {
for i in 0..8 {
for j in 0..8 {
if i == 0 && j == 0 {
continue; }
let cost_f32 = cost_map.get(br, bc, i, j);
if !cost_f32.is_finite() {
continue; }
let flat_idx = ((br * bw + bc) * 64 + i * 8 + j) as u32;
positions.push(CoeffPos { flat_idx, cost: cost_f32 });
}
}
}
}
positions
}
pub fn select_and_permute(cost_map: &CostMap, seed: &[u8; 32]) -> Vec<CoeffPos> {
let mut positions = collect_positions(cost_map);
permute_positions(&mut positions, seed);
positions
}
#[cfg(test)]
mod tests {
use super::*;
use crate::stego::cost::CostMap;
fn all_finite_map(bw: usize, bt: usize) -> CostMap {
let mut map = CostMap::new(bw, bt);
for br in 0..bt {
for bc in 0..bw {
for i in 0..8 {
for j in 0..8 {
if i == 0 && j == 0 {
continue;
}
map.set(br, bc, i, j, 1.0);
}
}
}
}
map
}
#[test]
fn deterministic() {
let map = all_finite_map(2, 2);
let seed = [42u8; 32];
let a = select_and_permute(&map, &seed);
let b = select_and_permute(&map, &seed);
let a_idx: Vec<_> = a.iter().map(|p| p.flat_idx).collect();
let b_idx: Vec<_> = b.iter().map(|p| p.flat_idx).collect();
assert_eq!(a_idx, b_idx);
}
#[test]
fn all_finite_entries_preserved() {
let map = all_finite_map(2, 2);
let positions = select_and_permute(&map, &[0u8; 32]);
assert_eq!(positions.len(), 252);
let mut indices: Vec<_> = positions.iter().map(|p| p.flat_idx).collect();
indices.sort();
indices.dedup();
assert_eq!(indices.len(), 252);
}
#[test]
fn wet_positions_excluded() {
let map = CostMap::new(2, 2);
let positions = select_and_permute(&map, &[0u8; 32]);
assert_eq!(positions.len(), 0, "WET positions should be excluded");
}
#[test]
fn different_seeds_differ() {
let map = all_finite_map(4, 4);
let a = select_and_permute(&map, &[1u8; 32]);
let b = select_and_permute(&map, &[2u8; 32]);
let a_idx: Vec<_> = a.iter().map(|p| p.flat_idx).collect();
let b_idx: Vec<_> = b.iter().map(|p| p.flat_idx).collect();
assert_ne!(a_idx, b_idx);
}
#[test]
fn no_dc_positions() {
let map = all_finite_map(3, 3);
let positions = select_and_permute(&map, &[99u8; 32]);
for p in &positions {
assert_ne!(
p.flat_idx % 64,
0,
"DC position included: flat_idx={}",
p.flat_idx
);
}
}
}