use ff::PrimeField;
pub trait Config<F: PrimeField, const T: usize> {
fn num_full_rounds() -> usize;
fn num_partial_rounds() -> usize;
fn num_total_rounds() -> usize;
fn sbox(x: F) -> F;
fn get_round_constants() -> &'static [F];
fn get_external_matrix() -> &'static [F];
fn get_internal_matrix() -> &'static [F];
}
pub fn sbox5<F: PrimeField>(x: F) -> F {
x.square().square() * x
}
fn linear<F: PrimeField, const T: usize>(matrix: &[F], state: [F; T]) -> [F; T] {
let mut result = [F::ZERO; T];
for i in 0..T {
for j in 0..T {
result[i] += matrix[i * T + j] * state[j];
}
}
result
}
fn external_linear<C: Config<F, T>, F: PrimeField, const T: usize>(state: [F; T]) -> [F; T] {
linear::<F, T>(C::get_external_matrix(), state)
}
fn internal_linear<C: Config<F, T>, F: PrimeField, const T: usize>(state: [F; T]) -> [F; T] {
linear::<F, T>(C::get_internal_matrix(), state)
}
pub(crate) fn permutation<C: Config<F, T>, F: PrimeField, const T: usize>(
mut state: [F; T],
) -> [F; T] {
let num_full_rounds = C::num_full_rounds();
let num_partial_rounds = C::num_partial_rounds();
let num_total_rounds = C::num_total_rounds();
assert_eq!(num_total_rounds, 2 * num_full_rounds + num_partial_rounds);
let c = C::get_round_constants();
state = external_linear::<C, F, T>(state);
for r in 0..num_full_rounds {
for i in 0..T {
state[i] += c[r * T + i];
}
for i in 0..T {
state[i] = C::sbox(state[i]);
}
state = external_linear::<C, F, T>(state);
}
for r in num_full_rounds..(num_full_rounds + num_partial_rounds) {
state[0] += c[r * T];
state[0] = C::sbox(state[0]);
state = internal_linear::<C, F, T>(state);
}
for r in (num_full_rounds + num_partial_rounds)..num_total_rounds {
for i in 0..T {
state[i] += c[r * T + i];
}
for i in 0..T {
state[i] = C::sbox(state[i]);
}
state = external_linear::<C, F, T>(state);
}
state
}
pub fn hash<C: Config<F, T>, F: PrimeField, const T: usize>(inputs: &[F]) -> F {
assert!(!inputs.is_empty());
let mut state = [F::ZERO; T];
for chunk in inputs.chunks(T - 1) {
for i in 0..chunk.len() {
state[i] += chunk[i];
}
state = permutation::<C, F, T>(state);
}
state[0]
}