use nebu::Fp3;
use super::circuit::air::{CircuitAir, Row, Sink};
use super::circuit::layout::{self as cl, PIN};
use crate::air::num::Num;
use crate::air::{Air, Public, Shape, Vals};
use crate::machine::air::{KConst, Machine, PUBLICS};
use crate::machine::layout::{W1, W2};
pub const WORD: usize = 64;
pub const COLS: usize = 3 * WORD;
pub const V1_AT: usize = WORD + W2;
pub const V2_AT: usize = 2 * WORD;
pub const PUB_NOX: usize = PUBLICS;
pub const PUB_V: usize = cl::pre::COUNT;
pub const PUBS: usize = PUB_NOX + PUB_V + PIN;
pub const SEEDS: usize = 3;
pub const G_POINT: usize = 2 * COLS + PUBS + 2 + SEEDS;
const _: () = assert!(W1 == WORD && W2 + cl::V1 <= WORD && cl::V2 <= WORD);
pub struct Relation {
pub machine: Machine,
pub circuit: CircuitAir,
pub ch: [Fp3; 2],
constraints: usize,
pub base: usize,
}
impl Relation {
pub fn new(machine: Machine, ch: [Fp3; 2]) -> Self {
let circuit = CircuitAir::default();
let constraints = machine.shape().constraints + circuit.constraints();
let mut base = 2;
while base * base * base < constraints {
base += 1;
}
Self {
machine,
circuit,
ch,
constraints,
base,
}
}
pub fn constraints(&self) -> usize {
self.constraints
}
pub fn degree(&self) -> usize {
8
}
pub fn g_degree(&self) -> usize {
self.degree() + SEEDS * (self.base - 1)
}
pub fn mu<T: Num>(&self, seeds: &[T]) -> Vec<T> {
let b = self.base;
let pw = |x: T| {
let mut v = Vec::with_capacity(b);
let mut c = T::ONE;
for _ in 0..b {
v.push(c);
c *= x;
}
v
};
let (p1, p2, p3) = (pw(seeds[0]), pw(seeds[1]), pw(seeds[2]));
(0..self.constraints)
.map(|k| p1[k % b] * p2[(k / b) % b] * p3[k / (b * b)])
.collect()
}
pub fn eval(&self, local: &[Fp3], next: &[Fp3], pubs: &[Fp3], ch_v: &[Fp3], out: &mut [Fp3]) {
self.eval_with(
local,
next,
pubs,
ch_v,
&self.ch,
&self.machine.constants.lift(),
out,
);
}
#[allow(clippy::too_many_arguments)]
pub fn eval_with<T: Num>(
&self,
local: &[T],
next: &[T],
pubs: &[T],
ch_v: &[T],
ch: &[T; 2],
k: &KConst<T>,
out: &mut [T],
) {
let km = self.machine.shape().constraints;
let v = Vals {
local: &local[..W1 + W2],
next: &next[..W1 + W2],
publics: &pubs[..PUB_NOX],
};
self.machine.eval_with(&v, k, ch, &mut out[..km]);
let row = Row {
l1: &local[V1_AT..V1_AT + cl::V1],
l2: &local[V2_AT..V2_AT + cl::V2],
n1: &next[V1_AT..V1_AT + cl::V1],
n2: &next[V2_AT..V2_AT + cl::V2],
p: &pubs[PUB_NOX..PUB_NOX + PUB_V],
pin: &pubs[PUB_NOX + PUB_V..],
alpha: ch_v[0],
beta: ch_v[1],
};
let mut s = Sink {
buf: &mut out[km..],
i: 0,
};
self.circuit.eval(&row, &mut s);
}
pub fn g(&self, point: &[Fp3]) -> Fp3 {
self.g_with(point, &self.ch, &self.machine.constants.lift())
}
pub fn g_with<T: Num>(&self, point: &[T], ch: &[T; 2], k: &KConst<T>) -> T {
assert_eq!(point.len(), G_POINT);
let (local, next, pubs) = (
&point[..COLS],
&point[COLS..2 * COLS],
&point[2 * COLS..2 * COLS + PUBS],
);
let ch_v = &point[2 * COLS + PUBS..2 * COLS + PUBS + 2];
let seeds = &point[2 * COLS + PUBS + 2..];
let mut out = vec![T::ZERO; self.constraints];
self.eval_with(local, next, pubs, ch_v, ch, k, &mut out);
out.iter()
.zip(self.mu(seeds))
.fold(T::ZERO, |a, (&c, m)| a + c * m)
}
}
pub struct View<'a>(pub &'a Relation);
impl Air for View<'_> {
fn shape(&self) -> Shape {
Shape {
w1: COLS,
w2: 0,
challenges: 2,
constraints: self.0.constraints,
degree: self.0.degree(),
}
}
fn publics(&self) -> &[Public] {
&[]
}
fn eval(&self, v: &Vals<'_>, challenges: &[Fp3], out: &mut [Fp3]) {
self.0.eval(v.local, v.next, v.publics, challenges, out);
}
fn eval_mix(
&self,
v: &Vals<'_, crate::air::mix::Mix>,
challenges: &[Fp3],
out: &mut [crate::air::mix::Mix],
) -> bool {
use crate::air::mix::Mix;
let ch_v: Vec<Mix> = challenges.iter().map(|&c| Mix::of(c)).collect();
let ch = self.0.ch.map(Mix::of);
self.0.eval_with(
v.local,
v.next,
v.publics,
&ch_v,
&ch,
&self.0.machine.constants.lift(),
out,
);
true
}
fn combine_graph(
&self,
inputs: usize,
challenges: &[Fp3],
mu: &[Fp3],
) -> Option<crate::air::num::Graph> {
use crate::air::num::{Sym, record};
let rel = self.0;
Some(record(inputs, |i| {
let (local, rest) = i.split_at(COLS);
let (next, pubs) = rest.split_at(COLS);
let ch_v: Vec<Sym> = challenges.iter().map(|&c| Sym::C(c)).collect();
let mut out = vec![Sym::ZERO; rel.constraints];
rel.eval_with(
local,
next,
pubs,
&ch_v,
&rel.ch.map(Sym::C),
&rel.machine.constants.lift(),
&mut out,
);
vec![
out.iter()
.zip(mu)
.fold(Sym::ZERO, |acc, (&c, &m)| acc + c * Sym::C(m)),
]
}))
}
}