use alloc::vec::Vec;
use p3_challenger::fs::TranscriptField;
use p3_challenger::{CanObserve, CanSampleUniformBits, FieldChallenger, GrindingChallenger};
use p3_commit::{Encoder, Mmcs};
use p3_field::{ExtensionField, Field};
use p3_matrix::dense::{DenseMatrix, RowMajorMatrix};
use p3_multilinear_util::point::Point;
use p3_sumcheck::SumcheckData;
use p3_sumcheck::layout::{Layout, SuffixResidualProver, Table, Witness};
use crate::PcsLayout;
use crate::fold::{ChallengeField, FoldAlphabet, fold_codeword_batch};
use crate::params::BinaryPcsConfig;
use crate::proof::RoundProof;
use crate::transcript::BinaryPcsProverTranscript;
use crate::verifier::flat_coset_indices;
const FOLDING: usize = 0;
const MIN_ENCODED_ARITY: usize = 4;
#[derive(Clone)]
pub struct BinaryPcsProverData<F: Field, EF: ExtensionField<F>, MT: Mmcs<F>> {
pub(crate) layout: PcsLayout<F, EF>,
pub(crate) merkle_data: MT::ProverData<DenseMatrix<F>>,
}
impl<F, EF, MT> BinaryPcsProverData<F, EF, MT>
where
F: TranscriptField,
EF: ExtensionField<F>,
MT: Mmcs<F>,
{
pub fn table(&self, id: usize) -> &Table<F> {
self.layout.table(id)
}
}
pub(crate) struct RoundCommitment<EF: Field, MX: Mmcs<EF>> {
pub commitment: MX::Commitment,
pub merkle_data: MX::ProverData<DenseMatrix<EF>>,
}
#[tracing::instrument(name = "binary pcs commit", skip_all)]
pub(crate) fn commit<F, EF, E, MT>(
config: &BinaryPcsConfig,
encoder: &E,
mmcs: &MT,
witness: Witness<F>,
) -> (MT::Commitment, BinaryPcsProverData<F, EF, MT>)
where
F: TranscriptField,
EF: ExtensionField<F>,
E: Encoder<F>,
MT: Mmcs<F>,
{
assert_eq!(
witness.num_variables(),
config.num_variables(),
"witness arity must match the config it is committed against"
);
let (layout, commitment, merkle_data) =
PcsLayout::<F, EF>::commit(encoder, mmcs, witness, FOLDING, config.log_inv_rate());
(
commitment,
BinaryPcsProverData {
layout,
merkle_data,
},
)
}
#[allow(clippy::type_complexity)]
#[tracing::instrument(name = "binary pcs fold rounds", skip_all)]
pub(crate) fn fold_rounds_with<const BIND_EACH_ROUND: bool, F, EF, MT, MX, Ch>(
prover_data: BinaryPcsProverData<F, EF, MT>,
config: &BinaryPcsConfig,
mmcs: &MT,
round_mmcs: &MX,
transcript: &mut BinaryPcsProverTranscript<'_, F, EF, Ch>,
) -> (
MT::ProverData<DenseMatrix<F>>,
SumcheckData<F, EF>,
Vec<RoundCommitment<EF, MX>>,
Point<EF>,
Vec<EF>,
)
where
F: TranscriptField + FoldAlphabet<EF>,
EF: ChallengeField<F> + ExtensionField<F> + FoldAlphabet<EF>,
MT: Mmcs<F>,
MX: Mmcs<EF>,
Ch: FieldChallenger<F>
+ GrindingChallenger<Witness = F>
+ CanSampleUniformBits<F>
+ CanObserve<MX::Commitment>,
{
let BinaryPcsProverData {
layout,
merkle_data,
} = prover_data;
let mut sumcheck_data = SumcheckData::default();
let (mut sumcheck, mut randomness) = transcript.fold_batch(|challenger| {
layout.into_sumcheck_in::<<EF as ChallengeField<F>>::SumcheckRepr, _>(
&mut sumcheck_data,
0,
challenger,
)
});
assert_eq!(
randomness.num_variables(),
0,
"the commit phase runs at zero preprocessing depth, so the sumcheck consumes no head rounds"
);
assert_eq!(
mmcs.get_matrices(&merkle_data)[0].width,
1,
"zero preprocessing depth commits a width-1 codeword"
);
let num_batches = config.num_fold_batches();
let mut rounds: Vec<RoundCommitment<EF, MX>> = Vec::with_capacity(num_batches - 1);
let mut final_codeword = Vec::new();
for (batch, (start, arity)) in config.fold_batches().enumerate() {
let _batch_span = tracing::info_span!("fold batch", batch, start, arity).entered();
let mut challenges = Vec::with_capacity(arity);
for round in start..start + arity {
let challenge = tracing::debug_span!("sumcheck round", round).in_scope(|| {
transcript.fold_batch(|challenger| {
sumcheck.compute_sumcheck_polynomials(&mut sumcheck_data, challenger, 1, 0)
})
});
challenges.push(challenge.as_slice()[0]);
randomness.extend(&challenge);
if BIND_EACH_ROUND {
sumcheck.settle();
}
}
let folded = tracing::info_span!("fold codeword").in_scope(|| {
let encoded = (arity >= MIN_ENCODED_ARITY)
.then(|| encode_bound_message(&mut sumcheck, config.log_inv_rate()))
.flatten();
encoded.unwrap_or_else(|| {
if batch == 0 {
fold_codeword_batch(&mmcs.get_matrices(&merkle_data)[0].values, &challenges)
} else {
let source = &rounds[batch - 1].merkle_data;
fold_codeword_batch(&round_mmcs.get_matrices(source)[0].values, &challenges)
}
})
});
if batch + 1 < num_batches {
let (commitment, round_data) = tracing::info_span!("commit folded codeword")
.in_scope(|| round_mmcs.commit_matrix(RowMajorMatrix::new(folded, 1)));
transcript.oracle_commitment(commitment.clone());
rounds.push(RoundCommitment {
commitment,
merkle_data: round_data,
});
} else {
final_codeword = folded;
}
}
#[cfg(debug_assertions)]
sumcheck.settle();
(
merkle_data,
sumcheck_data,
rounds,
randomness,
final_codeword,
)
}
fn encode_bound_message<F, EF>(
sumcheck: &mut SuffixResidualProver<F, EF, <EF as ChallengeField<F>>::SumcheckRepr>,
log_inv_rate: usize,
) -> Option<Vec<EF>>
where
F: Field,
EF: ChallengeField<F> + ExtensionField<F>,
{
let column = sumcheck.bound_column()?;
let mut message = EF::zero_vec(column.len() << log_inv_rate);
EF::from_sumcheck_repr(column, &mut message[..column.len()]);
let codeword = tracing::info_span!("encode bound message").in_scope(|| {
EF::Encoder::default().encode_batch_padded(RowMajorMatrix::new(message, 1), log_inv_rate)
});
Some(codeword.values)
}
pub(crate) struct QueryProofs<F: Field, EF: Field, MT: Mmcs<F>, MX: Mmcs<EF>> {
pub base_opened_values: Vec<Vec<F>>,
pub base_multi_proof: MT::MultiProof,
pub rounds: Vec<RoundProof<EF, MX>>,
pub pow_witness: F,
}
#[tracing::instrument(name = "binary pcs open queries", skip_all)]
pub(crate) fn open_queries<F, EF, MT, MX, Ch>(
config: &BinaryPcsConfig,
mmcs: &MT,
round_mmcs: &MX,
transcript: &mut BinaryPcsProverTranscript<'_, F, EF, Ch>,
base_merkle_data: &MT::ProverData<DenseMatrix<F>>,
rounds: &[RoundCommitment<EF, MX>],
final_codeword: &[EF],
) -> QueryProofs<F, EF, MT, MX>
where
F: TranscriptField,
EF: ExtensionField<F>,
MT: Mmcs<F>,
MX: Mmcs<EF>,
Ch: FieldChallenger<F> + GrindingChallenger<Witness = F> + CanSampleUniformBits<F>,
{
assert_eq!(
rounds.len(),
config.num_fold_batches() - 1,
"rounds is the caller's own fold-phase output, never proof-supplied data"
);
transcript.final_codeword(final_codeword);
let pow_witness = transcript.query_pow();
let shift = config.log_folding_factor() - 1;
let indices: Vec<usize> = transcript
.query_pairs()
.into_iter()
.map(|pair| pair << shift)
.collect();
let base_indices = flat_coset_indices(&indices, 0, config.log_folding_factor());
let (base_values, base_multi_proof) = mmcs.open_multi_batch(&base_indices, base_merkle_data);
let base_opened_values = single_matrix_rows(base_values);
let opened_rounds = rounds
.iter()
.zip(config.fold_batches().skip(1))
.map(|(round, (start, arity))| {
let round_indices = flat_coset_indices(&indices, start, arity);
let (values, multi_proof) =
round_mmcs.open_multi_batch(&round_indices, &round.merkle_data);
RoundProof {
commitment: round.commitment.clone(),
opened_values: single_matrix_rows(values),
multi_proof,
}
})
.collect();
QueryProofs {
base_opened_values,
base_multi_proof,
rounds: opened_rounds,
pow_witness,
}
}
fn single_matrix_rows<T>(values: Vec<Vec<Vec<T>>>) -> Vec<Vec<T>> {
values
.into_iter()
.map(|mut per_matrix| {
assert_eq!(per_matrix.len(), 1, "each round commits exactly one matrix");
per_matrix.swap_remove(0)
})
.collect()
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use alloc::{format, vec};
use p3_binary_dft::{AdditiveRsEncoder, EncodableLevel, NaiveAdditiveNtt};
use p3_binary_field::{BinaryField8, BinaryField64, BinaryField128};
use p3_challenger::fs::TranscriptField;
use p3_challenger::{CanObserve, CanSampleUniformBits, FieldChallenger, GrindingChallenger};
use p3_commit::Mmcs;
use p3_field::ExtensionField;
use p3_matrix::dense::RowMajorMatrix;
use p3_multilinear_util::point::Point;
use p3_multilinear_util::poly::Poly;
use p3_sumcheck::layout::{Layout, PrefixProver, SuffixProver, Table};
use p3_sumcheck::{OpeningBatch, SumcheckData};
use rand::SeedableRng;
use rand::distr::{Distribution, StandardUniform};
use rand::rngs::SmallRng;
use super::{MIN_ENCODED_ARITY, commit, fold_rounds_with};
use crate::fold::{ChallengeField, FoldAlphabet, fold_codeword, fold_codeword_batch};
use crate::params::{BinaryPcsConfig, BinaryPcsParams};
use crate::test_util::{
LevelChallenger, LevelMmcs, challenger, level_challenger, level_mmcs, mmcs,
narrow_challenger, narrow_mmcs,
};
use crate::transcript::{BinaryPcsProverTranscript, BinaryPcsShape};
type F = BinaryField128;
const NUM_VARIABLES: usize = 8;
const LOG_INV_RATE: usize = 2;
#[test]
fn the_codeword_and_the_sumcheck_stay_in_lockstep() {
let mut rng = SmallRng::seed_from_u64(21);
let table = Table::rand(&mut rng, 1, NUM_VARIABLES);
let witness = SuffixProver::<F, F>::new_witness(vec![table], 0);
let mut ch = challenger();
let (mut layout, _root, data) = SuffixProver::<F, F>::commit(
&AdditiveRsEncoder::<F, NaiveAdditiveNtt<F>>::default(),
&mmcs(),
witness,
0,
LOG_INV_RATE,
);
let m = mmcs().get_matrices(&data)[0].clone();
assert_eq!(m.width, 1);
let mut codeword = m.values;
let _virtual_eval = layout.add_virtual_eval(&mut ch);
let mut sc = SumcheckData::<F, F>::default();
let (mut prover, head) = layout.into_sumcheck(&mut sc, 0, &mut ch);
assert_eq!(head.num_variables(), 0);
for _ in 0..prover.num_variables() {
let beta = prover.compute_sumcheck_polynomials(&mut sc, &mut ch, 1, 0, None);
codeword = fold_codeword(&codeword, beta.as_slice()[0]);
}
let final_value = prover.evals().as_constant().expect("fully folded");
assert_eq!(codeword.len(), 1 << LOG_INV_RATE);
assert!(codeword.iter().all(|&v| v == final_value));
}
#[test]
fn prefix_layout_does_not_stay_in_lockstep() {
let mut rng = SmallRng::seed_from_u64(21);
let table = Table::rand(&mut rng, 1, NUM_VARIABLES);
let witness = PrefixProver::<F, F>::new_witness(vec![table], 0);
let mut ch = challenger();
let (mut layout, _root, data) = PrefixProver::<F, F>::commit(
&AdditiveRsEncoder::<F, NaiveAdditiveNtt<F>>::default(),
&mmcs(),
witness,
0,
LOG_INV_RATE,
);
let m = mmcs().get_matrices(&data)[0].clone();
assert_eq!(m.width, 1);
let mut codeword = m.values;
let _virtual_eval = layout.add_virtual_eval(&mut ch);
let mut sc = SumcheckData::<F, F>::default();
let (mut prover, head) = layout.into_sumcheck(&mut sc, 0, &mut ch);
assert_eq!(head.num_variables(), 0);
for _ in 0..prover.num_variables() {
let beta = prover.compute_sumcheck_polynomials(&mut sc, &mut ch, 1, 0, None);
codeword = fold_codeword(&codeword, beta.as_slice()[0]);
}
let final_value = prover.evals().as_constant().expect("fully folded");
assert!(!codeword.iter().all(|&v| v == final_value));
}
#[test]
fn commit_and_fold_rounds_match_an_independent_message_fold() {
let mut rng = SmallRng::seed_from_u64(0xC0FFEE);
let poly = Poly::<F>::rand(&mut rng, NUM_VARIABLES);
let mut message = poly.clone();
let table = Table::new(RowMajorMatrix::new(poly.into_evals(), 1 << NUM_VARIABLES));
let witness = SuffixProver::<F, F>::new_witness(vec![table], 0);
let params = BinaryPcsParams {
log_inv_rate: LOG_INV_RATE,
pow_bits: 4,
security_level: 40,
};
let config = BinaryPcsConfig::try_new::<F, F>(NUM_VARIABLES, params).unwrap();
let encoder = AdditiveRsEncoder::<F, NaiveAdditiveNtt<F>>::default();
let mmcs_instance = mmcs();
let mut ch = challenger();
let (_commitment, prover_data) =
commit::<F, F, _, _>(&config, &encoder, &mmcs_instance, witness);
let mut transcript = BinaryPcsProverTranscript::new(&mut ch, BinaryPcsShape::new(&config));
let (_merkle_data, _sumcheck_data, rounds, randomness, final_codeword) =
fold_rounds_with::<false, F, F, _, _, _>(
prover_data,
&config,
&mmcs_instance,
&mmcs_instance,
&mut transcript,
);
transcript.abort();
assert_eq!(rounds.len(), config.num_fold_rounds() - 1);
assert_eq!(randomness.num_variables(), NUM_VARIABLES);
assert_eq!(final_codeword.len(), 1 << LOG_INV_RATE);
for &beta in randomness.as_slice() {
message.fix_suffix_var_mut(beta);
}
let expected = message.as_constant().expect("fully folded");
assert!(final_codeword.iter().all(|&v| v == expected));
}
#[test]
fn a_narrow_alphabet_folds_to_the_same_constant_as_its_own_message() {
let mut rng = SmallRng::seed_from_u64(0xC0FFEE);
let table = Table::rand(&mut rng, 1, NUM_VARIABLES);
let embedded = Poly::new(
table
.poly(0)
.as_slice()
.iter()
.map(|&value| F::from(value))
.collect::<alloc::vec::Vec<_>>(),
);
let witness = SuffixProver::<BinaryField64, F>::new_witness(vec![table], 0);
let params = BinaryPcsParams {
log_inv_rate: LOG_INV_RATE,
pow_bits: 4,
security_level: 40,
};
let config = BinaryPcsConfig::try_new::<BinaryField64, F>(NUM_VARIABLES, params).unwrap();
let encoder = <BinaryField64 as EncodableLevel>::Encoder::default();
let mut ch = narrow_challenger();
let (_commitment, prover_data) =
commit::<BinaryField64, F, _, _>(&config, &encoder, &narrow_mmcs(), witness);
let mut transcript = BinaryPcsProverTranscript::new(&mut ch, BinaryPcsShape::new(&config));
let (_merkle_data, _sumcheck_data, _rounds, randomness, final_codeword) =
fold_rounds_with::<false, BinaryField64, F, _, _, _>(
prover_data,
&config,
&narrow_mmcs(),
&mmcs(),
&mut transcript,
);
transcript.abort();
let mut message = embedded;
for &beta in randomness.as_slice() {
message.fix_suffix_var_mut(beta);
}
let expected = message.as_constant().expect("fully folded");
assert_eq!(final_codeword.len(), 1 << LOG_INV_RATE);
assert!(final_codeword.iter().all(|&v| v == expected));
}
#[test]
fn fold_rounds_agree_with_binding_each_round() {
for (num_variables, log_folding_factor) in [(8usize, 1usize), (8, 3), (15, 1), (15, 3)] {
let params = BinaryPcsParams {
log_inv_rate: LOG_INV_RATE,
pow_bits: 4,
security_level: 40,
};
let config = BinaryPcsConfig::try_new_with_folding::<F, F>(
num_variables,
params,
log_folding_factor,
)
.unwrap();
let encoder = AdditiveRsEncoder::<F>::default();
let mmcs_instance = mmcs();
let mut rng = SmallRng::seed_from_u64(0xF0FA + num_variables as u64);
let table = Table::rand(&mut rng, 1, num_variables);
let shape = format!("{num_variables} variables, arity {log_folding_factor}");
let mut got_ch = challenger();
let (_commitment, got_data) = commit::<F, F, _, _>(
&config,
&encoder,
&mmcs_instance,
SuffixProver::<F, F>::new_witness(vec![table.clone()], 0),
);
let mut got_t =
BinaryPcsProverTranscript::new(&mut got_ch, BinaryPcsShape::new(&config));
let (_, got_sumcheck, got_rounds, got_randomness, got_final) =
fold_rounds_with::<false, F, F, _, _, _>(
got_data,
&config,
&mmcs_instance,
&mmcs_instance,
&mut got_t,
);
got_t.abort();
drop(got_t);
let mut want_ch = challenger();
let (_commitment, want_data) = commit::<F, F, _, _>(
&config,
&encoder,
&mmcs_instance,
SuffixProver::<F, F>::new_witness(vec![table], 0),
);
let mut want_t =
BinaryPcsProverTranscript::new(&mut want_ch, BinaryPcsShape::new(&config));
let (_, want_sumcheck, want_rounds, want_randomness, want_final) =
fold_rounds_with::<true, F, F, _, _, _>(
want_data,
&config,
&mmcs_instance,
&mmcs_instance,
&mut want_t,
);
want_t.abort();
drop(want_t);
assert_eq!(
got_sumcheck.num_rounds(),
want_sumcheck.num_rounds(),
"{shape}: round count"
);
for (round, (got_msg, want_msg)) in got_sumcheck
.polynomial_evaluations()
.iter()
.zip(want_sumcheck.polynomial_evaluations())
.enumerate()
{
assert_eq!(got_msg, want_msg, "{shape}: round {round} message");
}
assert_eq!(
got_randomness.as_slice(),
want_randomness.as_slice(),
"{shape}: folding randomness"
);
assert_eq!(got_final, want_final, "{shape}: final codeword");
for (round, (got_round, want_round)) in got_rounds.iter().zip(&want_rounds).enumerate()
{
assert_eq!(
got_round.commitment, want_round.commitment,
"{shape}: round {round} commitment"
);
}
assert_eq!(
got_ch.sample_algebra_element::<F>(),
want_ch.sample_algebra_element::<F>(),
"{shape}: transcript state"
);
}
}
fn check_fold_phase_codewords<const BIND_EACH_ROUND: bool, A, EF>(
num_variables: usize,
log_folding_factor: usize,
log_inv_rate: usize,
seed: u64,
) where
A: EncodableLevel + TranscriptField + FoldAlphabet<EF>,
EF: ChallengeField<A> + ExtensionField<A> + FoldAlphabet<EF>,
LevelMmcs<A>: Mmcs<A>,
LevelMmcs<EF>: Mmcs<EF>,
LevelChallenger<A>: FieldChallenger<A>
+ GrindingChallenger<Witness = A>
+ CanSampleUniformBits<A>
+ CanObserve<<LevelMmcs<EF> as Mmcs<EF>>::Commitment>
+ Clone,
StandardUniform: Distribution<A> + Distribution<EF>,
{
let params = BinaryPcsParams {
log_inv_rate,
pow_bits: 0,
security_level: 40,
};
let config = BinaryPcsConfig::try_new_with_folding::<A, EF>(
num_variables,
params,
log_folding_factor,
)
.unwrap();
let (base_mmcs, round_mmcs) = (level_mmcs::<A>(), level_mmcs::<EF>());
let shape = format!(
"bits {}/{}, {num_variables} variables, arity {log_folding_factor}, rate {log_inv_rate}",
A::bits(),
EF::bits()
);
let mut rng = SmallRng::seed_from_u64(seed);
let table = Table::<A>::rand(&mut rng, 1, num_variables);
let (_commitment, mut prover_data) = commit::<A, EF, _, _>(
&config,
&A::Encoder::default(),
&base_mmcs,
SuffixProver::<A, EF>::new_witness(vec![table], 0),
);
let point = Point::<EF>::rand(&mut rng, num_variables);
prover_data
.layout
.record_opening(0, &OpeningBatch::new(vec![0], Vec::new()), &point);
let mut probe_ch = level_challenger::<A>();
let mut probe_sc = SumcheckData::default();
let (mut probe, _) = prover_data
.layout
.clone()
.into_sumcheck_in::<EF::SumcheckRepr, _>(&mut probe_sc, 0, &mut probe_ch);
let mut holds_bound_column = false;
for (batch, (_, arity)) in config.fold_batches().enumerate() {
for _ in 0..arity {
let _ = probe.compute_sumcheck_polynomials(&mut probe_sc, &mut probe_ch, 1, 0);
holds_bound_column |= probe.bound_column().is_some();
}
if arity >= MIN_ENCODED_ARITY {
assert!(
probe.bound_column().is_some(),
"{shape}: batch {batch} encodes its codeword"
);
}
}
assert!(holds_bound_column, "{shape}: banked route");
let mut ch = level_challenger::<A>();
let mut transcript = BinaryPcsProverTranscript::new(&mut ch, BinaryPcsShape::new(&config));
let (merkle_data, _sumcheck_data, rounds, randomness, final_codeword) =
fold_rounds_with::<BIND_EACH_ROUND, A, EF, _, _, _>(
prover_data,
&config,
&base_mmcs,
&round_mmcs,
&mut transcript,
);
transcript.abort();
let base = base_mmcs.get_matrices(&merkle_data)[0].values.clone();
let mut expected: Vec<EF> = Vec::new();
for (batch, (start, arity)) in config.fold_batches().enumerate() {
let challenges = &randomness.as_slice()[start..start + arity];
expected = if batch == 0 {
fold_codeword_batch(&base, challenges)
} else {
fold_codeword_batch(&expected, challenges)
};
let got = rounds.get(batch).map_or_else(
|| final_codeword.clone(),
|round| {
round_mmcs.get_matrices(&round.merkle_data)[0]
.values
.clone()
},
);
assert_eq!(got, expected, "{shape}: batch {batch}");
}
}
#[test]
fn every_fold_phase_codeword_is_the_fold_of_the_base_codeword() {
for (seed, log_folding_factor) in (0..).zip(1..=4) {
for (num_variables, log_inv_rate) in [(13, 1), (13, 2)] {
check_fold_phase_codewords::<false, F, F>(
num_variables,
log_folding_factor,
log_inv_rate,
seed,
);
check_fold_phase_codewords::<false, BinaryField64, F>(
num_variables,
log_folding_factor,
log_inv_rate,
seed,
);
check_fold_phase_codewords::<false, BinaryField64, BinaryField64>(
num_variables,
log_folding_factor,
log_inv_rate,
seed,
);
}
check_fold_phase_codewords::<false, BinaryField8, BinaryField64>(
6,
log_folding_factor,
2,
seed,
);
check_fold_phase_codewords::<false, BinaryField8, F>(6, log_folding_factor, 2, seed);
check_fold_phase_codewords::<true, F, F>(13, log_folding_factor, 1, seed);
check_fold_phase_codewords::<true, BinaryField64, BinaryField64>(
13,
log_folding_factor,
1,
seed,
);
}
for (seed, num_variables) in [(4, 12), (5, 16)] {
check_fold_phase_codewords::<false, F, F>(num_variables, 4, 1, seed);
}
check_fold_phase_codewords::<false, F, F>(16, 8, 1, 6);
}
#[test]
#[should_panic(expected = "witness arity must match the config it is committed against")]
fn commit_rejects_a_witness_arity_mismatch() {
let params = BinaryPcsParams {
log_inv_rate: LOG_INV_RATE,
pow_bits: 4,
security_level: 40,
};
let config = BinaryPcsConfig::try_new::<F, F>(NUM_VARIABLES, params).unwrap();
let encoder = AdditiveRsEncoder::<F, NaiveAdditiveNtt<F>>::default();
let mmcs_instance = mmcs();
let mut rng = SmallRng::seed_from_u64(0);
let table = Table::rand(&mut rng, 1, NUM_VARIABLES - 1);
let witness = SuffixProver::<F, F>::new_witness(vec![table], 0);
let _ = commit::<F, F, _, _>(&config, &encoder, &mmcs_instance, witness);
}
}