use crate::constants::{P0, ROTATION_SCHEDULE};
use crate::utils::rotl64;
const ROUNDS: usize = 16;
#[inline]
pub fn clock_permute(state: &mut [u64; 8]) {
for round in 0..ROUNDS {
for i in 0..8 {
let next_idx = (i + 1) % 8;
state[i] = state[i].wrapping_add(state[next_idx]).wrapping_mul(P0);
}
for i in 0..8 {
let xor_idx = (i + 3) % 8;
let rotation_idx = (round + i) % 16;
let rotation = ROTATION_SCHEDULE[rotation_idx];
state[i] = rotl64(state[i] ^ state[xor_idx], rotation);
}
state.swap(1, 5);
state.swap(2, 6);
state.swap(3, 7);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clock_permute_zeros() {
let mut state = [0u64; 8];
let original = state;
clock_permute(&mut state);
assert_eq!(state, original);
}
#[test]
fn test_clock_permute_basic() {
let mut state = [0u64; 8];
state[0] = 0x1234567890ABCDEF;
let original = state;
clock_permute(&mut state);
assert_ne!(state, original);
}
#[test]
fn test_clock_permute_avalanche() {
let mut state1 = [0u64; 8];
let mut state2 = [0u64; 8];
state1[0] = 0x0000000000000000;
state2[0] = 0x0000000000000001;
clock_permute(&mut state1);
clock_permute(&mut state2);
assert_ne!(state1, state2);
}
#[test]
fn test_clock_permute_deterministic() {
let mut state = [0u64; 8];
state[0] = 0xDEADBEEFCAFEBABE;
let mut state_copy = state;
clock_permute(&mut state);
clock_permute(&mut state_copy);
assert_eq!(state, state_copy);
}
#[test]
fn test_clock_permute_full_diffusion() {
let mut state1 = [0u64; 8];
let mut state2 = [0u64; 8];
state1[0] = 1;
state2[0] = 0;
clock_permute(&mut state1);
clock_permute(&mut state2);
for i in 0..8 {
assert_ne!(state1[i], state2[i], "Word {} should differ", i);
}
}
}