use nebu::{Fp3, Goldilocks};
use super::circuit::layout::pre as pc;
use super::circuit::trace::Pre;
use crate::envelope::codec::{Reader, Writer};
const DOMAIN: &[u8] = b"zheng-recursion-key-layout-v1";
pub fn digest(bytes: &[u8]) -> [u8; 32] {
let mut h = hemera::Hasher::new();
h.update(DOMAIN);
h.update(&(bytes.len() as u64).to_le_bytes());
h.update(bytes);
*h.finalize().as_bytes()
}
pub(crate) fn write_fp3(w: &mut Writer, v: Fp3) {
for c in [v.c0, v.c1, v.c2] {
w.varint(c.as_u64());
}
}
pub(crate) fn read_fp3(r: &mut Reader) -> Result<Fp3, String> {
let mut c = [Goldilocks::ZERO; 3];
for x in &mut c {
*x = Goldilocks::new(r.field().map_err(|e| e.to_string())?);
}
Ok(Fp3 {
c0: c[0],
c1: c[1],
c2: c[2],
})
}
pub(crate) fn write_digest(w: &mut Writer, d: &[Goldilocks; 4]) {
for v in d {
w.varint(v.as_u64());
}
}
pub(crate) fn read_digest(r: &mut Reader) -> Result<[Goldilocks; 4], String> {
let mut d = [Goldilocks::ZERO; 4];
for x in &mut d {
*x = Goldilocks::new(r.field().map_err(|e| e.to_string())?);
}
Ok(d)
}
pub(crate) fn write_pre(w: &mut Writer, pre: &Pre) {
for col in &pre.cols {
let nz: Vec<(usize, Fp3)> = col
.iter()
.copied()
.enumerate()
.filter(|(_, v)| *v != Fp3::ZERO)
.collect();
w.len(nz.len());
let mut next = 0;
for (row, v) in nz {
w.varint((row - next) as u64);
next = row + 1;
write_fp3(w, v);
}
}
}
pub(crate) fn read_pre(r: &mut Reader, rows: usize) -> Result<Pre, String> {
let e = |e: crate::envelope::EnvelopeError| e.to_string();
let mut cols = vec![vec![Fp3::ZERO; rows]; pc::COUNT];
for col in &mut cols {
let n = r.len(rows, 4).map_err(e)?;
let mut next = 0usize;
for _ in 0..n {
let row = usize::try_from(r.varint().map_err(e)?)
.ok()
.and_then(|d| next.checked_add(d))
.filter(|&row| row < rows)
.ok_or("key layout: row out of range")?;
let v = read_fp3(r)?;
if v == Fp3::ZERO {
return Err("key layout: a zero entry".into());
}
col[row] = v;
next = row + 1;
}
}
Ok(Pre { cols })
}
pub(crate) fn sparse(pre: &Pre) -> Vec<Vec<(u32, Fp3)>> {
pre.cols
.iter()
.map(|c| {
c.iter()
.enumerate()
.filter(|(_, v)| **v != Fp3::ZERO)
.map(|(i, &v)| (i as u32, v))
.collect()
})
.collect()
}
pub(crate) fn ext(pre: &Pre) -> bool {
pre.cols
.iter()
.flatten()
.any(|v| v.c1 != Goldilocks::ZERO || v.c2 != Goldilocks::ZERO)
}