use arbitrary::Arbitrary;
use std::ops::Index;
use std::ops::Range;
use std::ops::RangeInclusive;
use strum::EnumCount;
use twenty_first::prelude::*;
use twenty_first::tip5;
use air::challenge_id::ChallengeId;
use air::cross_table_argument::CrossTableArg;
use air::cross_table_argument::EvalArg;
use crate::prelude::Claim;
#[derive(Debug, Clone, Arbitrary)]
pub struct Challenges {
pub challenges: [XFieldElement; Self::COUNT],
}
impl Challenges {
pub const COUNT: usize = ChallengeId::COUNT;
pub const SAMPLE_COUNT: usize = Self::COUNT - ChallengeId::NUM_DERIVED_CHALLENGES;
pub fn new(mut challenges: Vec<XFieldElement>, claim: &Claim) -> Self {
assert_eq!(Self::SAMPLE_COUNT, challenges.len());
let compressed_digest = EvalArg::compute_terminal(
&claim.program_digest.values(),
EvalArg::default_initial(),
challenges[ChallengeId::CompressProgramDigestIndeterminate.index()],
);
let input_terminal = EvalArg::compute_terminal(
&claim.input,
EvalArg::default_initial(),
challenges[ChallengeId::StandardInputIndeterminate.index()],
);
let output_terminal = EvalArg::compute_terminal(
&claim.output,
EvalArg::default_initial(),
challenges[ChallengeId::StandardOutputIndeterminate.index()],
);
let lookup_terminal = EvalArg::compute_terminal(
&tip5::LOOKUP_TABLE.map(BFieldElement::from),
EvalArg::default_initial(),
challenges[ChallengeId::LookupTablePublicIndeterminate.index()],
);
challenges.insert(ChallengeId::StandardInputTerminal.index(), input_terminal);
challenges.insert(ChallengeId::StandardOutputTerminal.index(), output_terminal);
challenges.insert(
ChallengeId::LookupTablePublicTerminal.index(),
lookup_terminal,
);
challenges.insert(
ChallengeId::CompressedProgramDigest.index(),
compressed_digest,
);
assert_eq!(Self::COUNT, challenges.len());
let challenges = challenges.try_into().unwrap();
Self { challenges }
}
}
impl Index<usize> for Challenges {
type Output = XFieldElement;
fn index(&self, id: usize) -> &Self::Output {
&self.challenges[id]
}
}
impl Index<Range<usize>> for Challenges {
type Output = [XFieldElement];
fn index(&self, indices: Range<usize>) -> &Self::Output {
&self.challenges[indices.start..indices.end]
}
}
impl Index<RangeInclusive<usize>> for Challenges {
type Output = [XFieldElement];
fn index(&self, indices: RangeInclusive<usize>) -> &Self::Output {
&self.challenges[*indices.start()..=*indices.end()]
}
}
impl Index<ChallengeId> for Challenges {
type Output = XFieldElement;
fn index(&self, id: ChallengeId) -> &Self::Output {
&self[id.index()]
}
}
impl Index<Range<ChallengeId>> for Challenges {
type Output = [XFieldElement];
fn index(&self, indices: Range<ChallengeId>) -> &Self::Output {
&self[indices.start.index()..indices.end.index()]
}
}
impl Index<RangeInclusive<ChallengeId>> for Challenges {
type Output = [XFieldElement];
fn index(&self, indices: RangeInclusive<ChallengeId>) -> &Self::Output {
&self[indices.start().index()..=indices.end().index()]
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use crate::prelude::Claim;
use twenty_first::xfe;
impl Default for Challenges {
fn default() -> Self {
Self::placeholder(&Claim::default())
}
}
impl Challenges {
pub fn placeholder(claim: &Claim) -> Self {
let stand_in_challenges = (1..=Self::SAMPLE_COUNT)
.map(|i| xfe!([42, i as u64, 24]))
.collect();
Self::new(stand_in_challenges, claim)
}
}
#[test]
fn various_challenge_indexing_operations_are_possible() {
let challenges = Challenges::default();
let _ = challenges[ChallengeId::StackWeight0];
let _ = challenges[ChallengeId::StackWeight0..ChallengeId::StackWeight8];
let _ = challenges[ChallengeId::StackWeight0..=ChallengeId::StackWeight8];
let _ = challenges[0];
let _ = challenges[0..8];
let _ = challenges[0..=8];
}
}