use std::collections::{BTreeMap, VecDeque};
use crate::{
fft::{DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain},
polycommit::sonic_pc::LabeledPolynomial,
r1cs::{SynthesisError, SynthesisResult},
snark::varuna::{AHPError, AHPForR1CS, Circuit, SNARKMode},
};
use anyhow::anyhow;
use snarkvm_fields::PrimeField;
pub struct CircuitSpecificState<F: PrimeField> {
pub(super) input_domain: EvaluationDomain<F>,
pub(super) variable_domain: EvaluationDomain<F>,
pub(super) constraint_domain: EvaluationDomain<F>,
pub(super) non_zero_a_domain: EvaluationDomain<F>,
pub(super) non_zero_b_domain: EvaluationDomain<F>,
pub(super) non_zero_c_domain: EvaluationDomain<F>,
pub(in crate::snark) batch_size: usize,
pub(super) padded_public_variables: Vec<Vec<F>>,
pub(super) private_variables: Vec<Vec<F>>,
pub(super) z_a: Option<Vec<Vec<F>>>,
pub(super) z_b: Option<Vec<Vec<F>>>,
pub(super) z_c: Option<Vec<Vec<F>>>,
pub(super) x_polys: Vec<DensePolynomial<F>>,
pub(in crate::snark) z_m_at_alpha_polys: Option<VecDeque<[DensePolynomial<F>; 3]>>,
pub(in crate::snark) a_polys: Option<[LabeledPolynomial<F>; 3]>,
pub(in crate::snark) b_polys: Option<[LabeledPolynomial<F>; 3]>,
pub(super) lhs_polynomials: Option<[DensePolynomial<F>; 3]>,
}
pub struct State<'a, F: PrimeField, SM: SNARKMode> {
pub(in crate::snark) circuit_specific_states: BTreeMap<&'a Circuit<F, SM>, CircuitSpecificState<F>>,
pub(in crate::snark) first_round_oracles: Option<super::FirstOracles<F>>,
pub(in crate::snark) max_non_zero_domain: EvaluationDomain<F>,
pub(in crate::snark) max_constraint_domain: EvaluationDomain<F>,
pub(in crate::snark) max_variable_domain: EvaluationDomain<F>,
pub(in crate::snark) total_instances: usize,
}
type PaddedPubInputs<F> = Vec<F>;
type PrivateInputs<F> = Vec<F>;
type Za<F> = Vec<F>;
type Zb<F> = Vec<F>;
type Zc<F> = Vec<F>;
pub(super) struct Assignments<F>(
pub(super) PaddedPubInputs<F>,
pub(super) PrivateInputs<F>,
pub(super) Za<F>,
pub(super) Zb<F>,
pub(super) Zc<F>,
);
impl<'a, F: PrimeField, SM: SNARKMode> State<'a, F, SM> {
pub(super) fn initialize(
indices_and_assignments: BTreeMap<&'a Circuit<F, SM>, Vec<Assignments<F>>>,
) -> Result<Self, AHPError> {
let mut max_non_zero_domain: Option<EvaluationDomain<F>> = None;
let mut max_num_constraints = 0;
let mut max_num_variables = 0;
let mut total_instances = 0usize;
let circuit_specific_states = indices_and_assignments
.into_iter()
.map(|(circuit, variable_assignments)| {
let index_info = &circuit.index_info;
let constraint_domain =
EvaluationDomain::new(index_info.num_constraints).ok_or(SynthesisError::PolyTooLarge)?;
max_num_constraints = max_num_constraints.max(index_info.num_constraints);
let variable_domain = EvaluationDomain::new(index_info.num_public_and_private_variables)
.ok_or(SynthesisError::PolyTooLarge)?;
max_num_variables = max_num_variables.max(index_info.num_public_and_private_variables);
let non_zero_domains = AHPForR1CS::<_, SM>::cmp_non_zero_domains(index_info, max_non_zero_domain)?;
max_non_zero_domain = non_zero_domains.max_non_zero_domain;
let first_padded_public_inputs = &variable_assignments[0].0;
let input_domain = EvaluationDomain::new(first_padded_public_inputs.len())
.ok_or(anyhow!("Cannot create EvaluationDomain"))?;
let batch_size = variable_assignments.len();
total_instances =
total_instances.checked_add(batch_size).ok_or_else(|| anyhow!("Batch size too large"))?;
let mut z_as = Vec::with_capacity(batch_size);
let mut z_bs = Vec::with_capacity(batch_size);
let mut z_cs = Vec::with_capacity(batch_size);
let mut x_polys = Vec::with_capacity(batch_size);
let mut padded_public_variables = Vec::with_capacity(batch_size);
let mut private_variables = Vec::with_capacity(batch_size);
for Assignments(padded_public_input, private_input, z_a, z_b, z_c) in variable_assignments {
z_as.push(z_a);
z_bs.push(z_b);
z_cs.push(z_c);
let x_poly = EvaluationsOnDomain::from_vec_and_domain(padded_public_input.clone(), input_domain)
.interpolate();
x_polys.push(x_poly);
padded_public_variables.push(padded_public_input);
private_variables.push(private_input);
}
let state = CircuitSpecificState {
input_domain,
variable_domain,
constraint_domain,
non_zero_a_domain: non_zero_domains.domain_a,
non_zero_b_domain: non_zero_domains.domain_b,
non_zero_c_domain: non_zero_domains.domain_c,
batch_size,
padded_public_variables,
x_polys,
private_variables,
z_a: Some(z_as),
z_b: Some(z_bs),
z_c: Some(z_cs),
z_m_at_alpha_polys: None,
a_polys: None,
b_polys: None,
lhs_polynomials: None,
};
Ok((circuit, state))
})
.collect::<SynthesisResult<BTreeMap<_, _>>>()?;
let max_non_zero_domain = max_non_zero_domain.ok_or(AHPError::BatchSizeIsZero)?;
let max_constraint_domain = EvaluationDomain::new(max_num_constraints).ok_or(SynthesisError::PolyTooLarge)?;
let max_variable_domain = EvaluationDomain::new(max_num_variables).ok_or(SynthesisError::PolyTooLarge)?;
Ok(Self {
max_constraint_domain,
max_variable_domain,
max_non_zero_domain,
circuit_specific_states,
total_instances,
first_round_oracles: None,
})
}
pub fn batch_size(&self, circuit: &Circuit<F, SM>) -> Option<usize> {
self.circuit_specific_states.get(circuit).map(|s| s.batch_size)
}
pub fn public_inputs(&self, circuit: &Circuit<F, SM>) -> Option<Vec<Vec<F>>> {
self.circuit_specific_states.get(circuit).map(|s| {
s.padded_public_variables.iter().map(|v| super::ConstraintSystem::unformat_public_input(v)).collect()
})
}
pub fn padded_public_inputs(&self, circuit: &Circuit<F, SM>) -> Option<&[Vec<F>]> {
self.circuit_specific_states.get(circuit).map(|s| s.padded_public_variables.as_slice())
}
pub fn lhs_polys_into_iter(self) -> impl Iterator<Item = DensePolynomial<F>> + 'a {
self.circuit_specific_states.into_values().flat_map(|s| s.lhs_polynomials.unwrap().into_iter())
}
}