use rand::Rng;
use rand_chacha::ChaCha20Rng;
use rand::SeedableRng;
pub const SPREAD_LEN: usize = 8;
pub fn generate_spreading_vectors(seed: &[u8; 32], count: usize) -> Vec<[f64; SPREAD_LEN]> {
let mut rng = ChaCha20Rng::from_seed(*seed);
let mut vectors = Vec::with_capacity(count);
for _ in 0..count {
let mut v = [0.0f64; SPREAD_LEN];
for val in v.iter_mut() {
*val = rng.gen_range(-1.0..1.0);
}
let norm: f64 = v.iter().map(|x| x * x).sum::<f64>().sqrt();
if norm > 1e-10 {
for val in v.iter_mut() {
*val /= norm;
}
} else {
v[0] = 1.0;
}
vectors.push(v);
}
vectors
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vectors_are_unit_norm() {
let vectors = generate_spreading_vectors(&[42u8; 32], 100);
for (i, v) in vectors.iter().enumerate() {
let norm: f64 = v.iter().map(|x| x * x).sum::<f64>().sqrt();
assert!(
(norm - 1.0).abs() < 1e-10,
"vector {i} has norm {norm}"
);
}
}
#[test]
fn deterministic() {
let a = generate_spreading_vectors(&[7u8; 32], 50);
let b = generate_spreading_vectors(&[7u8; 32], 50);
for (va, vb) in a.iter().zip(b.iter()) {
for (ea, eb) in va.iter().zip(vb.iter()) {
assert_eq!(ea.to_bits(), eb.to_bits());
}
}
}
#[test]
fn different_seeds_differ() {
let a = generate_spreading_vectors(&[1u8; 32], 10);
let b = generate_spreading_vectors(&[2u8; 32], 10);
assert_ne!(
a.iter().map(|v| v[0].to_bits()).collect::<Vec<_>>(),
b.iter().map(|v| v[0].to_bits()).collect::<Vec<_>>()
);
}
#[test]
fn correct_count() {
let vectors = generate_spreading_vectors(&[0u8; 32], 37);
assert_eq!(vectors.len(), 37);
}
}