use core::ops::{Add, AddAssign, Mul};
use crate::field::SumcheckField;
use super::code::Field;
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct LinearForm<F>(Vec<F>);
impl<F> LinearForm<F> {
pub(crate) fn new(coefficients: Vec<F>) -> Self {
Self(coefficients)
}
pub(crate) fn coefficients(&self) -> &[F] {
&self.0
}
pub(crate) fn into_coefficients(self) -> Vec<F> {
self.0
}
}
impl<F: SumcheckField> Add for LinearForm<F> {
type Output = Self;
fn add(mut self, rhs: Self) -> Self::Output {
self += rhs;
self
}
}
impl<F: SumcheckField> AddAssign for LinearForm<F> {
fn add_assign(&mut self, rhs: Self) {
assert_eq!(self.0.len(), rhs.0.len());
for (acc, term) in self.0.iter_mut().zip(rhs.0) {
*acc += term;
}
}
}
impl<F: SumcheckField> Mul<F> for LinearForm<F> {
type Output = Self;
fn mul(mut self, rhs: F) -> Self::Output {
for coeff in self.0.iter_mut() {
*coeff *= rhs;
}
self
}
}
pub(crate) trait LinearFormHandle {
type Alphabet: Field;
fn form_size(&self) -> usize;
fn folded_form(&self, rand: &[Self::Alphabet]) -> Vec<Self::Alphabet>;
}
pub(crate) struct LinearConstraint<LFH: LinearFormHandle> {
pub(crate) linear_form_handle: LFH,
pub(crate) value: LFH::Alphabet,
}
pub(crate) struct FoldedFormHandle<F> {
pub(crate) linear_form_handle: Box<dyn LinearFormHandle<Alphabet = F>>,
pub(crate) rand: Vec<F>,
pub(crate) scale: F,
}
impl<F: Field> LinearFormHandle for FoldedFormHandle<F> {
type Alphabet = F;
fn form_size(&self) -> usize {
folded_len(self.linear_form_handle.form_size(), self.rand.len())
}
fn folded_form(&self, rand: &[Self::Alphabet]) -> Vec<Self::Alphabet> {
let mut combined = Vec::with_capacity(self.rand.len() + rand.len());
combined.extend_from_slice(&self.rand);
combined.extend_from_slice(rand);
let mut out = self.linear_form_handle.folded_form(&combined);
if self.scale != F::ONE {
for x in out.iter_mut() {
*x *= self.scale;
}
}
out
}
}
pub(crate) struct LinearCombinationForm<F> {
pub(crate) linear_form_handles: Vec<Box<dyn LinearFormHandle<Alphabet = F>>>,
pub(crate) combination_rand: Vec<F>,
}
impl<F: Field> LinearFormHandle for LinearCombinationForm<F> {
type Alphabet = F;
fn form_size(&self) -> usize {
let Some(first) = self.linear_form_handles.first() else {
return 0;
};
let size = first.form_size();
assert!(
self.linear_form_handles
.iter()
.all(|h| h.form_size() == size)
);
size
}
fn folded_form(&self, rand: &[Self::Alphabet]) -> Vec<Self::Alphabet> {
assert_eq!(self.linear_form_handles.len(), self.combination_rand.len());
let form_size = self.form_size();
if form_size == 0 {
return Vec::new();
}
let mut acc = vec![F::ZERO; folded_len(form_size, rand.len())];
for (handle, &coeff) in self.linear_form_handles.iter().zip(&self.combination_rand) {
let folded = handle.folded_form(rand);
assert_eq!(acc.len(), folded.len());
for (a, t) in acc.iter_mut().zip(folded) {
*a += t * coeff;
}
}
acc
}
}
fn fold_step<F>(values: &mut Vec<F>, w: F)
where
F: Copy + core::ops::Add<Output = F> + core::ops::Sub<Output = F> + core::ops::Mul<Output = F>,
{
let half = values.len() / 2;
for k in 0..half {
let lo = values[k];
let hi = values[k + half];
values[k] = lo + w * (hi - lo);
}
values.truncate(half);
}
pub(crate) fn fold_evaluations<F: Field>(values: Vec<F>, rand: &[F]) -> Vec<F> {
assert!(values.len().is_power_of_two());
assert!(rand.len() <= values.len().ilog2() as usize);
rand.iter().copied().fold(values, |mut values, w| {
fold_step(&mut values, w);
values
})
}
fn folded_len(size: usize, rounds: usize) -> usize {
if size == 0 {
return 0;
}
assert!(size.is_power_of_two());
assert!(rounds <= size.ilog2() as usize);
size >> rounds
}