use alloc::{collections::BTreeMap, vec::Vec};
use crypto::{RandomCoin, RandomCoinError};
use math::{fft, ExtensibleField, ExtensionOf, FieldElement, StarkField, ToElements};
use crate::{BatchingMethod, ProofOptions};
mod aux;
pub use aux::AuxRandElements;
mod trace_info;
pub use trace_info::TraceInfo;
mod context;
pub use context::AirContext;
mod assertions;
pub use assertions::Assertion;
mod boundary;
pub use boundary::{BoundaryConstraint, BoundaryConstraintGroup, BoundaryConstraints};
mod transition;
pub use transition::{EvaluationFrame, TransitionConstraintDegree, TransitionConstraints};
mod coefficients;
pub use coefficients::{ConstraintCompositionCoefficients, DeepCompositionCoefficients};
mod divisor;
pub use divisor::ConstraintDivisor;
#[cfg(test)]
mod tests;
const MIN_CYCLE_LENGTH: usize = 2;
pub trait Air: Send + Sync {
type BaseField: StarkField + ExtensibleField<2> + ExtensibleField<3>;
type PublicInputs: ToElements<Self::BaseField> + Send;
fn new(trace_info: TraceInfo, pub_inputs: Self::PublicInputs, options: ProofOptions) -> Self;
fn context(&self) -> &AirContext<Self::BaseField>;
fn evaluate_transition<E: FieldElement<BaseField = Self::BaseField>>(
&self,
frame: &EvaluationFrame<E>,
periodic_values: &[E],
result: &mut [E],
);
fn get_assertions(&self) -> Vec<Assertion<Self::BaseField>>;
#[allow(unused_variables)]
fn evaluate_aux_transition<F, E>(
&self,
main_frame: &EvaluationFrame<F>,
aux_frame: &EvaluationFrame<E>,
periodic_values: &[F],
aux_rand_elements: &AuxRandElements<E>,
result: &mut [E],
) where
F: FieldElement<BaseField = Self::BaseField>,
E: FieldElement<BaseField = Self::BaseField> + ExtensionOf<F>,
{
unimplemented!("evaluation of auxiliary transition constraints has not been implemented");
}
#[allow(unused_variables)]
fn get_aux_assertions<E: FieldElement<BaseField = Self::BaseField>>(
&self,
aux_rand_elements: &AuxRandElements<E>,
) -> Vec<Assertion<E>> {
Vec::new()
}
fn get_aux_rand_elements<E, R>(
&self,
public_coin: &mut R,
) -> Result<AuxRandElements<E>, RandomCoinError>
where
E: FieldElement<BaseField = Self::BaseField>,
R: RandomCoin<BaseField = Self::BaseField>,
{
let num_elements = self.trace_info().get_num_aux_segment_rand_elements();
let mut rand_elements = Vec::with_capacity(num_elements);
for _ in 0..num_elements {
rand_elements.push(public_coin.draw()?);
}
Ok(AuxRandElements::new(rand_elements))
}
fn get_periodic_column_values(&self) -> Vec<Vec<Self::BaseField>> {
Vec::new()
}
fn get_periodic_column_polys(&self) -> Vec<Vec<Self::BaseField>> {
let mut twiddle_map = BTreeMap::new();
self.get_periodic_column_values()
.into_iter()
.map(|mut column| {
let cycle_length = column.len();
assert!(
cycle_length >= MIN_CYCLE_LENGTH,
"number of values in a periodic column must be at least {MIN_CYCLE_LENGTH}, but was {cycle_length}"
);
assert!(
cycle_length.is_power_of_two(),
"number of values in a periodic column must be a power of two, but was {cycle_length}"
);
assert!(cycle_length <= self.trace_length(),
"number of values in a periodic column cannot exceed trace length {}, but was {}",
self.trace_length(),
cycle_length
);
let inv_twiddles = twiddle_map
.entry(cycle_length)
.or_insert_with(|| fft::get_inv_twiddles::<Self::BaseField>(cycle_length));
fft::interpolate_poly(&mut column, inv_twiddles);
column
})
.collect()
}
fn get_transition_constraints<E: FieldElement<BaseField = Self::BaseField>>(
&self,
composition_coefficients: &[E],
) -> TransitionConstraints<E> {
TransitionConstraints::new(self.context(), composition_coefficients)
}
fn get_boundary_constraints<E: FieldElement<BaseField = Self::BaseField>>(
&self,
aux_rand_elements: Option<&AuxRandElements<E>>,
composition_coefficients: &[E],
) -> BoundaryConstraints<E> {
BoundaryConstraints::new(
self.context(),
self.get_assertions(),
aux_rand_elements
.map(|aux_rand_elements| self.get_aux_assertions(aux_rand_elements))
.unwrap_or_default(),
composition_coefficients,
)
}
fn options(&self) -> &ProofOptions {
&self.context().options
}
fn trace_info(&self) -> &TraceInfo {
&self.context().trace_info
}
fn trace_length(&self) -> usize {
self.context().trace_info.length()
}
fn trace_poly_degree(&self) -> usize {
self.context().trace_poly_degree()
}
fn trace_domain_generator(&self) -> Self::BaseField {
self.context().trace_domain_generator
}
fn ce_blowup_factor(&self) -> usize {
self.context().ce_blowup_factor
}
fn ce_domain_size(&self) -> usize {
self.context().ce_domain_size()
}
fn lde_blowup_factor(&self) -> usize {
self.context().options.blowup_factor()
}
fn lde_domain_size(&self) -> usize {
self.context().lde_domain_size()
}
fn lde_domain_generator(&self) -> Self::BaseField {
self.context().lde_domain_generator
}
fn domain_offset(&self) -> Self::BaseField {
self.context().options.domain_offset()
}
fn get_constraint_composition_coefficients<E, R>(
&self,
public_coin: &mut R,
) -> Result<ConstraintCompositionCoefficients<E>, RandomCoinError>
where
E: FieldElement<BaseField = Self::BaseField>,
R: RandomCoin<BaseField = Self::BaseField>,
{
match self.context().options.constraint_batching_method() {
BatchingMethod::Linear => ConstraintCompositionCoefficients::draw_linear(
public_coin,
self.context().num_transition_constraints(),
self.context().num_assertions(),
),
BatchingMethod::Algebraic => ConstraintCompositionCoefficients::draw_algebraic(
public_coin,
self.context().num_transition_constraints(),
self.context().num_assertions(),
),
BatchingMethod::Horner => ConstraintCompositionCoefficients::draw_horner(
public_coin,
self.context().num_transition_constraints(),
self.context().num_assertions(),
),
}
}
fn get_deep_composition_coefficients<E, R>(
&self,
public_coin: &mut R,
) -> Result<DeepCompositionCoefficients<E>, RandomCoinError>
where
E: FieldElement<BaseField = Self::BaseField>,
R: RandomCoin<BaseField = Self::BaseField>,
{
match self.context().options.deep_poly_batching_method() {
BatchingMethod::Linear => DeepCompositionCoefficients::draw_linear(
public_coin,
self.trace_info().width(),
self.context().num_constraint_composition_columns(),
),
BatchingMethod::Algebraic => DeepCompositionCoefficients::draw_algebraic(
public_coin,
self.trace_info().width(),
self.context().num_constraint_composition_columns(),
),
BatchingMethod::Horner => DeepCompositionCoefficients::draw_horner(
public_coin,
self.trace_info().width(),
self.context().num_constraint_composition_columns(),
),
}
}
}