use starkom_ff::PrimeField;
pub trait Config<F: PrimeField, const T: usize, const R: usize, const C: usize> {
fn num_full_rounds() -> usize;
fn num_partial_rounds() -> usize;
fn num_total_rounds() -> usize {
Self::num_full_rounds() * 2 + Self::num_partial_rounds()
}
fn sbox(x: F) -> F;
fn get_round_constants() -> &'static [F];
fn get_mds_matrix() -> &'static [F];
}
pub fn sbox5<F: PrimeField>(x: F) -> F {
x.square().square() * x
}
fn mds<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
}
pub fn permutation<
Cfg: Config<F, T, R, C>,
F: PrimeField,
const T: usize,
const R: usize,
const C: usize,
>(
mut state: [F; T],
) -> [F; T] {
const { assert!(T == R + C) };
let num_full_rounds = Cfg::num_full_rounds();
let num_partial_rounds = Cfg::num_partial_rounds();
let num_total_rounds = Cfg::num_total_rounds();
assert_eq!(num_total_rounds, 2 * num_full_rounds + num_partial_rounds);
let c = Cfg::get_round_constants();
let m = Cfg::get_mds_matrix();
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] = Cfg::sbox(state[i]);
}
state = mds::<F, T>(m, state);
}
for r in num_full_rounds..(num_full_rounds + num_partial_rounds) {
for i in 0..T {
state[i] += c[r * T + i];
}
state[0] = Cfg::sbox(state[0]);
state = mds::<F, T>(m, 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] = Cfg::sbox(state[i]);
}
state = mds::<F, T>(m, state);
}
state
}
pub fn hash<
Cfg: Config<F, T, R, C>,
F: PrimeField,
const T: usize,
const R: usize,
const C: usize,
>(
inputs: &[F],
) -> [F; T] {
assert!(!inputs.is_empty());
let mut state = [F::ZERO; T];
for chunk in inputs.chunks(T - C) {
for i in 0..chunk.len() {
state[i] += chunk[i];
}
state = permutation::<Cfg, F, T, R, C>(state);
}
state
}
pub fn hash0<
Cfg: Config<F, T, R, C>,
F: PrimeField,
const T: usize,
const R: usize,
const C: usize,
>(
inputs: &[F],
) -> F {
hash::<Cfg, F, T, R, C>(inputs)[0]
}