use alloc::vec::Vec;
use air::{proof::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>,
ood_trace_states: TraceOodFrame<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 t1_composition = vec![E::ZERO; trace_length];
let mut t2_composition = vec![E::ZERO; trace_length];
let mut i = 0;
for poly in trace_polys.main_trace_polys() {
acc_trace_poly::<E::BaseField, E>(
&mut t1_composition,
poly,
ood_trace_states.current_row()[i],
self.cc.trace[i],
);
acc_trace_poly::<E::BaseField, E>(
&mut t2_composition,
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 t1_composition,
poly,
ood_trace_states.current_row()[i],
self.cc.trace[i],
);
acc_trace_poly::<E, E>(
&mut t2_composition,
poly,
ood_trace_states.next_row()[i],
self.cc.trace[i],
);
i += 1;
}
let trace_poly =
merge_trace_compositions(vec![t1_composition, t2_composition], vec![self.z, next_z]);
self.coefficients = trace_poly;
assert_eq!(self.poly_size() - 2, self.degree());
}
pub fn add_composition_poly(
&mut self,
composition_poly: CompositionPoly<E>,
ood_evaluations: Vec<E>,
) {
assert!(!self.coefficients.is_empty());
let z = self.z;
let mut column_polys = composition_poly.into_columns();
iter_mut!(column_polys).zip(ood_evaluations).for_each(|(poly, value_at_z)| {
poly[0] -= value_at_z;
polynom::syn_div_in_place(poly, 1, z);
});
for (i, poly) in column_polys.into_iter().enumerate() {
mul_acc::<E, E>(&mut self.coefficients, &poly, self.cc.constraints[i]);
}
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_trace_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;
}