use alloc::vec::Vec;
use air::{
proof::{QuotientOodFrame, TraceOodFrame},
DeepCompositionCoefficients,
};
use math::{
add_in_place, fft, mul_acc,
polynom::{self},
ExtensionOf, FieldElement, StarkField,
};
use utils::iter_mut;
#[cfg(feature = "concurrent")]
use utils::iterators::*;
use super::{constraints::CompositionPoly, StarkDomain, TracePolyTable};
pub struct DeepCompositionPoly<E: FieldElement> {
coefficients: Vec<E>,
cc: DeepCompositionCoefficients<E>,
z: E,
}
impl<E: FieldElement> DeepCompositionPoly<E> {
pub fn new(z: E, cc: DeepCompositionCoefficients<E>) -> Self {
DeepCompositionPoly { coefficients: vec![], cc, z }
}
pub fn poly_size(&self) -> usize {
self.coefficients.len()
}
pub fn degree(&self) -> usize {
polynom::degree_of(&self.coefficients)
}
pub fn add_trace_polys(
&mut self,
trace_polys: TracePolyTable<E>,
quotient_polys: CompositionPoly<E>,
ood_trace_states: TraceOodFrame<E>,
ood_quotient_states: QuotientOodFrame<E>,
) {
assert!(self.coefficients.is_empty());
let trace_length = trace_polys.poly_size();
let g = E::from(E::BaseField::get_root_of_unity(trace_length.ilog2()));
let next_z = self.z * g;
let mut composition_z = vec![E::ZERO; trace_length];
let mut composition_gz = vec![E::ZERO; trace_length];
let mut i = 0;
for poly in trace_polys.main_trace_polys() {
acc_trace_poly::<E::BaseField, E>(
&mut composition_z,
poly,
ood_trace_states.current_row()[i],
self.cc.trace[i],
);
acc_trace_poly::<E::BaseField, E>(
&mut composition_gz,
poly,
ood_trace_states.next_row()[i],
self.cc.trace[i],
);
i += 1;
}
for poly in trace_polys.aux_trace_polys() {
acc_trace_poly::<E, E>(
&mut composition_z,
poly,
ood_trace_states.current_row()[i],
self.cc.trace[i],
);
acc_trace_poly::<E, E>(
&mut composition_gz,
poly,
ood_trace_states.next_row()[i],
self.cc.trace[i],
);
i += 1;
}
for (i, poly) in quotient_polys.into_columns().iter().enumerate() {
acc_trace_poly::<E, E>(
&mut composition_z,
poly,
ood_quotient_states.current_row()[i],
self.cc.constraints[i],
);
acc_trace_poly::<E, E>(
&mut composition_gz,
poly,
ood_quotient_states.next_row()[i],
self.cc.constraints[i],
);
}
let trace_poly =
merge_compositions(vec![composition_z, composition_gz], vec![self.z, next_z]);
self.coefficients = trace_poly;
assert_eq!(self.poly_size() - 2, self.degree());
}
pub fn evaluate(self, domain: &StarkDomain<E::BaseField>) -> Vec<E> {
fft::evaluate_poly_with_offset(
&self.coefficients,
domain.trace_twiddles(),
domain.offset(),
domain.trace_to_lde_blowup(),
)
}
}
fn merge_compositions<E: FieldElement>(mut polys: Vec<Vec<E>>, divisors: Vec<E>) -> Vec<E> {
iter_mut!(polys).zip(divisors).for_each(|(poly, divisor)| {
polynom::syn_div_in_place(poly, 1, divisor);
});
let mut result = polys.remove(0);
for poly in polys.iter() {
add_in_place(&mut result, poly);
}
result
}
fn acc_trace_poly<F, E>(accumulator: &mut [E], poly: &[F], value: E, k: E)
where
F: FieldElement,
E: FieldElement<BaseField = F::BaseField> + ExtensionOf<F>,
{
mul_acc(accumulator, poly, k);
let adjusted_tz = value * k;
accumulator[0] -= adjusted_tz;
}