use alloc::vec::Vec;
use core::fmt::Debug;
use p3_field::ExtensionField;
use p3_matrix::Matrix;
use p3_matrix::dense::RowMajorMatrix;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::{PeriodicColumns, PeriodicLdeTable, PolynomialSpace};
pub type Val<D> = <D as PolynomialSpace>::Val;
pub trait Pcs<Challenge, Challenger>
where
Challenge: ExtensionField<Val<Self::Domain>>,
{
type Domain: PolynomialSpace;
type Commitment: Clone + Serialize + DeserializeOwned;
type ProverData;
type Proof: Clone + Serialize + DeserializeOwned;
type Error: Debug;
type ProverError: Debug;
fn natural_domain_for_degree(&self, degree: usize) -> Self::Domain;
#[allow(clippy::type_complexity)]
fn commit(
&self,
evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val<Self::Domain>>)>,
) -> Result<(Self::Commitment, Self::ProverData), Self::ProverError>;
fn open(
&self,
commitment_data_with_opening_points: Vec<OpeningRequest<'_, Self::ProverData, Challenge>>,
fiat_shamir_challenger: &mut Challenger,
) -> Result<(OpenedValues<Challenge>, Self::Proof), Self::ProverError>;
fn verify(
&self,
commitments_with_opening_points: Vec<
CommitmentOpening<Challenge, Self::Commitment, Self::Domain>,
>,
proof: &Self::Proof,
fiat_shamir_challenger: &mut Challenger,
) -> Result<(), Self::Error>;
}
pub trait UnivariateStarkPcs<Challenge, Challenger>: Pcs<Challenge, Challenger>
where
Challenge: ExtensionField<Val<Self::Domain>>,
{
type EvaluationsOnDomain<'a>: Matrix<Val<Self::Domain>> + 'a;
const ZK: bool;
fn log_max_trace_height(&self) -> usize;
fn log_min_trace_height(&self) -> usize;
fn commit_preprocessing(
&self,
evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val<Self::Domain>>)>,
) -> Result<(Self::Commitment, Self::ProverData), Self::ProverError> {
self.commit(evaluations)
}
#[allow(clippy::type_complexity)]
fn commit_quotient(
&self,
quotient_domain: Self::Domain,
quotient_evaluations: RowMajorMatrix<Val<Self::Domain>>,
num_chunks: usize,
) -> Result<(Self::Commitment, Self::ProverData), Self::ProverError> {
let quotient_sub_evaluations =
quotient_domain.split_evals(num_chunks, quotient_evaluations);
let quotient_sub_domains = quotient_domain.split_domains(num_chunks);
let ldes = self.get_quotient_ldes(
quotient_sub_domains
.into_iter()
.zip(quotient_sub_evaluations),
num_chunks,
)?;
self.commit_ldes(ldes)
}
#[allow(clippy::type_complexity)]
fn get_quotient_ldes(
&self,
evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val<Self::Domain>>)>,
num_chunks: usize,
) -> Result<Vec<RowMajorMatrix<Val<Self::Domain>>>, Self::ProverError>;
fn commit_ldes(
&self,
ldes: Vec<RowMajorMatrix<Val<Self::Domain>>>,
) -> Result<(Self::Commitment, Self::ProverData), Self::ProverError>;
fn get_evaluations_on_domain<'a>(
&self,
prover_data: &'a Self::ProverData,
idx: usize,
domain: Self::Domain,
) -> Self::EvaluationsOnDomain<'a>;
fn get_evaluations_on_domain_no_random<'a>(
&self,
prover_data: &'a Self::ProverData,
idx: usize,
domain: Self::Domain,
) -> Self::EvaluationsOnDomain<'a> {
self.get_evaluations_on_domain(prover_data, idx, domain)
}
fn open_with_preprocessing(
&self,
commitment_data_with_opening_points: Vec<OpeningRequest<'_, Self::ProverData, Challenge>>,
fiat_shamir_challenger: &mut Challenger,
_preprocessed_commitment: Option<usize>,
) -> Result<(OpenedValues<Challenge>, Self::Proof), Self::ProverError> {
assert!(
!Self::ZK,
"open_with_preprocessing should have a different implementation when ZK is enabled"
);
self.open(commitment_data_with_opening_points, fiat_shamir_challenger)
}
fn verify_with_preprocessing(
&self,
rounds: Vec<CommitmentOpening<Challenge, Self::Commitment, Self::Domain>>,
proof: &Self::Proof,
challenger: &mut Challenger,
_preprocessed_commitment: Option<usize>,
) -> Result<(), Self::Error> {
self.verify(rounds, proof, challenger)
}
#[allow(clippy::type_complexity)]
fn get_opt_randomization_poly_commitment(
&self,
_domain: impl IntoIterator<Item = Self::Domain>,
) -> Result<Option<(Self::Commitment, Self::ProverData)>, Self::ProverError> {
Ok(None)
}
fn build_periodic_lde_table(
&self,
periodic_cols: &[Vec<Val<Self::Domain>>],
trace_domain: Self::Domain,
quotient_domain: Self::Domain,
) -> PeriodicLdeTable<Val<Self::Domain>>
where
Self::Domain: Clone,
Val<Self::Domain>: Clone,
{
let trace_size = trace_domain.size();
let quotient_size = quotient_domain.size();
assert!(
quotient_size >= trace_size,
"quotient domain size ({quotient_size}) must be >= trace domain size ({trace_size})",
);
assert!(
quotient_size.is_multiple_of(trace_size),
"quotient domain size ({quotient_size}) must be divisible by trace domain size ({trace_size})",
);
let blowup = quotient_size / trace_size;
let periodic_cols =
PeriodicColumns::new(periodic_cols, trace_size).unwrap_or_else(|err| panic!("{err}"));
let Some(max_period) = periodic_cols.max_period() else {
return PeriodicLdeTable::empty();
};
let extended_height = max_period
.checked_mul(blowup)
.expect("extended height overflow when computing max_period * blowup");
debug_assert!(extended_height <= quotient_size);
let num_cols = periodic_cols.len();
let row_major_capacity = extended_height
.checked_mul(num_cols)
.expect("row-major periodic table capacity overflow");
let mut quotient_pts = Vec::with_capacity(extended_height);
let mut pt = quotient_domain.first_point();
for _ in 0..extended_height {
quotient_pts.push(pt);
pt = quotient_domain
.next_point(pt)
.expect("quotient domain must support next_point");
}
let padded_cols: Vec<Vec<Val<Self::Domain>>> = periodic_cols
.as_slice()
.iter()
.map(|col| (0..max_period).map(|i| col[i % col.len()]).collect())
.collect();
let mut row_major = Vec::with_capacity(row_major_capacity);
for point in quotient_pts.iter().take(extended_height) {
for padded in &padded_cols {
row_major.push(trace_domain.evaluate_periodic_column_at(padded, *point));
}
}
PeriodicLdeTable::new(RowMajorMatrix::new(row_major, num_cols))
}
}
#[derive(Clone, Debug)]
pub struct CommitmentOpening<Challenge, Commitment, Domain> {
pub commitment: Commitment,
pub matrices: Vec<MatrixOpening<Challenge, Domain>>,
}
#[derive(Clone, Debug)]
pub struct MatrixOpening<Challenge, Domain> {
pub domain: Domain,
pub points: Vec<PointOpening<Challenge>>,
}
#[derive(Clone, Debug)]
pub struct PointOpening<Challenge> {
pub point: Challenge,
pub values: Vec<Challenge>,
}
#[derive(Debug)]
pub struct OpeningRequest<'a, ProverData, Challenge> {
pub prover_data: &'a ProverData,
pub points: Vec<Vec<Challenge>>,
}
impl<ProverData, Challenge: Clone> Clone for OpeningRequest<'_, ProverData, Challenge> {
fn clone(&self) -> Self {
Self {
prover_data: self.prover_data,
points: self.points.clone(),
}
}
}
impl<'a, ProverData, Challenge> From<(&'a ProverData, Vec<Vec<Challenge>>)>
for OpeningRequest<'a, ProverData, Challenge>
{
fn from((prover_data, points): (&'a ProverData, Vec<Vec<Challenge>>)) -> Self {
Self {
prover_data,
points,
}
}
}
impl<Challenge> From<(Challenge, Vec<Challenge>)> for PointOpening<Challenge> {
fn from((point, values): (Challenge, Vec<Challenge>)) -> Self {
Self { point, values }
}
}
impl<Challenge, Domain> From<(Domain, Vec<(Challenge, Vec<Challenge>)>)>
for MatrixOpening<Challenge, Domain>
{
fn from((domain, points): (Domain, Vec<(Challenge, Vec<Challenge>)>)) -> Self {
Self {
domain,
points: points.into_iter().map(Into::into).collect(),
}
}
}
impl<Challenge, Commitment, Domain>
From<(Commitment, Vec<(Domain, Vec<(Challenge, Vec<Challenge>)>)>)>
for CommitmentOpening<Challenge, Commitment, Domain>
{
fn from(
(commitment, matrices): (Commitment, Vec<(Domain, Vec<(Challenge, Vec<Challenge>)>)>),
) -> Self {
Self {
commitment,
matrices: matrices.into_iter().map(Into::into).collect(),
}
}
}
pub type CommitmentWithOpeningPoints<Challenge, Commitment, Domain> =
CommitmentOpening<Challenge, Commitment, Domain>;
pub type OpenedValues<F> = Vec<OpenedValuesForRound<F>>;
pub type OpenedValuesForRound<F> = Vec<OpenedValuesForMatrix<F>>;
pub type OpenedValuesForMatrix<F> = Vec<OpenedValuesForPoint<F>>;
pub type OpenedValuesForPoint<F> = Vec<F>;