use alloc::vec;
use alloc::vec::Vec;
use core::marker::PhantomData;
use p3_challenger::fs::{
DomainSeparator, FieldToFieldCodec, FieldUnit, Hierarchy, Interaction, InteractionPattern,
Kind, Length, ProverState, TranscriptField, VerifierState,
};
use p3_challenger::{CanObserve, CanSample};
use p3_field::ExtensionField;
use super::{JaggedLayout, JaggedPoint};
const VERSION: u8 = 1;
const NAME: &[u8] = b"p3-sumcheck-jagged";
const PRODUCT_SUMCHECK: &str = "product_sumcheck";
const DENSE_EVALUATION: &str = "dense_evaluation";
type Alphabet<F> = FieldUnit<F>;
struct ProductSumcheck;
fn pattern<F, EF>() -> InteractionPattern
where
F: TranscriptField,
EF: ExtensionField<F>,
{
let steps = vec![
Interaction::marker::<ProductSumcheck>(Hierarchy::Begin, Kind::Protocol, PRODUCT_SUMCHECK),
Interaction::marker::<ProductSumcheck>(Hierarchy::End, Kind::Protocol, PRODUCT_SUMCHECK),
Interaction::algebra::<F, EF>(
Hierarchy::Atomic,
Kind::Message,
DENSE_EVALUATION,
Length::Scalar,
),
];
InteractionPattern::new(steps).expect("one matched bracket is structurally valid")
}
fn domain_separator<F, EF>(
layout: &JaggedLayout,
point: &JaggedPoint<EF>,
claimed_value: EF,
) -> DomainSeparator<Alphabet<F>>
where
F: TranscriptField,
EF: ExtensionField<F>,
{
let mut separator = DomainSeparator::new(VERSION, NAME, pattern::<F, EF>());
let statement = encode_statement::<F, EF>(layout, point, claimed_value);
separator.instance(&statement);
separator
}
pub(super) struct JaggedProverTranscript<'a, C, F: TranscriptField, EF> {
state: ProverState<&'a mut C, Alphabet<F>>,
_ef: PhantomData<EF>,
}
impl<'a, C, F, EF> JaggedProverTranscript<'a, C, F, EF>
where
C: CanObserve<F> + CanSample<F>,
F: TranscriptField,
EF: ExtensionField<F>,
{
pub(super) fn new(
challenger: &'a mut C,
layout: &JaggedLayout,
point: &JaggedPoint<EF>,
claimed_value: EF,
) -> Self {
let separator = domain_separator::<F, EF>(layout, point, claimed_value);
Self {
state: ProverState::new(challenger, &separator),
_ef: PhantomData,
}
}
pub(super) fn product_sumcheck<R>(&mut self, run: impl FnOnce(&mut C) -> R) -> R {
self.state
.begin_protocol::<ProductSumcheck>(PRODUCT_SUMCHECK);
let output = run(self.state.challenger_mut());
self.state.end_protocol::<ProductSumcheck>(PRODUCT_SUMCHECK);
output
}
pub(super) fn dense_evaluation(&mut self, value: EF) {
self.state
.observe_extension::<F, EF, FieldToFieldCodec<F>>(DENSE_EVALUATION, &value);
}
pub(super) fn finish(self) {
assert!(
self.state.finalize().is_empty(),
"the jagged reduction carries every value in its proof"
);
}
}
pub(super) struct JaggedVerifierTranscript<'a, C, F: TranscriptField, EF> {
state: VerifierState<'static, &'a mut C, Alphabet<F>>,
_ef: PhantomData<EF>,
}
impl<'a, C, F, EF> JaggedVerifierTranscript<'a, C, F, EF>
where
C: CanObserve<F> + CanSample<F>,
F: TranscriptField,
EF: ExtensionField<F>,
{
pub(super) fn new(
challenger: &'a mut C,
layout: &JaggedLayout,
point: &JaggedPoint<EF>,
claimed_value: EF,
) -> Self {
let separator = domain_separator::<F, EF>(layout, point, claimed_value);
Self {
state: VerifierState::new(challenger, &separator, &[]),
_ef: PhantomData,
}
}
pub(super) fn product_sumcheck<R>(&mut self, run: impl FnOnce(&mut C) -> R) -> R {
self.state
.begin_protocol::<ProductSumcheck>(PRODUCT_SUMCHECK);
let output = run(self.state.challenger_mut());
self.state.end_protocol::<ProductSumcheck>(PRODUCT_SUMCHECK);
output
}
pub(super) fn dense_evaluation(&mut self, value: EF) {
self.state
.observe_extension::<F, EF, FieldToFieldCodec<F>>(DENSE_EVALUATION, &value);
}
pub(super) fn abort(&mut self) {
self.state.abort();
}
pub(super) fn finish(self) {
self.state
.finalize()
.expect("the jagged reduction reads an empty wire");
}
}
fn encode_statement<F, EF>(
layout: &JaggedLayout,
point: &JaggedPoint<EF>,
claimed_value: EF,
) -> Vec<u8>
where
F: TranscriptField,
EF: ExtensionField<F>,
{
let mut bytes = encode_layout(layout);
for value in point
.row()
.iter()
.chain(point.column().iter())
.chain(core::iter::once(&claimed_value))
{
for coefficient in value.as_basis_coefficients_slice() {
F::encode(coefficient, &mut bytes);
}
}
bytes
}
pub(super) fn encode_layout(layout: &JaggedLayout) -> Vec<u8> {
let mut bytes = Vec::new();
encode_usize(&mut bytes, layout.row_variables());
encode_usize(&mut bytes, layout.dense_variables());
encode_usize(&mut bytes, layout.cumulative_heights().len());
for &height in layout.cumulative_heights() {
encode_usize(&mut bytes, height);
}
bytes
}
fn encode_usize(bytes: &mut Vec<u8>, value: usize) {
bytes.extend_from_slice(&(value as u64).to_be_bytes());
}