use alloc::vec::Vec;
use p3_field::{ExtensionField, Field, dot_product};
use crate::Claim;
use crate::layout::witness::{Table, TablePlacement};
use crate::layout::{ProverMultiClaim, ProverVirtualClaim};
#[derive(Debug, Clone)]
pub struct StackedClaims<F: Field, EF: ExtensionField<F>> {
pub(crate) tables: Vec<Table<F>>,
pub(crate) placements: Vec<TablePlacement>,
pub(crate) num_variables: usize,
pub(crate) folding: usize,
pub(crate) claim_map: Vec<Vec<ProverMultiClaim<F, EF>>>,
pub(crate) virtual_claims: Vec<ProverVirtualClaim<EF>>,
}
impl<F: Field, EF: ExtensionField<F>> StackedClaims<F, EF> {
pub(crate) fn new(
tables: Vec<Table<F>>,
placements: Vec<TablePlacement>,
num_variables: usize,
folding: usize,
) -> Self {
let claim_map = (0..tables.len()).map(|_| Vec::new()).collect();
Self {
tables,
placements,
num_variables,
folding,
claim_map,
virtual_claims: Vec::new(),
}
}
pub(crate) const fn folding(&self) -> usize {
self.folding
}
pub(crate) const fn num_variables(&self) -> usize {
self.num_variables
}
pub(crate) fn num_variables_table(&self, id: usize) -> usize {
self.tables[id].num_variables()
}
pub fn table(&self, id: usize) -> &Table<F> {
&self.tables[id]
}
pub(crate) fn num_claims(&self) -> usize {
self.claim_map
.iter()
.flat_map(|claims| claims.iter().map(ProverMultiClaim::len))
.sum()
}
pub(crate) fn concrete_claims(&self) -> impl Iterator<Item = &ProverMultiClaim<F, EF>> {
self.placements
.iter()
.flat_map(|placement| self.claim_map[placement.idx()].iter())
}
pub(crate) fn sum(&self, alpha: EF) -> EF {
let mut sum = EF::ZERO;
let mut alphas = alpha.powers();
for placement in &self.placements {
for claim in &self.claim_map[placement.idx()] {
for opening in claim.current_openings() {
sum += opening.eval() * alphas.next().unwrap();
}
for opening in claim.next_openings() {
sum += opening.eval() * alphas.next().unwrap();
}
}
}
sum += dot_product::<EF, _, _>(
self.virtual_claims.iter().map(Claim::eval),
alpha.shifted_powers(alpha.exp_u64(self.num_claims() as u64)),
);
sum
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use p3_baby_bear::BabyBear;
use p3_field::PrimeCharacteristicRing;
use p3_field::extension::BinomialExtensionField;
use p3_multilinear_util::point::Point;
use super::*;
use crate::layout::opening::{EqSvoPartials, NextSvoPartials, Opening};
use crate::strategy::VariableOrder;
use crate::svo::SvoPoint;
type F = BabyBear;
type EF = BinomialExtensionField<F, 4>;
fn table(arity: usize) -> Table<F> {
Table::zero(1, arity)
}
fn placement(idx: usize) -> TablePlacement {
TablePlacement::new(idx, vec![])
}
fn claim(current: &[u64], next: &[u64]) -> ProverMultiClaim<F, EF> {
let point = SvoPoint::<F, EF>::new_unpacked(0, &Point::new(vec![]), VariableOrder::Prefix);
let current_openings = current
.iter()
.map(|&e| Opening::new_with_data(0, EF::from_u64(e), EqSvoPartials::new(vec![])))
.collect();
let next_openings = next
.iter()
.map(|&e| Opening::new_with_data(0, EF::from_u64(e), NextSvoPartials::new(vec![])))
.collect();
ProverMultiClaim::new(point, current_openings, next_openings)
}
#[test]
fn new_starts_empty_and_records_shape() {
let sc = StackedClaims::<F, EF>::new(
vec![table(3), table(5)],
vec![placement(1), placement(0)],
6,
2,
);
assert_eq!(sc.folding(), 2);
assert_eq!(sc.num_variables(), 6);
assert_eq!(sc.num_variables_table(0), 3);
assert_eq!(sc.num_variables_table(1), 5);
assert_eq!(sc.num_claims(), 0);
assert!(sc.concrete_claims().next().is_none());
assert_eq!(sc.claim_map.len(), 2);
assert!(sc.virtual_claims.is_empty());
}
#[test]
fn num_claims_counts_openings_in_both_groups() {
let mut sc = StackedClaims::<F, EF>::new(vec![table(2)], vec![placement(0)], 2, 0);
sc.claim_map[0].push(claim(&[3, 5], &[7]));
sc.claim_map[0].push(claim(&[11], &[]));
assert_eq!(sc.num_claims(), 4);
}
#[test]
fn concrete_claims_walks_in_placement_order() {
let mut sc = StackedClaims::<F, EF>::new(
vec![table(2), table(2)],
vec![placement(1), placement(0)],
3,
0,
);
sc.claim_map[0].push(claim(&[100], &[]));
sc.claim_map[1].push(claim(&[200], &[]));
let evals: Vec<EF> = sc
.concrete_claims()
.map(|c| c.current_openings()[0].eval())
.collect();
assert_eq!(evals, vec![EF::from_u64(200), EF::from_u64(100)]);
}
#[test]
fn sum_weights_openings_by_alpha_powers() {
let mut sc = StackedClaims::<F, EF>::new(vec![table(2)], vec![placement(0)], 2, 0);
sc.claim_map[0].push(claim(&[3, 5], &[7]));
sc.claim_map[0].push(claim(&[11], &[]));
sc.virtual_claims.push(Claim {
point: Point::new(vec![]),
eval: EF::from_u64(13),
data: Vec::new(),
});
assert_eq!(sc.num_claims(), 4);
assert_eq!(sc.sum(EF::from_u64(2)), EF::from_u64(337));
}
}