use crate::constants::ROUND_CONSTANTS;
use crate::field::{Goldilocks, matmul_internal, mds_light_permutation};
const NUM_EXTERNAL: usize = 128;
pub fn permute(state: &mut [Goldilocks; 16]) {
permute_with_constants(state, &ROUND_CONSTANTS);
}
pub fn permute_with_constants(state: &mut [Goldilocks; 16], constants: &[Goldilocks]) {
let (external, internal) = constants.split_at(NUM_EXTERNAL);
let (initial_rc, terminal_rc) = external.split_at(NUM_EXTERNAL / 2);
mds_light_permutation(state);
for round in 0..4 {
let rc = &initial_rc[round * 16..(round + 1) * 16];
for i in 0..16 {
state[i] += rc[i];
state[i] = state[i].pow7();
}
mds_light_permutation(state);
}
for round in 0..64 {
state[0] += internal[round];
state[0] = state[0].pow7();
matmul_internal(state);
}
for round in 0..4 {
let rc = &terminal_rc[round * 16..(round + 1) * 16];
for i in 0..16 {
state[i] += rc[i];
state[i] = state[i].pow7();
}
mds_light_permutation(state);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn permutation_is_deterministic() {
let mut s1 = [Goldilocks::ZERO; 16];
let mut s2 = [Goldilocks::ZERO; 16];
permute(&mut s1);
permute(&mut s2);
assert_eq!(s1, s2);
}
#[test]
fn permutation_changes_state() {
let mut state = [Goldilocks::ZERO; 16];
let original = state;
permute(&mut state);
assert_ne!(state, original);
}
#[test]
fn different_inputs_different_outputs() {
let mut s1 = [Goldilocks::ZERO; 16];
let mut s2 = [Goldilocks::ZERO; 16];
s2[0] = Goldilocks::new(1);
permute(&mut s1);
permute(&mut s2);
assert_ne!(s1, s2);
}
}