#[cfg(not(feature = "std"))]
use alloc::{format, vec, vec::Vec};
use core::cmp::min;
use core::mem::swap;
use anyhow::{ensure, Result};
use hashbrown::HashMap;
use plonky2_maybe_rayon::*;
use qp_plonky2_core::{PolyFriZkConfig, ZkMode};
use super::circuit_builder::{LookupChallenges, LookupWire};
use crate::field::extension::Extendable;
use crate::field::polynomial::{PolynomialCoeffs, PolynomialValues};
use crate::field::types::Field;
use crate::field::zero_poly_coset::ZeroPolyOnCoset;
use crate::fri::oracle::PolynomialBatch;
use crate::fri::{FriChallenger, FriParamsObserve};
use crate::gates::lookup::LookupGate;
use crate::gates::lookup_table::LookupTableGate;
use crate::gates::selectors::LookupSelectors;
use crate::hash::hash_types::RichField;
use crate::iop::challenger::Challenger;
use crate::iop::generator::generate_partial_witness;
use crate::iop::target::Target;
use crate::iop::witness::{MatrixWitness, PartialWitness, PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::NUM_COINS_LOOKUP;
use crate::plonk::circuit_data::{CommonCircuitData, ProverOnlyCircuitData};
use crate::plonk::config::{GenericConfig, Hasher};
use crate::plonk::plonk_common::PlonkOracle;
use crate::plonk::proof::{OpeningSet, Proof, ProofWithPublicInputs};
use crate::plonk::vanishing_poly::{eval_vanishing_poly_base_batch, get_lut_poly};
use crate::plonk::vars::EvaluationVarsBaseBatch;
use crate::plonk::zk::{
commit_coeffs_with_split_mask, commit_values_with_split_mask, LogicalPolynomialBatch,
SplitMaskPlan,
};
use crate::timed;
use crate::util::partial_products::{partial_products_and_z_gx, quotient_chunk_products};
use crate::util::timing::TimingTree;
use crate::util::{log2_ceil, transpose};
pub fn set_lookup_wires<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
prover_data: &ProverOnlyCircuitData<F, C, D>,
common_data: &CommonCircuitData<F, D>,
pw: &mut PartitionWitness<F>,
) -> Result<()> {
for (
lut_index,
&LookupWire {
last_lu_gate: _,
last_lut_gate,
first_lut_gate,
},
) in prover_data.lookup_rows.iter().enumerate()
{
let lut_len = common_data.luts[lut_index].len();
let num_entries = LookupGate::num_slots(&common_data.config);
let num_lut_entries = LookupTableGate::num_slots(&common_data.config);
let mut multiplicities = vec![0; lut_len];
let table_value_to_idx: HashMap<u16, usize> = common_data.luts[lut_index]
.iter()
.enumerate()
.map(|(i, (inp_target, _))| (*inp_target, i))
.collect();
for (inp_target, _) in prover_data.lut_to_lookups[lut_index].iter() {
let inp_value = pw.get_target(*inp_target);
let idx = table_value_to_idx
.get(&u16::try_from(inp_value.to_canonical_u64()).unwrap())
.unwrap();
multiplicities[*idx] += 1;
}
let remaining_slots = (num_entries
- (prover_data.lut_to_lookups[lut_index].len() % num_entries))
% num_entries;
let (first_inp_value, first_out_value) = common_data.luts[lut_index][0];
for slot in (num_entries - remaining_slots)..num_entries {
let inp_target =
Target::wire(last_lut_gate - 1, LookupGate::wire_ith_looking_inp(slot));
let out_target =
Target::wire(last_lut_gate - 1, LookupGate::wire_ith_looking_out(slot));
pw.set_target(inp_target, F::from_canonical_u16(first_inp_value))?;
pw.set_target(out_target, F::from_canonical_u16(first_out_value))?;
multiplicities[0] += 1;
}
for lut_entry in 0..lut_len {
let row = first_lut_gate - lut_entry / num_lut_entries;
let col = lut_entry % num_lut_entries;
let mul_target = Target::wire(row, LookupTableGate::wire_ith_multiplicity(col));
pw.set_target(
mul_target,
F::from_canonical_usize(multiplicities[lut_entry]),
)?;
}
}
Ok(())
}
pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
prover_data: &ProverOnlyCircuitData<F, C, D>,
common_data: &CommonCircuitData<F, D>,
inputs: PartialWitness<F>,
timing: &mut TimingTree,
) -> Result<ProofWithPublicInputs<F, C, D>>
where
C::Hasher: Hasher<F>,
C::InnerHasher: Hasher<F>,
{
let partition_witness = timed!(
timing,
&format!("run {} generators", prover_data.generators.len()),
generate_partial_witness(inputs, prover_data, common_data)?
);
prove_with_partition_witness(prover_data, common_data, partition_witness, timing)
}
pub fn prove_with_partition_witness<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
prover_data: &ProverOnlyCircuitData<F, C, D>,
common_data: &CommonCircuitData<F, D>,
mut partition_witness: PartitionWitness<F>,
timing: &mut TimingTree,
) -> Result<ProofWithPublicInputs<F, C, D>>
where
C::Hasher: Hasher<F>,
C::InnerHasher: Hasher<F>,
{
let has_lookup = !common_data.luts.is_empty();
let config = &common_data.config;
let num_challenges = config.num_challenges;
let quotient_degree = common_data.quotient_degree();
let degree = common_data.degree();
set_lookup_wires(prover_data, common_data, &mut partition_witness)?;
let public_inputs = partition_witness.get_targets(&prover_data.public_inputs);
let public_inputs_hash = C::InnerHasher::hash_no_pad(&public_inputs);
let witness = timed!(
timing,
"compute full witness",
partition_witness.full_witness()
);
let wires_values: Vec<PolynomialValues<F>> = timed!(
timing,
"compute wire polynomials",
witness
.wire_values
.par_iter()
.map(|column| PolynomialValues::new(column.clone()))
.collect()
);
let wires_commitment = timed!(
timing,
"compute wires commitment",
commit_values_with_split_mask::<F, C, D>(
wires_values,
wire_mask_plan(common_data).as_ref(),
Some(common_data.public_initial_degree()),
config.fri_config.rate_bits,
config.uses_leaf_hiding() && PlonkOracle::WIRES.blinding,
config.fri_config.cap_height,
timing,
prover_data.fft_root_table.as_ref(),
)
);
let mut challenger = Challenger::<F, C::Hasher>::new();
common_data.fri_params.observe(&mut challenger);
challenger.observe_hash::<C::Hasher>(prover_data.circuit_digest);
challenger.observe_hash::<C::InnerHasher>(public_inputs_hash);
challenger.observe_cap::<C::Hasher>(&wires_commitment.raw.merkle_tree.cap);
let num_lookup_challenges = NUM_COINS_LOOKUP * num_challenges;
let betas = challenger.get_n_challenges(num_challenges);
let gammas = challenger.get_n_challenges(num_challenges);
let deltas = if has_lookup {
let mut delts = Vec::with_capacity(2 * num_challenges);
let num_additional_challenges = num_lookup_challenges - 2 * num_challenges;
let additional = challenger.get_n_challenges(num_additional_challenges);
delts.extend(&betas);
delts.extend(&gammas);
delts.extend(additional);
delts
} else {
vec![]
};
assert!(
common_data.quotient_degree_factor < common_data.config.num_routed_wires,
"When the number of routed wires is smaller that the degree, we should change the logic to avoid computing partial products."
);
let mut partial_products_and_zs = timed!(
timing,
"compute partial products",
all_wires_permutation_partial_products(&witness, &betas, &gammas, prover_data, common_data)
);
let plonk_z_vecs = partial_products_and_zs
.iter_mut()
.map(|partial_products_and_z| partial_products_and_z.pop().unwrap())
.collect();
let zs_partial_products = [plonk_z_vecs, partial_products_and_zs.concat()].concat();
let lookup_polys =
compute_all_lookup_polys(&witness, &deltas, prover_data, common_data, has_lookup);
let zs_partial_products_lookups = if has_lookup {
[zs_partial_products, lookup_polys].concat()
} else {
zs_partial_products
};
let partial_products_zs_and_lookup_commitment = timed!(
timing,
"commit to partial products, Z's and, if any, lookup polynomials",
commit_values_with_split_mask::<F, C, D>(
zs_partial_products_lookups,
z_mask_plan(common_data).as_ref(),
Some(common_data.public_initial_degree()),
config.fri_config.rate_bits,
config.uses_leaf_hiding() && PlonkOracle::ZS_PARTIAL_PRODUCTS.blinding,
config.fri_config.cap_height,
timing,
prover_data.fft_root_table.as_ref(),
)
);
challenger.observe_cap::<C::Hasher>(
&partial_products_zs_and_lookup_commitment
.raw
.merkle_tree
.cap,
);
let alphas = challenger.get_n_challenges(num_challenges);
let quotient_polys = timed!(
timing,
"compute quotient polys",
compute_quotient_polys::<F, C, D>(
common_data,
prover_data,
&public_inputs_hash,
&wires_commitment,
&partial_products_zs_and_lookup_commitment,
&betas,
&gammas,
&deltas,
&alphas,
)
);
let all_quotient_poly_chunks: Vec<PolynomialCoeffs<F>> = timed!(
timing,
"split up quotient polys",
quotient_polys
.into_par_iter()
.flat_map(|mut quotient_poly| {
quotient_poly.trim_to_len(quotient_degree).expect(
"Quotient has failed, the vanishing polynomial is not divisible by Z_H",
);
quotient_poly.chunks(degree)
})
.collect()
);
let quotient_polys_commitment = timed!(
timing,
"commit to quotient polys",
commit_coeffs_with_split_mask::<F, C, D>(
all_quotient_poly_chunks,
None,
Some(common_data.public_initial_degree()),
config.fri_config.rate_bits,
config.uses_leaf_hiding() && PlonkOracle::QUOTIENT.blinding,
config.fri_config.cap_height,
timing,
None,
)
);
challenger.observe_cap::<C::Hasher>("ient_polys_commitment.raw.merkle_tree.cap);
let zeta = challenger.get_extension_challenge::<D>();
let g = F::Extension::primitive_root_of_unity(common_data.degree_bits());
ensure!(
zeta.exp_power_of_2(common_data.degree_bits()) != F::Extension::ONE,
"Opening point is in the subgroup."
);
let openings = timed!(
timing,
"construct the opening set, including lookups",
OpeningSet::new(
zeta,
g,
&prover_data.constants_sigmas_commitment,
&wires_commitment,
&partial_products_zs_and_lookup_commitment,
"ient_polys_commitment,
common_data
)
);
challenger.observe_openings(&openings.to_fri_openings());
let instance = common_data.get_fri_instance(zeta);
let opening_proof = timed!(
timing,
"compute opening proofs",
PolynomialBatch::<F, C, D>::prove_openings_masked(
&instance,
&[
&prover_data.constants_sigmas_commitment,
&wires_commitment.raw,
&partial_products_zs_and_lookup_commitment.raw,
"ient_polys_commitment.raw,
],
&mut challenger,
&common_data.fri_params,
timing,
)
);
let proof = Proof::<F, C, D> {
wires_cap: wires_commitment.raw.merkle_tree.cap,
plonk_zs_partial_products_cap: partial_products_zs_and_lookup_commitment
.raw
.merkle_tree
.cap,
quotient_polys_cap: quotient_polys_commitment.raw.merkle_tree.cap,
openings,
opening_proof,
};
Ok(ProofWithPublicInputs::<F, C, D> {
proof,
public_inputs,
})
}
fn split_mask_plan_from_mode<F: RichField + Extendable<D>, const D: usize>(
common_data: &CommonCircuitData<F, D>,
select_degree: impl FnOnce(&PolyFriZkConfig) -> usize,
) -> Option<SplitMaskPlan> {
match &common_data.config.zk_config.mode {
ZkMode::Disabled => None,
ZkMode::RowBlinding => None,
ZkMode::PolyFri(poly_fri) => Some(SplitMaskPlan {
split_power: common_data.degree(),
mask_degree: select_degree(poly_fri),
}),
}
}
fn wire_mask_plan<F: RichField + Extendable<D>, const D: usize>(
common_data: &CommonCircuitData<F, D>,
) -> Option<SplitMaskPlan> {
split_mask_plan_from_mode(common_data, |cfg| cfg.wire_mask_degree)
}
fn z_mask_plan<F: RichField + Extendable<D>, const D: usize>(
common_data: &CommonCircuitData<F, D>,
) -> Option<SplitMaskPlan> {
split_mask_plan_from_mode(common_data, |cfg| cfg.z_mask_degree)
}
fn all_wires_permutation_partial_products<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
witness: &MatrixWitness<F>,
betas: &[F],
gammas: &[F],
prover_data: &ProverOnlyCircuitData<F, C, D>,
common_data: &CommonCircuitData<F, D>,
) -> Vec<Vec<PolynomialValues<F>>> {
(0..common_data.config.num_challenges)
.map(|i| {
wires_permutation_partial_products_and_zs(
witness,
betas[i],
gammas[i],
prover_data,
common_data,
)
})
.collect()
}
fn wires_permutation_partial_products_and_zs<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
witness: &MatrixWitness<F>,
beta: F,
gamma: F,
prover_data: &ProverOnlyCircuitData<F, C, D>,
common_data: &CommonCircuitData<F, D>,
) -> Vec<PolynomialValues<F>> {
let degree = common_data.permutation_partial_product_degree();
let subgroup = &prover_data.subgroup;
let k_is = &common_data.k_is;
let num_prods = common_data.num_partial_products;
let all_quotient_chunk_products = subgroup
.par_iter()
.enumerate()
.map(|(i, &x)| {
let s_sigmas = &prover_data.sigmas[i];
let numerators = (0..common_data.config.num_routed_wires).map(|j| {
let wire_value = witness.get_wire(i, j);
let k_i = k_is[j];
let s_id = k_i * x;
wire_value + beta * s_id + gamma
});
let denominators = (0..common_data.config.num_routed_wires)
.map(|j| {
let wire_value = witness.get_wire(i, j);
let s_sigma = s_sigmas[j];
wire_value + beta * s_sigma + gamma
})
.collect::<Vec<_>>();
let denominator_invs = F::batch_multiplicative_inverse(&denominators);
let quotient_values = numerators
.zip(denominator_invs)
.map(|(num, den_inv)| num * den_inv)
.collect::<Vec<_>>();
quotient_chunk_products("ient_values, degree)
})
.collect::<Vec<_>>();
let mut z_x = F::ONE;
let mut all_partial_products_and_zs = Vec::with_capacity(all_quotient_chunk_products.len());
for quotient_chunk_products in all_quotient_chunk_products {
let mut partial_products_and_z_gx =
partial_products_and_z_gx(z_x, "ient_chunk_products);
swap(&mut z_x, &mut partial_products_and_z_gx[num_prods]);
all_partial_products_and_zs.push(partial_products_and_z_gx);
}
transpose(&all_partial_products_and_zs)
.into_par_iter()
.map(PolynomialValues::new)
.collect()
}
fn compute_lookup_polys<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
witness: &MatrixWitness<F>,
deltas: &[F; 4],
prover_data: &ProverOnlyCircuitData<F, C, D>,
common_data: &CommonCircuitData<F, D>,
) -> Vec<PolynomialValues<F>> {
let degree = common_data.degree();
let num_lu_slots = LookupGate::num_slots(&common_data.config);
let max_lookup_degree = common_data.lookup_accumulator_degree();
let num_partial_lookups = num_lu_slots.div_ceil(max_lookup_degree);
let num_lut_slots = LookupTableGate::num_slots(&common_data.config);
let max_lookup_table_degree = num_lut_slots.div_ceil(num_partial_lookups);
let mut final_poly_vecs = Vec::with_capacity(num_partial_lookups + 1);
for _ in 0..num_partial_lookups + 1 {
final_poly_vecs.push(PolynomialValues::<F>::new(vec![F::ZERO; degree]));
}
for LookupWire {
last_lu_gate: last_lu_row,
last_lut_gate: last_lut_row,
first_lut_gate: first_lut_row,
} in prover_data.lookup_rows.clone()
{
for row in (last_lut_row..(first_lut_row + 1)).rev() {
let looked_combos: Vec<F> = (0..num_lut_slots)
.map(|s| {
let looked_inp = witness.get_wire(row, LookupTableGate::wire_ith_looked_inp(s));
let looked_out = witness.get_wire(row, LookupTableGate::wire_ith_looked_out(s));
looked_inp + deltas[LookupChallenges::ChallengeA as usize] * looked_out
})
.collect();
let minus_looked_combos: Vec<F> = (0..num_lut_slots)
.map(|s| deltas[LookupChallenges::ChallengeAlpha as usize] - looked_combos[s])
.collect();
let looked_combo_inverses = F::batch_multiplicative_inverse(&minus_looked_combos);
let lookup_combos: Vec<F> = (0..num_lut_slots)
.map(|s| {
let looked_inp = witness.get_wire(row, LookupTableGate::wire_ith_looked_inp(s));
let looked_out = witness.get_wire(row, LookupTableGate::wire_ith_looked_out(s));
looked_inp + deltas[LookupChallenges::ChallengeB as usize] * looked_out
})
.collect();
let mut new_re = final_poly_vecs[0].values[row + 1];
for elt in &lookup_combos {
new_re = new_re * deltas[LookupChallenges::ChallengeDelta as usize] + *elt
}
final_poly_vecs[0].values[row] = new_re;
for slot in 0..num_partial_lookups {
let prev = if slot != 0 {
final_poly_vecs[slot].values[row]
} else {
final_poly_vecs[num_partial_lookups].values[row + 1]
};
let sum = (slot * max_lookup_table_degree
..min((slot + 1) * max_lookup_table_degree, num_lut_slots))
.fold(prev, |acc, s| {
acc + witness.get_wire(row, LookupTableGate::wire_ith_multiplicity(s))
* looked_combo_inverses[s]
});
final_poly_vecs[slot + 1].values[row] = sum;
}
}
for row in (last_lu_row..last_lut_row).rev() {
let looking_combos: Vec<F> = (0..num_lu_slots)
.map(|s| {
let looking_in = witness.get_wire(row, LookupGate::wire_ith_looking_inp(s));
let looking_out = witness.get_wire(row, LookupGate::wire_ith_looking_out(s));
looking_in + deltas[LookupChallenges::ChallengeA as usize] * looking_out
})
.collect();
let minus_looking_combos: Vec<F> = (0..num_lu_slots)
.map(|s| deltas[LookupChallenges::ChallengeAlpha as usize] - looking_combos[s])
.collect();
let looking_combo_inverses = F::batch_multiplicative_inverse(&minus_looking_combos);
for slot in 0..num_partial_lookups {
let prev = if slot == 0 {
final_poly_vecs[num_partial_lookups].values[row + 1]
} else {
final_poly_vecs[slot].values[row]
};
let sum = (slot * max_lookup_degree
..min((slot + 1) * max_lookup_degree, num_lu_slots))
.fold(F::ZERO, |acc, s| acc + looking_combo_inverses[s]);
final_poly_vecs[slot + 1].values[row] = prev - sum;
}
}
}
final_poly_vecs
}
fn compute_all_lookup_polys<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
witness: &MatrixWitness<F>,
deltas: &[F],
prover_data: &ProverOnlyCircuitData<F, C, D>,
common_data: &CommonCircuitData<F, D>,
lookup: bool,
) -> Vec<PolynomialValues<F>> {
if lookup {
let polys: Vec<Vec<PolynomialValues<F>>> = (0..common_data.config.num_challenges)
.map(|c| {
compute_lookup_polys(
witness,
&deltas[c * NUM_COINS_LOOKUP..(c + 1) * NUM_COINS_LOOKUP]
.try_into()
.unwrap(),
prover_data,
common_data,
)
})
.collect();
polys.concat()
} else {
vec![]
}
}
const BATCH_SIZE: usize = 32;
fn compute_quotient_polys<
'a,
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
common_data: &CommonCircuitData<F, D>,
prover_data: &'a ProverOnlyCircuitData<F, C, D>,
public_inputs_hash: &<<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
wires_commitment: &'a LogicalPolynomialBatch<F, C, D>,
zs_partial_products_and_lookup_commitment: &'a LogicalPolynomialBatch<F, C, D>,
betas: &[F],
gammas: &[F],
deltas: &[F],
alphas: &[F],
) -> Vec<PolynomialCoeffs<F>> {
let num_challenges = common_data.config.num_challenges;
let has_lookup = common_data.num_lookup_polys != 0;
let quotient_degree_bits = log2_ceil(common_data.quotient_degree_factor);
assert!(
quotient_degree_bits <= common_data.config.fri_config.rate_bits,
"Having constraints of degree higher than the rate is not supported yet. \
If we need this in the future, we can precompute the larger LDE before computing the `PolynomialBatch`s."
);
let step = 1
<< (common_data.public_initial_degree_bits() - common_data.degree_bits()
+ common_data.config.fri_config.rate_bits
- quotient_degree_bits);
let next_step = 1 << quotient_degree_bits;
let points = F::two_adic_subgroup(common_data.degree_bits() + quotient_degree_bits);
let lde_size = points.len();
let z_h_on_coset = ZeroPolyOnCoset::new(common_data.degree_bits(), quotient_degree_bits);
let lut_re_poly_evals: Vec<Vec<F>> = if has_lookup {
let num_lut_slots = LookupTableGate::num_slots(&common_data.config);
(0..num_challenges)
.map(move |i| {
let cur_deltas = &deltas[NUM_COINS_LOOKUP * i..NUM_COINS_LOOKUP * (i + 1)];
let cur_challenge_delta = cur_deltas[LookupChallenges::ChallengeDelta as usize];
(LookupSelectors::StartEnd as usize..common_data.num_lookup_selectors)
.map(|r| {
let lut_row_number = common_data.luts
[r - LookupSelectors::StartEnd as usize]
.len()
.div_ceil(num_lut_slots);
get_lut_poly(
common_data,
r - LookupSelectors::StartEnd as usize,
cur_deltas,
num_lut_slots * lut_row_number,
)
.eval(cur_challenge_delta)
})
.collect()
})
.collect()
} else {
vec![]
};
let lut_re_poly_evals_refs: Vec<&[F]> =
lut_re_poly_evals.iter().map(|v| v.as_slice()).collect();
let points_batches = points.par_chunks(BATCH_SIZE);
let num_batches = points.len().div_ceil(BATCH_SIZE);
let quotient_values: Vec<Vec<F>> = points_batches
.enumerate()
.flat_map(|(batch_i, xs_batch)| {
debug_assert!(
xs_batch.len() == BATCH_SIZE
|| (batch_i == num_batches - 1 && xs_batch.len() <= BATCH_SIZE)
);
let indices_batch: Vec<usize> =
(BATCH_SIZE * batch_i..BATCH_SIZE * batch_i + xs_batch.len()).collect();
let mut shifted_xs_batch = Vec::with_capacity(xs_batch.len());
let mut local_zs_batch = Vec::<Vec<F>>::with_capacity(xs_batch.len());
let mut next_zs_batch = Vec::<Vec<F>>::with_capacity(xs_batch.len());
let mut local_lookup_batch = Vec::<Vec<F>>::with_capacity(xs_batch.len());
let mut next_lookup_batch = Vec::<Vec<F>>::with_capacity(xs_batch.len());
let mut partial_products_batch = Vec::<Vec<F>>::with_capacity(xs_batch.len());
let mut s_sigmas_batch = Vec::<Vec<F>>::with_capacity(xs_batch.len());
let mut local_constants_batch_refs = Vec::with_capacity(xs_batch.len());
let mut local_wires_batch_refs = Vec::with_capacity(xs_batch.len());
for (&i, &x) in indices_batch.iter().zip(xs_batch) {
let shifted_x = F::coset_shift() * x;
let i_next = (i + next_step) % lde_size;
let shifted_x_next = F::coset_shift() * points[i_next];
let local_constants_sigmas = prover_data
.constants_sigmas_commitment
.get_lde_values(i, step);
let local_constants = &local_constants_sigmas[common_data.constants_range()];
let s_sigmas = &local_constants_sigmas[common_data.sigmas_range()];
let local_wires = wires_commitment.get_lde_values(i, step, shifted_x);
let local_zs_partial_and_lookup =
zs_partial_products_and_lookup_commitment.get_lde_values(i, step, shifted_x);
let next_zs_partial_and_lookup = zs_partial_products_and_lookup_commitment
.get_lde_values(i_next, step, shifted_x_next);
let local_zs = &local_zs_partial_and_lookup[common_data.zs_range()];
let next_zs = &next_zs_partial_and_lookup[common_data.zs_range()];
let partial_products =
&local_zs_partial_and_lookup[common_data.partial_products_range()];
if has_lookup {
let local_lookup_zs = &local_zs_partial_and_lookup[common_data.lookup_range()];
let next_lookup_zs = &next_zs_partial_and_lookup[common_data.lookup_range()];
debug_assert_eq!(local_lookup_zs.len(), common_data.num_all_lookup_polys());
local_lookup_batch.push(local_lookup_zs.to_vec());
next_lookup_batch.push(next_lookup_zs.to_vec());
}
debug_assert_eq!(local_wires.len(), common_data.config.num_wires);
debug_assert_eq!(local_zs.len(), num_challenges);
local_constants_batch_refs.push(local_constants);
local_wires_batch_refs.push(local_wires);
shifted_xs_batch.push(shifted_x);
local_zs_batch.push(local_zs.to_vec());
next_zs_batch.push(next_zs.to_vec());
partial_products_batch.push(partial_products.to_vec());
s_sigmas_batch.push(s_sigmas.to_vec());
}
let mut local_constants_batch =
vec![F::ZERO; xs_batch.len() * local_constants_batch_refs[0].len()];
for i in 0..local_constants_batch_refs[0].len() {
for (j, constants) in local_constants_batch_refs.iter().enumerate() {
local_constants_batch[i * xs_batch.len() + j] = constants[i];
}
}
let mut local_wires_batch =
vec![F::ZERO; xs_batch.len() * local_wires_batch_refs[0].len()];
for i in 0..local_wires_batch_refs[0].len() {
for (j, wires) in local_wires_batch_refs.iter().enumerate() {
local_wires_batch[i * xs_batch.len() + j] = wires[i];
}
}
let vars_batch = EvaluationVarsBaseBatch::new(
xs_batch.len(),
&local_constants_batch,
&local_wires_batch,
public_inputs_hash,
);
let local_zs_batch_refs = local_zs_batch.iter().map(Vec::as_slice).collect::<Vec<_>>();
let next_zs_batch_refs = next_zs_batch.iter().map(Vec::as_slice).collect::<Vec<_>>();
let local_lookup_batch_refs = local_lookup_batch
.iter()
.map(Vec::as_slice)
.collect::<Vec<_>>();
let next_lookup_batch_refs = next_lookup_batch
.iter()
.map(Vec::as_slice)
.collect::<Vec<_>>();
let partial_products_batch_refs = partial_products_batch
.iter()
.map(Vec::as_slice)
.collect::<Vec<_>>();
let s_sigmas_batch_refs = s_sigmas_batch.iter().map(Vec::as_slice).collect::<Vec<_>>();
let mut quotient_values_batch = eval_vanishing_poly_base_batch::<F, D>(
common_data,
&indices_batch,
&shifted_xs_batch,
vars_batch,
&local_zs_batch_refs,
&next_zs_batch_refs,
&local_lookup_batch_refs,
&next_lookup_batch_refs,
&partial_products_batch_refs,
&s_sigmas_batch_refs,
betas,
gammas,
deltas,
alphas,
&z_h_on_coset,
&lut_re_poly_evals_refs,
);
for (&i, quotient_values) in indices_batch.iter().zip(quotient_values_batch.iter_mut())
{
let denominator_inv = z_h_on_coset.eval_inverse(i);
quotient_values
.iter_mut()
.for_each(|v| *v *= denominator_inv);
}
quotient_values_batch
})
.collect();
transpose("ient_values)
.into_par_iter()
.map(PolynomialValues::new)
.map(|values| values.coset_ifft(F::coset_shift()))
.collect()
}