1use crate::{
2 commitment::{b_poly_coefficients, CommitmentCurve},
3 ipa::SRS,
4 PolynomialsToCombine,
5};
6use ark_ec::{CurveGroup, VariableBaseMSM};
7use ark_ff::{batch_inversion, FftField, Field, One, PrimeField, UniformRand, Zero};
8use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Evaluations};
9use o1_utils::ExtendedDensePolynomial;
10use rayon::prelude::*;
11
12pub enum DensePolynomialOrEvaluations<'a, F: FftField, D: EvaluationDomain<F>> {
14 DensePolynomial(&'a DensePolynomial<F>),
16 Evaluations(&'a Evaluations<F, D>, D),
18}
19
20#[derive(Default)]
28struct ScaledChunkedPolynomial<F, P>(Vec<(F, P)>);
29
30impl<F, P> ScaledChunkedPolynomial<F, P> {
31 fn add_poly(&mut self, scale: F, p: P) {
32 self.0.push((scale, p))
33 }
34}
35
36impl<'a, F: Field> ScaledChunkedPolynomial<F, &'a [F]> {
37 fn to_dense_polynomial(&self) -> DensePolynomial<F> {
45 let mut res = DensePolynomial::<F>::zero();
47
48 let scaled: Vec<_> = self
49 .0
50 .par_iter()
51 .map(|(scale, segment)| {
52 let scale = *scale;
53 let v = segment.par_iter().map(|x| scale * *x).collect();
57 DensePolynomial::from_coefficients_vec(v)
58 })
59 .collect();
60
61 for p in scaled {
62 res += &p;
63 }
64
65 res
66 }
67}
68
69pub fn combine_polys<G: CommitmentCurve, D: EvaluationDomain<G::ScalarField>>(
96 plnms: PolynomialsToCombine<G, D>,
97 polyscale: G::ScalarField,
98 srs_length: usize,
99) -> (DensePolynomial<G::ScalarField>, G::ScalarField) {
100 let mut plnm_coefficients =
102 ScaledChunkedPolynomial::<G::ScalarField, &[G::ScalarField]>::default();
103 let mut plnm_evals_part = {
105 let degree = plnms
110 .iter()
111 .fold(None, |acc, (p, _)| match p {
112 DensePolynomialOrEvaluations::DensePolynomial(_) => acc,
113 DensePolynomialOrEvaluations::Evaluations(_, d) => {
114 if let Some(n) = acc {
115 assert_eq!(n, d.size());
116 }
117 Some(d.size())
118 }
119 })
120 .unwrap_or(0);
121 vec![G::ScalarField::zero(); degree]
122 };
123
124 let mut combined_comm = G::ScalarField::zero();
126
127 let mut polyscale_to_i = G::ScalarField::one();
129
130 for (p_i, p_i_comm) in plnms {
137 match p_i {
138 DensePolynomialOrEvaluations::Evaluations(evals_i, sub_domain) => {
142 let stride = evals_i.evals.len() / sub_domain.size();
143 let evals = &evals_i.evals;
144 plnm_evals_part
145 .par_iter_mut()
146 .enumerate()
147 .for_each(|(i, x)| {
148 *x += polyscale_to_i * evals[i * stride];
149 });
150 for comm_chunk in p_i_comm.into_iter() {
151 combined_comm += &(*comm_chunk * polyscale_to_i);
152 polyscale_to_i *= &polyscale;
153 }
154 }
155
156 DensePolynomialOrEvaluations::DensePolynomial(p_i) => {
158 let mut offset = 0;
159 for comm_chunk in p_i_comm.into_iter() {
161 let segment = &p_i.coeffs[std::cmp::min(offset, p_i.coeffs.len())
162 ..std::cmp::min(offset + srs_length, p_i.coeffs.len())];
163 plnm_coefficients.add_poly(polyscale_to_i, segment);
164
165 combined_comm += &(*comm_chunk * polyscale_to_i);
166 polyscale_to_i *= &polyscale;
167 offset += srs_length;
168 }
169 }
170 }
171 }
172
173 let mut combined_plnm = plnm_coefficients.to_dense_polynomial();
179
180 if !plnm_evals_part.is_empty() {
181 let n = plnm_evals_part.len();
185 let max_poly_size = srs_length;
186 let num_chunks = n / max_poly_size + if n % max_poly_size == 0 { 0 } else { 1 };
188 combined_plnm += &Evaluations::from_vec_and_domain(plnm_evals_part, D::new(n).unwrap())
190 .interpolate()
191 .to_chunked_polynomial(num_chunks, max_poly_size)
192 .linearize(polyscale);
193 }
194
195 (combined_plnm, combined_comm)
196}
197
198pub fn batch_dlog_accumulator_check<G: CommitmentCurve>(
200 urs: &SRS<G>,
201 comms: &[G],
202 chals: &[G::ScalarField],
203) -> bool {
204 let k = comms.len();
205
206 if k == 0 {
207 assert_eq!(chals.len(), 0);
208 return true;
209 }
210
211 let rounds = chals.len() / k;
212 assert_eq!(chals.len() % rounds, 0);
213
214 let rs = {
215 let r = G::ScalarField::rand(&mut rand::rngs::OsRng);
216 let mut rs = vec![G::ScalarField::one(); k];
217 for i in 1..k {
218 rs[i] = r * rs[i - 1];
219 }
220 rs
221 };
222
223 let mut points = urs.g.clone();
224 let n = points.len();
225 points.extend(comms);
226
227 let mut scalars = vec![G::ScalarField::zero(); n];
228 scalars.extend(&rs[..]);
229
230 let chal_invs = {
231 let mut cs = chals.to_vec();
232 batch_inversion(&mut cs);
233 cs
234 };
235
236 let termss: Vec<_> = chals
237 .par_iter()
238 .zip(chal_invs)
239 .chunks(rounds)
240 .zip(rs)
241 .map(|(chunk, r)| {
242 let chals: Vec<_> = chunk.iter().map(|(c, _)| **c).collect();
243 let mut s = b_poly_coefficients(&chals);
244 s.iter_mut().for_each(|c| *c *= &r);
245 s
246 })
247 .collect();
248
249 for terms in termss {
250 assert_eq!(terms.len(), n);
251 for i in 0..n {
252 scalars[i] -= &terms[i];
253 }
254 }
255
256 let scalars: Vec<_> = scalars.iter().map(|x| x.into_bigint()).collect();
257 G::Group::msm_bigint(&points, &scalars) == G::Group::zero()
258}
259
260pub fn batch_dlog_accumulator_generate<G: CommitmentCurve>(
261 urs: &SRS<G>,
262 num_comms: usize,
263 chals: &Vec<G::ScalarField>,
264) -> Vec<G> {
265 let k = num_comms;
266
267 if k == 0 {
268 assert_eq!(chals.len(), 0);
269 return vec![];
270 }
271
272 let rounds = chals.len() / k;
273 assert_eq!(chals.len() % rounds, 0);
274
275 let comms: Vec<_> = chals
276 .into_par_iter()
277 .chunks(rounds)
278 .map(|chals| {
279 let chals: Vec<G::ScalarField> = chals.into_iter().copied().collect();
280 let scalars: Vec<_> = b_poly_coefficients(&chals)
281 .into_iter()
282 .map(|x| x.into_bigint())
283 .collect();
284 let points: Vec<_> = urs.g.clone();
285 G::Group::msm_bigint(&points, &scalars).into_affine()
286 })
287 .collect();
288
289 comms
290}