use crate::{Hash, PrimeField64};
const IV: [u32; 8] = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19];
const MSG: [[usize; 16]; 7] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8],
[3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1],
[10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6],
[12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4],
[9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7],
[11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13],
];
const CHUNK_START: u32 = 1;
const CHUNK_END: u32 = 2;
const ROOT: u32 = 8;
const GL_P: u64 = 0xFFFFFFFF00000001;
#[inline]
fn g(s: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, mx: u32, my: u32) {
s[a] = s[a].wrapping_add(s[b]).wrapping_add(mx);
s[d] = (s[d] ^ s[a]).rotate_right(16);
s[c] = s[c].wrapping_add(s[d]);
s[b] = (s[b] ^ s[c]).rotate_right(12);
s[a] = s[a].wrapping_add(s[b]).wrapping_add(my);
s[d] = (s[d] ^ s[a]).rotate_right(8);
s[c] = s[c].wrapping_add(s[d]);
s[b] = (s[b] ^ s[c]).rotate_right(7);
}
#[inline]
fn round(s: &mut [u32; 16], m: &[u32; 16], r: usize) {
let x = &MSG[r];
g(s, 0, 4, 8, 12, m[x[0]], m[x[1]]);
g(s, 1, 5, 9, 13, m[x[2]], m[x[3]]);
g(s, 2, 6, 10, 14, m[x[4]], m[x[5]]);
g(s, 3, 7, 11, 15, m[x[6]], m[x[7]]);
g(s, 0, 5, 10, 15, m[x[8]], m[x[9]]);
g(s, 1, 6, 11, 12, m[x[10]], m[x[11]]);
g(s, 2, 7, 8, 13, m[x[12]], m[x[13]]);
g(s, 3, 4, 9, 14, m[x[14]], m[x[15]]);
}
fn compress_pre(cv: &[u32; 8], block: &[u32; 16], blen: u32, counter: u64, flags: u32) -> [u32; 16] {
let mut s = [0u32; 16];
s[..8].copy_from_slice(cv);
s[8] = IV[0];
s[9] = IV[1];
s[10] = IV[2];
s[11] = IV[3];
s[12] = counter as u32;
s[13] = (counter >> 32) as u32;
s[14] = blen;
s[15] = flags;
for r in 0..7 {
round(&mut s, block, r);
}
s
}
fn compress_xof(cv: &[u32; 8], block: &[u32; 16], blen: u32, counter: u64, flags: u32) -> [u32; 16] {
let s = compress_pre(cv, block, blen, counter, flags);
let mut o = [0u32; 16];
for i in 0..8 {
o[i] = s[i] ^ s[i + 8];
o[i + 8] = s[i + 8] ^ cv[i];
}
o
}
#[inline]
fn canon(x: u64) -> u64 {
if x >= GL_P {
x - GL_P
} else {
x
}
}
pub fn blake3_permute8<F: PrimeField64>(state: &mut [F; 8]) {
let mut w = [0u64; 8];
for i in 0..8 {
w[i] = state[i].as_canonical_u64();
}
let mut b0 = [0u32; 16];
for k in 0..8 {
b0[2 * k] = w[k] as u32;
b0[2 * k + 1] = (w[k] >> 32) as u32;
}
let o0 = compress_xof(&IV, &b0, 64, 0, CHUNK_START | CHUNK_END | ROOT);
for k in 0..8 {
let v0 = (o0[2 * k] as u64) | ((o0[2 * k + 1] as u64) << 32);
state[k] = F::from_u64(canon(v0));
}
}
pub struct Blake3_8;
impl<F: PrimeField64> Hash<F> for Blake3_8 {
const WIDTH: usize = 8;
const RATE: usize = 4;
const CAPACITY: usize = 4;
type State = [F; 8];
fn hash(state: &mut [F; 8]) {
blake3_permute8::<F>(state);
}
}