use super::MlDsaError;
use super::ntt::{ZETAS, mod_q};
use super::params::N;
use super::rng::CryptoRng;
use alloc::vec;
pub fn generate_permutation(perm: &mut [u16], rng: &mut dyn CryptoRng) -> Result<(), MlDsaError> {
let n = perm.len();
for i in 0..n {
perm[i] = i as u16;
}
let mut rand_buf = [0u8; 2];
for i in (1..n).rev() {
let bound = (i + 1) as u16;
let limit = u16::MAX - (u16::MAX % bound);
loop {
rng.fill_bytes(&mut rand_buf)?;
let r = u16::from_le_bytes(rand_buf);
if r < limit {
let j = (r % bound) as usize;
perm.swap(i, j);
break;
}
}
}
Ok(())
}
pub fn ntt_shuffled(f: &mut [i32; N], rng: &mut dyn CryptoRng) -> Result<(), MlDsaError> {
let mut m = 0usize;
let mut len = 128;
while len >= 1 {
let num_groups = N / (2 * len);
let mut group_perm = vec![0u16; num_groups];
generate_permutation(&mut group_perm, rng)?;
for &gi in group_perm.iter() {
let group_index = gi as usize;
let start = group_index * 2 * len;
let zeta = ZETAS[m + 1 + group_index];
let mut inner_perm = vec![0u16; len];
generate_permutation(&mut inner_perm, rng)?;
for &ji in inner_perm.iter() {
let j = start + ji as usize;
let t = mod_q(((zeta as i64 * f[j + len] as i64) % super::params::Q as i64) as i32);
f[j + len] = mod_q(f[j] - t);
f[j] = mod_q(f[j] + t);
}
}
m += num_groups;
len /= 2;
}
for c in f.iter_mut() {
*c = mod_q(*c);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::super::ntt;
use super::*;
struct TestRng(u64);
impl CryptoRng for TestRng {
fn fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), MlDsaError> {
for chunk in dest.chunks_mut(8) {
let mut x = self.0;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.0 = x;
let bytes = x.to_le_bytes();
for (i, b) in chunk.iter_mut().enumerate() {
*b = bytes[i];
}
}
Ok(())
}
}
#[test]
fn shuffled_ntt_matches_regular_ntt() {
let mut a = [0i32; N];
for i in 0..N {
a[i] = ((i as i32 * 12345 + 7).rem_euclid(super::super::params::Q)) as i32;
}
let mut b = a;
ntt::ntt(&mut a);
let mut rng = TestRng(0xDEADBEEFCAFEBABE);
ntt_shuffled(&mut b, &mut rng).unwrap();
assert_eq!(a, b, "shuffled NTT must match the regular NTT bit-for-bit");
}
}