Skip to main content

kimchi/
proof.rs

1//! This module implements the data structures of a proof.
2use alloc::{vec, vec::Vec};
3
4use crate::circuits::{
5    berkeley_columns::Column,
6    gate::GateType,
7    lookup::lookups::LookupPattern,
8    wires::{COLUMNS, PERMUTS},
9};
10use ark_ec::AffineRepr;
11use ark_ff::{FftField, One, Zero};
12use ark_poly::univariate::DensePolynomial;
13use core::array;
14use o1_utils::ExtendedDensePolynomial;
15use poly_commitment::{
16    commitment::{b_poly, b_poly_coefficients, CommitmentCurve, PolyComm},
17    OpenProof,
18};
19use serde::{Deserialize, Serialize};
20use serde_with::serde_as;
21
22//~ spec:startcode
23/// Evaluations of a polynomial at 2 points
24#[serde_as]
25#[derive(Copy, Clone, Serialize, Deserialize, Default, Debug, PartialEq)]
26#[cfg_attr(
27    feature = "ocaml_types",
28    derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)
29)]
30#[serde(bound(
31    serialize = "Vec<o1_utils::serialization::SerdeAs>: serde_with::SerializeAs<Evals>",
32    deserialize = "Vec<o1_utils::serialization::SerdeAs>: serde_with::DeserializeAs<'de, Evals>"
33))]
34pub struct PointEvaluations<Evals> {
35    /// Evaluation at the challenge point zeta.
36    #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
37    pub zeta: Evals,
38    /// Evaluation at `zeta . omega`, the product of the challenge point and the group generator.
39    #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
40    pub zeta_omega: Evals,
41}
42
43// TODO: this should really be vectors here, perhaps create another type for chunked evaluations?
44/// Polynomial evaluations contained in a `ProverProof`.
45/// - **Chunked evaluations** `Field` is instantiated with vectors with a length
46/// that equals the length of the chunk
47/// - **Non chunked evaluations** `Field` is instantiated with a field, so they
48/// are single-sized#[serde_as]
49#[serde_as]
50#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
51pub struct ProofEvaluations<Evals> {
52    /// public input polynomials
53    pub public: Option<Evals>,
54    /// witness polynomials
55    pub w: [Evals; COLUMNS],
56    /// permutation polynomial
57    pub z: Evals,
58    /// permutation polynomials
59    /// (PERMUTS-1 evaluations because the last permutation is only used in
60    /// commitment form)
61    pub s: [Evals; PERMUTS - 1],
62    /// coefficient polynomials
63    pub coefficients: [Evals; COLUMNS],
64    /// evaluation of the generic selector polynomial
65    pub generic_selector: Evals,
66    /// evaluation of the poseidon selector polynomial
67    pub poseidon_selector: Evals,
68    /// evaluation of the elliptic curve addition selector polynomial
69    pub complete_add_selector: Evals,
70    /// evaluation of the elliptic curve variable base scalar multiplication
71    /// selector polynomial
72    pub mul_selector: Evals,
73    /// evaluation of the endoscalar multiplication selector polynomial
74    pub emul_selector: Evals,
75    /// evaluation of the endoscalar multiplication scalar computation selector
76    /// polynomial
77    pub endomul_scalar_selector: Evals,
78
79    // Optional gates
80    /// evaluation of the RangeCheck0 selector polynomial
81    pub range_check0_selector: Option<Evals>,
82    /// evaluation of the RangeCheck1 selector polynomial
83    pub range_check1_selector: Option<Evals>,
84    /// evaluation of the ForeignFieldAdd selector polynomial
85    pub foreign_field_add_selector: Option<Evals>,
86    /// evaluation of the ForeignFieldMul selector polynomial
87    pub foreign_field_mul_selector: Option<Evals>,
88    /// evaluation of the Xor selector polynomial
89    pub xor_selector: Option<Evals>,
90    /// evaluation of the Rot selector polynomial
91    pub rot_selector: Option<Evals>,
92
93    // lookup-related evaluations
94    /// evaluation of lookup aggregation polynomial
95    pub lookup_aggregation: Option<Evals>,
96    /// evaluation of lookup table polynomial
97    pub lookup_table: Option<Evals>,
98    /// evaluation of lookup sorted polynomials
99    pub lookup_sorted: [Option<Evals>; 5],
100    /// evaluation of runtime lookup table polynomial
101    pub runtime_lookup_table: Option<Evals>,
102
103    // lookup selectors
104    /// evaluation of the runtime lookup table selector polynomial
105    pub runtime_lookup_table_selector: Option<Evals>,
106    /// evaluation of the Xor range check pattern selector polynomial
107    pub xor_lookup_selector: Option<Evals>,
108    /// evaluation of the Lookup range check pattern selector polynomial
109    pub lookup_gate_lookup_selector: Option<Evals>,
110    /// evaluation of the RangeCheck range check pattern selector polynomial
111    pub range_check_lookup_selector: Option<Evals>,
112    /// evaluation of the ForeignFieldMul range check pattern selector
113    /// polynomial
114    pub foreign_field_mul_lookup_selector: Option<Evals>,
115}
116
117/// Commitments linked to the lookup feature
118#[serde_as]
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
120#[serde(bound = "G: ark_serialize::CanonicalDeserialize + ark_serialize::CanonicalSerialize")]
121pub struct LookupCommitments<G: AffineRepr> {
122    /// Commitments to the sorted lookup table polynomial (may have chunks)
123    pub sorted: Vec<PolyComm<G>>,
124    /// Commitment to the lookup aggregation polynomial
125    pub aggreg: PolyComm<G>,
126    /// Optional commitment to concatenated runtime tables
127    pub runtime: Option<PolyComm<G>>,
128}
129
130/// All the commitments that the prover creates as part of the proof.
131#[serde_as]
132#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
133#[serde(bound = "G: ark_serialize::CanonicalDeserialize + ark_serialize::CanonicalSerialize")]
134pub struct ProverCommitments<G: AffineRepr> {
135    /// The commitments to the witness (execution trace)
136    pub w_comm: [PolyComm<G>; COLUMNS],
137    /// The commitment to the permutation polynomial
138    pub z_comm: PolyComm<G>,
139    /// The commitment to the quotient polynomial
140    pub t_comm: PolyComm<G>,
141    /// Commitments related to the lookup argument
142    pub lookup: Option<LookupCommitments<G>>,
143}
144
145/// The proof that the prover creates from a
146/// [ProverIndex](super::prover_index::ProverIndex) and a `witness`.
147#[serde_as]
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
149#[serde(bound = "G: ark_serialize::CanonicalDeserialize + ark_serialize::CanonicalSerialize")]
150pub struct ProverProof<G, OpeningProof, const FULL_ROUNDS: usize>
151where
152    G: CommitmentCurve,
153    OpeningProof: OpenProof<G, FULL_ROUNDS>,
154{
155    /// All the polynomial commitments required in the proof
156    pub commitments: ProverCommitments<G>,
157
158    /// batched commitment opening proof
159    #[serde(bound(
160        serialize = "OpeningProof: Serialize",
161        deserialize = "OpeningProof: Deserialize<'de>"
162    ))]
163    pub proof: OpeningProof,
164
165    /// Two evaluations over a number of committed polynomials
166    pub evals: ProofEvaluations<PointEvaluations<Vec<G::ScalarField>>>,
167
168    /// Required evaluation for [Maller's
169    /// optimization](https://o1-labs.github.io/proof-systems/kimchi/maller_15.html#the-evaluation-of-l)
170    #[serde_as(as = "o1_utils::serialization::SerdeAs")]
171    pub ft_eval1: G::ScalarField,
172
173    /// Accumulators from previously verified proofs in the recursion chain.
174    ///
175    /// Each [`RecursionChallenge`] stores the IPA folding challenges and accumulated
176    /// commitment from verifying a previous proof. Instead of checking the IPA
177    /// immediately (which requires an expensive MSM `<s, G>` where `s` has `2^k`
178    /// elements), we defer this check by storing the accumulator.
179    ///
180    /// During verification, these accumulators are processed as follows:
181    /// 1. The commitments are absorbed into the Fiat-Shamir sponge
182    /// 2. The challenges are used to compute evaluations of `b(X)` at `zeta` and
183    ///    `zeta * omega` (see [`RecursionChallenge::evals`])
184    /// 3. These evaluations are paired with the commitments and included in the
185    ///    batched polynomial commitment check
186    ///
187    /// The actual MSM verification happens in [`SRS::verify`](poly_commitment::ipa::SRS::verify)
188    /// (see `poly-commitment/src/ipa.rs`), where `b_poly_coefficients` computes
189    /// the `2^k` coefficients and they are batched into a single large MSM with
190    /// all other verification checks.
191    ///
192    /// This design enables efficient recursive proof composition as described in
193    /// Section 3.2 of the [Halo paper](https://eprint.iacr.org/2019/1021.pdf).
194    pub prev_challenges: Vec<RecursionChallenge<G>>,
195}
196
197/// Stores the accumulator from a previously verified IPA (Inner Product Argument).
198///
199/// In recursive proof composition, when we verify a proof, the IPA verification
200/// produces an accumulator that can be "deferred" rather than checked immediately.
201/// This accumulator consists of:
202///
203/// - **`chals`**: The folding challenges `u_1, ..., u_k` sampled during the
204///   `k = log_2(n)` rounds of the IPA. These challenges define the
205///   **challenge polynomial** (also called `b(X)` or `h(X)`):
206///   ```text
207///   b(X) = prod_{i=0}^{k-1} (1 + u_{k-i} * X^{2^i})
208///   ```
209///   This polynomial was introduced in Section 3.2 of the
210///   [Halo paper](https://eprint.iacr.org/2019/1021.pdf) as a way to efficiently
211///   represent the folded evaluation point.
212///
213/// - **`comm`**: The accumulated commitment `U = <h, G>` where `h` is the vector
214///   of coefficients of `b(X)` and `G` is the commitment basis. This is the
215///   "deferred" part of IPA verification.
216///
217/// The accumulator satisfies the relation `R_Acc`: anyone can verify it in `O(n)`
218/// time by recomputing `<h, G>`.
219///
220/// See the [accumulation documentation](https://o1-labs.github.io/proof-systems/pickles/accumulation.html)
221/// for a complete description of how these accumulators are used in Pickles.
222#[serde_as]
223#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
224#[serde(bound = "G: ark_serialize::CanonicalDeserialize + ark_serialize::CanonicalSerialize")]
225pub struct RecursionChallenge<G>
226where
227    G: AffineRepr,
228{
229    /// The IPA folding challenges `[u_1, ..., u_k]` that define the challenge
230    /// polynomial `b(X)`. See [`b_poly`].
231    #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
232    pub chals: Vec<G::ScalarField>,
233    /// The accumulated commitment from IPA verification.
234    ///
235    /// This commitment is used in two places:
236    /// 1. Absorbed into the Fq-sponge for Fiat-Shamir (see `prover.rs` and
237    ///    `verifier.rs` where commitments of previous challenges are absorbed).
238    /// 2. Included in the batched polynomial commitment verification, paired
239    ///    with evaluations of `b(X)` at `zeta` and `zeta * omega` (see
240    ///    `verifier.rs` where `polys` is constructed from `prev_challenges`).
241    pub comm: PolyComm<G>,
242}
243
244//~ spec:endcode
245
246impl<Evals> PointEvaluations<Evals> {
247    pub fn map<Evals2, FN: Fn(Evals) -> Evals2>(self, f: &FN) -> PointEvaluations<Evals2> {
248        let PointEvaluations { zeta, zeta_omega } = self;
249        PointEvaluations {
250            zeta: f(zeta),
251            zeta_omega: f(zeta_omega),
252        }
253    }
254
255    pub fn map_ref<Evals2, FN: Fn(&Evals) -> Evals2>(&self, f: &FN) -> PointEvaluations<Evals2> {
256        let PointEvaluations { zeta, zeta_omega } = self;
257        PointEvaluations {
258            zeta: f(zeta),
259            zeta_omega: f(zeta_omega),
260        }
261    }
262}
263
264impl<Eval> ProofEvaluations<Eval> {
265    pub fn map<Eval2, FN: Fn(Eval) -> Eval2>(self, f: &FN) -> ProofEvaluations<Eval2> {
266        let ProofEvaluations {
267            public,
268            w,
269            z,
270            s,
271            coefficients,
272            generic_selector,
273            poseidon_selector,
274            complete_add_selector,
275            mul_selector,
276            emul_selector,
277            endomul_scalar_selector,
278            range_check0_selector,
279            range_check1_selector,
280            foreign_field_add_selector,
281            foreign_field_mul_selector,
282            xor_selector,
283            rot_selector,
284            lookup_aggregation,
285            lookup_table,
286            lookup_sorted,
287            runtime_lookup_table,
288            runtime_lookup_table_selector,
289            xor_lookup_selector,
290            lookup_gate_lookup_selector,
291            range_check_lookup_selector,
292            foreign_field_mul_lookup_selector,
293        } = self;
294        ProofEvaluations {
295            public: public.map(f),
296            w: w.map(f),
297            z: f(z),
298            s: s.map(f),
299            coefficients: coefficients.map(f),
300            generic_selector: f(generic_selector),
301            poseidon_selector: f(poseidon_selector),
302            complete_add_selector: f(complete_add_selector),
303            mul_selector: f(mul_selector),
304            emul_selector: f(emul_selector),
305            endomul_scalar_selector: f(endomul_scalar_selector),
306            range_check0_selector: range_check0_selector.map(f),
307            range_check1_selector: range_check1_selector.map(f),
308            foreign_field_add_selector: foreign_field_add_selector.map(f),
309            foreign_field_mul_selector: foreign_field_mul_selector.map(f),
310            xor_selector: xor_selector.map(f),
311            rot_selector: rot_selector.map(f),
312            lookup_aggregation: lookup_aggregation.map(f),
313            lookup_table: lookup_table.map(f),
314            lookup_sorted: lookup_sorted.map(|x| x.map(f)),
315            runtime_lookup_table: runtime_lookup_table.map(f),
316            runtime_lookup_table_selector: runtime_lookup_table_selector.map(f),
317            xor_lookup_selector: xor_lookup_selector.map(f),
318            lookup_gate_lookup_selector: lookup_gate_lookup_selector.map(f),
319            range_check_lookup_selector: range_check_lookup_selector.map(f),
320            foreign_field_mul_lookup_selector: foreign_field_mul_lookup_selector.map(f),
321        }
322    }
323
324    pub fn map_ref<Eval2, FN: Fn(&Eval) -> Eval2>(&self, f: &FN) -> ProofEvaluations<Eval2> {
325        let ProofEvaluations {
326            public,
327            w: [w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14],
328            z,
329            s: [s0, s1, s2, s3, s4, s5],
330            coefficients: [c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14],
331            generic_selector,
332            poseidon_selector,
333            complete_add_selector,
334            mul_selector,
335            emul_selector,
336            endomul_scalar_selector,
337            range_check0_selector,
338            range_check1_selector,
339            foreign_field_add_selector,
340            foreign_field_mul_selector,
341            xor_selector,
342            rot_selector,
343            lookup_aggregation,
344            lookup_table,
345            lookup_sorted,
346            runtime_lookup_table,
347            runtime_lookup_table_selector,
348            xor_lookup_selector,
349            lookup_gate_lookup_selector,
350            range_check_lookup_selector,
351            foreign_field_mul_lookup_selector,
352        } = self;
353        ProofEvaluations {
354            public: public.as_ref().map(f),
355            w: [
356                f(w0),
357                f(w1),
358                f(w2),
359                f(w3),
360                f(w4),
361                f(w5),
362                f(w6),
363                f(w7),
364                f(w8),
365                f(w9),
366                f(w10),
367                f(w11),
368                f(w12),
369                f(w13),
370                f(w14),
371            ],
372            z: f(z),
373            s: [f(s0), f(s1), f(s2), f(s3), f(s4), f(s5)],
374            coefficients: [
375                f(c0),
376                f(c1),
377                f(c2),
378                f(c3),
379                f(c4),
380                f(c5),
381                f(c6),
382                f(c7),
383                f(c8),
384                f(c9),
385                f(c10),
386                f(c11),
387                f(c12),
388                f(c13),
389                f(c14),
390            ],
391            generic_selector: f(generic_selector),
392            poseidon_selector: f(poseidon_selector),
393            complete_add_selector: f(complete_add_selector),
394            mul_selector: f(mul_selector),
395            emul_selector: f(emul_selector),
396            endomul_scalar_selector: f(endomul_scalar_selector),
397            range_check0_selector: range_check0_selector.as_ref().map(f),
398            range_check1_selector: range_check1_selector.as_ref().map(f),
399            foreign_field_add_selector: foreign_field_add_selector.as_ref().map(f),
400            foreign_field_mul_selector: foreign_field_mul_selector.as_ref().map(f),
401            xor_selector: xor_selector.as_ref().map(f),
402            rot_selector: rot_selector.as_ref().map(f),
403            lookup_aggregation: lookup_aggregation.as_ref().map(f),
404            lookup_table: lookup_table.as_ref().map(f),
405            lookup_sorted: array::from_fn(|i| lookup_sorted[i].as_ref().map(f)),
406            runtime_lookup_table: runtime_lookup_table.as_ref().map(f),
407            runtime_lookup_table_selector: runtime_lookup_table_selector.as_ref().map(f),
408            xor_lookup_selector: xor_lookup_selector.as_ref().map(f),
409            lookup_gate_lookup_selector: lookup_gate_lookup_selector.as_ref().map(f),
410            range_check_lookup_selector: range_check_lookup_selector.as_ref().map(f),
411            foreign_field_mul_lookup_selector: foreign_field_mul_lookup_selector.as_ref().map(f),
412        }
413    }
414}
415
416impl<G: AffineRepr> RecursionChallenge<G> {
417    pub fn new(chals: Vec<G::ScalarField>, comm: PolyComm<G>) -> RecursionChallenge<G> {
418        RecursionChallenge { chals, comm }
419    }
420
421    /// Computes evaluations of the challenge polynomial `b(X)` at the given points.
422    ///
423    /// The challenge polynomial is defined by the IPA challenges as:
424    /// ```text
425    /// b(X) = prod_{i=0}^{k-1} (1 + u_{k-i} * X^{2^i})
426    /// ```
427    /// where `u_1, ..., u_k` are the challenges sampled during the `k` rounds of
428    /// the IPA protocol (stored in `self.chals`).
429    ///
430    /// This method evaluates `b(X)` at `evaluation_points` (typically `zeta` and
431    /// `zeta * omega`). If the polynomial degree exceeds `max_poly_size`, the
432    /// evaluations are "chunked" to handle polynomial splitting.
433    ///
434    /// These evaluations are paired with [`Self::comm`] and included in the
435    /// batched polynomial commitment verification (see `verifier.rs`).
436    ///
437    /// The MSM has size `2^k` where `k` is the number of IPA rounds (e.g., `k = 15` for
438    /// a domain of size `2^15`, giving an MSM of 32768 points). Computing this in-circuit
439    /// would require EC scalar multiplication: using [`VarBaseMul`](crate::circuits::polynomials::varbasemul)
440    /// costs 2 rows per 5 bits (~104 rows for a 256-bit scalar). For an MSM of 32768 points,
441    /// the constraint count would be higher than the accepted circuit size. By deferring to
442    /// the out-of-circuit verifier, we avoid this cost entirely.
443    ///
444    /// # Arguments
445    /// * `max_poly_size` - Maximum polynomial size for chunking
446    /// * `evaluation_points` - Points at which to evaluate (typically `[zeta, zeta * omega]`)
447    /// * `powers_of_eval_points_for_chunks` - Powers used for recombining chunks
448    ///
449    /// # Returns
450    /// A vector of evaluation vectors, one per evaluation point. Each inner vector
451    /// contains the chunked evaluations (or a single evaluation if no chunking needed).
452    ///
453    /// # References
454    /// - [Halo paper, Section 3.2](https://eprint.iacr.org/2019/1021.pdf)
455    pub fn evals(
456        &self,
457        max_poly_size: usize,
458        evaluation_points: &[G::ScalarField],
459        powers_of_eval_points_for_chunks: &[G::ScalarField],
460    ) -> Vec<Vec<G::ScalarField>> {
461        let RecursionChallenge { chals, comm: _ } = self;
462        // No need to check the correctness of poly explicitly. Its correctness is assured by the
463        // checking of the inner product argument.
464        let b_len = 1 << chals.len();
465        let mut b: Option<Vec<G::ScalarField>> = None;
466
467        (0..2)
468            .map(|i| {
469                let full = b_poly(chals, evaluation_points[i]);
470                if max_poly_size == b_len {
471                    return vec![full];
472                }
473                let mut betaacc = G::ScalarField::one();
474                let diff = (max_poly_size..b_len)
475                    .map(|j| {
476                        let b_j = match &b {
477                            None => {
478                                let t = b_poly_coefficients(chals);
479                                let res = t[j];
480                                b = Some(t);
481                                res
482                            }
483                            Some(b) => b[j],
484                        };
485
486                        let ret = betaacc * b_j;
487                        betaacc *= &evaluation_points[i];
488                        ret
489                    })
490                    .fold(G::ScalarField::zero(), |x, y| x + y);
491                vec![full - (diff * powers_of_eval_points_for_chunks[i]), diff]
492            })
493            .collect()
494    }
495}
496
497impl<F: Zero + Copy> ProofEvaluations<PointEvaluations<F>> {
498    pub fn dummy_with_witness_evaluations(
499        curr: [F; COLUMNS],
500        next: [F; COLUMNS],
501    ) -> ProofEvaluations<PointEvaluations<F>> {
502        let pt = |curr, next| PointEvaluations {
503            zeta: curr,
504            zeta_omega: next,
505        };
506        ProofEvaluations {
507            public: Some(pt(F::zero(), F::zero())),
508            w: array::from_fn(|i| pt(curr[i], next[i])),
509            z: pt(F::zero(), F::zero()),
510            s: array::from_fn(|_| pt(F::zero(), F::zero())),
511            coefficients: array::from_fn(|_| pt(F::zero(), F::zero())),
512            generic_selector: pt(F::zero(), F::zero()),
513            poseidon_selector: pt(F::zero(), F::zero()),
514            complete_add_selector: pt(F::zero(), F::zero()),
515            mul_selector: pt(F::zero(), F::zero()),
516            emul_selector: pt(F::zero(), F::zero()),
517            endomul_scalar_selector: pt(F::zero(), F::zero()),
518            range_check0_selector: None,
519            range_check1_selector: None,
520            foreign_field_add_selector: None,
521            foreign_field_mul_selector: None,
522            xor_selector: None,
523            rot_selector: None,
524            lookup_aggregation: None,
525            lookup_table: None,
526            lookup_sorted: array::from_fn(|_| None),
527            runtime_lookup_table: None,
528            runtime_lookup_table_selector: None,
529            xor_lookup_selector: None,
530            lookup_gate_lookup_selector: None,
531            range_check_lookup_selector: None,
532            foreign_field_mul_lookup_selector: None,
533        }
534    }
535}
536
537impl<F: FftField> ProofEvaluations<PointEvaluations<Vec<F>>> {
538    pub fn combine(&self, pt: &PointEvaluations<F>) -> ProofEvaluations<PointEvaluations<F>> {
539        self.map_ref(&|evals| PointEvaluations {
540            zeta: DensePolynomial::eval_polynomial(&evals.zeta, pt.zeta),
541            zeta_omega: DensePolynomial::eval_polynomial(&evals.zeta_omega, pt.zeta_omega),
542        })
543    }
544}
545
546impl<F> ProofEvaluations<F> {
547    pub fn get_column(&self, col: Column) -> Option<&F> {
548        match col {
549            Column::Witness(i) => Some(&self.w[i]),
550            Column::Z => Some(&self.z),
551            Column::LookupSorted(i) => self.lookup_sorted[i].as_ref(),
552            Column::LookupAggreg => self.lookup_aggregation.as_ref(),
553            Column::LookupTable => self.lookup_table.as_ref(),
554            Column::LookupKindIndex(LookupPattern::Xor) => self.xor_lookup_selector.as_ref(),
555            Column::LookupKindIndex(LookupPattern::Lookup) => {
556                self.lookup_gate_lookup_selector.as_ref()
557            }
558            Column::LookupKindIndex(LookupPattern::RangeCheck) => {
559                self.range_check_lookup_selector.as_ref()
560            }
561            Column::LookupKindIndex(LookupPattern::ForeignFieldMul) => {
562                self.foreign_field_mul_lookup_selector.as_ref()
563            }
564            Column::LookupRuntimeSelector => self.runtime_lookup_table_selector.as_ref(),
565            Column::LookupRuntimeTable => self.runtime_lookup_table.as_ref(),
566            Column::Index(GateType::Generic) => Some(&self.generic_selector),
567            Column::Index(GateType::Poseidon) => Some(&self.poseidon_selector),
568            Column::Index(GateType::CompleteAdd) => Some(&self.complete_add_selector),
569            Column::Index(GateType::VarBaseMul) => Some(&self.mul_selector),
570            Column::Index(GateType::EndoMul) => Some(&self.emul_selector),
571            Column::Index(GateType::EndoMulScalar) => Some(&self.endomul_scalar_selector),
572            Column::Index(GateType::RangeCheck0) => self.range_check0_selector.as_ref(),
573            Column::Index(GateType::RangeCheck1) => self.range_check1_selector.as_ref(),
574            Column::Index(GateType::ForeignFieldAdd) => self.foreign_field_add_selector.as_ref(),
575            Column::Index(GateType::ForeignFieldMul) => self.foreign_field_mul_selector.as_ref(),
576            Column::Index(GateType::Xor16) => self.xor_selector.as_ref(),
577            Column::Index(GateType::Rot64) => self.rot_selector.as_ref(),
578            Column::Index(_) => None,
579            Column::Coefficient(i) => Some(&self.coefficients[i]),
580            Column::Permutation(i) => Some(&self.s[i]),
581        }
582    }
583}
584
585//
586// OCaml types
587//
588
589#[cfg(feature = "ocaml_types")]
590pub mod caml {
591    use super::*;
592    use poly_commitment::commitment::caml::CamlPolyComm;
593
594    //
595    // CamlRecursionChallenge<CamlG, CamlF>
596    //
597
598    #[derive(Clone, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
599    pub struct CamlRecursionChallenge<CamlG, CamlF> {
600        pub chals: Vec<CamlF>,
601        pub comm: CamlPolyComm<CamlG>,
602    }
603
604    //
605    // CamlRecursionChallenge<CamlG, CamlF> <-> RecursionChallenge<G>
606    //
607
608    impl<G, CamlG, CamlF> From<RecursionChallenge<G>> for CamlRecursionChallenge<CamlG, CamlF>
609    where
610        G: AffineRepr,
611        CamlG: From<G>,
612        CamlF: From<G::ScalarField>,
613    {
614        fn from(ch: RecursionChallenge<G>) -> Self {
615            Self {
616                chals: ch.chals.into_iter().map(Into::into).collect(),
617                comm: ch.comm.into(),
618            }
619        }
620    }
621
622    impl<G, CamlG, CamlF> From<CamlRecursionChallenge<CamlG, CamlF>> for RecursionChallenge<G>
623    where
624        G: AffineRepr + From<CamlG>,
625        G::ScalarField: From<CamlF>,
626    {
627        fn from(caml_ch: CamlRecursionChallenge<CamlG, CamlF>) -> RecursionChallenge<G> {
628            RecursionChallenge {
629                chals: caml_ch.chals.into_iter().map(Into::into).collect(),
630                comm: caml_ch.comm.into(),
631            }
632        }
633    }
634
635    //
636    // CamlProofEvaluations<CamlF>
637    //
638
639    #[allow(clippy::type_complexity)]
640    #[derive(Clone, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
641    pub struct CamlProofEvaluations<CamlF> {
642        pub w: (
643            PointEvaluations<Vec<CamlF>>,
644            PointEvaluations<Vec<CamlF>>,
645            PointEvaluations<Vec<CamlF>>,
646            PointEvaluations<Vec<CamlF>>,
647            PointEvaluations<Vec<CamlF>>,
648            PointEvaluations<Vec<CamlF>>,
649            PointEvaluations<Vec<CamlF>>,
650            PointEvaluations<Vec<CamlF>>,
651            PointEvaluations<Vec<CamlF>>,
652            PointEvaluations<Vec<CamlF>>,
653            PointEvaluations<Vec<CamlF>>,
654            PointEvaluations<Vec<CamlF>>,
655            PointEvaluations<Vec<CamlF>>,
656            PointEvaluations<Vec<CamlF>>,
657            PointEvaluations<Vec<CamlF>>,
658        ),
659        pub z: PointEvaluations<Vec<CamlF>>,
660        pub s: (
661            PointEvaluations<Vec<CamlF>>,
662            PointEvaluations<Vec<CamlF>>,
663            PointEvaluations<Vec<CamlF>>,
664            PointEvaluations<Vec<CamlF>>,
665            PointEvaluations<Vec<CamlF>>,
666            PointEvaluations<Vec<CamlF>>,
667        ),
668        pub coefficients: (
669            PointEvaluations<Vec<CamlF>>,
670            PointEvaluations<Vec<CamlF>>,
671            PointEvaluations<Vec<CamlF>>,
672            PointEvaluations<Vec<CamlF>>,
673            PointEvaluations<Vec<CamlF>>,
674            PointEvaluations<Vec<CamlF>>,
675            PointEvaluations<Vec<CamlF>>,
676            PointEvaluations<Vec<CamlF>>,
677            PointEvaluations<Vec<CamlF>>,
678            PointEvaluations<Vec<CamlF>>,
679            PointEvaluations<Vec<CamlF>>,
680            PointEvaluations<Vec<CamlF>>,
681            PointEvaluations<Vec<CamlF>>,
682            PointEvaluations<Vec<CamlF>>,
683            PointEvaluations<Vec<CamlF>>,
684        ),
685
686        pub generic_selector: PointEvaluations<Vec<CamlF>>,
687        pub poseidon_selector: PointEvaluations<Vec<CamlF>>,
688        pub complete_add_selector: PointEvaluations<Vec<CamlF>>,
689        pub mul_selector: PointEvaluations<Vec<CamlF>>,
690        pub emul_selector: PointEvaluations<Vec<CamlF>>,
691        pub endomul_scalar_selector: PointEvaluations<Vec<CamlF>>,
692
693        pub range_check0_selector: Option<PointEvaluations<Vec<CamlF>>>,
694        pub range_check1_selector: Option<PointEvaluations<Vec<CamlF>>>,
695        pub foreign_field_add_selector: Option<PointEvaluations<Vec<CamlF>>>,
696        pub foreign_field_mul_selector: Option<PointEvaluations<Vec<CamlF>>>,
697        pub xor_selector: Option<PointEvaluations<Vec<CamlF>>>,
698        pub rot_selector: Option<PointEvaluations<Vec<CamlF>>>,
699        pub lookup_aggregation: Option<PointEvaluations<Vec<CamlF>>>,
700        pub lookup_table: Option<PointEvaluations<Vec<CamlF>>>,
701        pub lookup_sorted: Vec<Option<PointEvaluations<Vec<CamlF>>>>,
702        pub runtime_lookup_table: Option<PointEvaluations<Vec<CamlF>>>,
703
704        pub runtime_lookup_table_selector: Option<PointEvaluations<Vec<CamlF>>>,
705        pub xor_lookup_selector: Option<PointEvaluations<Vec<CamlF>>>,
706        pub lookup_gate_lookup_selector: Option<PointEvaluations<Vec<CamlF>>>,
707        pub range_check_lookup_selector: Option<PointEvaluations<Vec<CamlF>>>,
708        pub foreign_field_mul_lookup_selector: Option<PointEvaluations<Vec<CamlF>>>,
709    }
710
711    //
712    // ProofEvaluations<Vec<F>> <-> CamlProofEvaluations<CamlF>
713    //
714
715    impl<F, CamlF> From<ProofEvaluations<PointEvaluations<Vec<F>>>>
716        for (
717            Option<PointEvaluations<Vec<CamlF>>>,
718            CamlProofEvaluations<CamlF>,
719        )
720    where
721        F: Clone,
722        CamlF: From<F>,
723    {
724        fn from(pe: ProofEvaluations<PointEvaluations<Vec<F>>>) -> Self {
725            let first = pe.public.map(|x: PointEvaluations<Vec<F>>| {
726                // map both fields of each evaluation.
727                x.map(&|x: Vec<F>| {
728                    let y: Vec<CamlF> = x.into_iter().map(Into::into).collect();
729                    y
730                })
731            });
732            let w = (
733                pe.w[0]
734                    .clone()
735                    .map(&|x| x.into_iter().map(Into::into).collect()),
736                pe.w[1]
737                    .clone()
738                    .map(&|x| x.into_iter().map(Into::into).collect()),
739                pe.w[2]
740                    .clone()
741                    .map(&|x| x.into_iter().map(Into::into).collect()),
742                pe.w[3]
743                    .clone()
744                    .map(&|x| x.into_iter().map(Into::into).collect()),
745                pe.w[4]
746                    .clone()
747                    .map(&|x| x.into_iter().map(Into::into).collect()),
748                pe.w[5]
749                    .clone()
750                    .map(&|x| x.into_iter().map(Into::into).collect()),
751                pe.w[6]
752                    .clone()
753                    .map(&|x| x.into_iter().map(Into::into).collect()),
754                pe.w[7]
755                    .clone()
756                    .map(&|x| x.into_iter().map(Into::into).collect()),
757                pe.w[8]
758                    .clone()
759                    .map(&|x| x.into_iter().map(Into::into).collect()),
760                pe.w[9]
761                    .clone()
762                    .map(&|x| x.into_iter().map(Into::into).collect()),
763                pe.w[10]
764                    .clone()
765                    .map(&|x| x.into_iter().map(Into::into).collect()),
766                pe.w[11]
767                    .clone()
768                    .map(&|x| x.into_iter().map(Into::into).collect()),
769                pe.w[12]
770                    .clone()
771                    .map(&|x| x.into_iter().map(Into::into).collect()),
772                pe.w[13]
773                    .clone()
774                    .map(&|x| x.into_iter().map(Into::into).collect()),
775                pe.w[14]
776                    .clone()
777                    .map(&|x| x.into_iter().map(Into::into).collect()),
778            );
779            let coefficients = (
780                pe.coefficients[0]
781                    .clone()
782                    .map(&|x| x.into_iter().map(Into::into).collect()),
783                pe.coefficients[1]
784                    .clone()
785                    .map(&|x| x.into_iter().map(Into::into).collect()),
786                pe.coefficients[2]
787                    .clone()
788                    .map(&|x| x.into_iter().map(Into::into).collect()),
789                pe.coefficients[3]
790                    .clone()
791                    .map(&|x| x.into_iter().map(Into::into).collect()),
792                pe.coefficients[4]
793                    .clone()
794                    .map(&|x| x.into_iter().map(Into::into).collect()),
795                pe.coefficients[5]
796                    .clone()
797                    .map(&|x| x.into_iter().map(Into::into).collect()),
798                pe.coefficients[6]
799                    .clone()
800                    .map(&|x| x.into_iter().map(Into::into).collect()),
801                pe.coefficients[7]
802                    .clone()
803                    .map(&|x| x.into_iter().map(Into::into).collect()),
804                pe.coefficients[8]
805                    .clone()
806                    .map(&|x| x.into_iter().map(Into::into).collect()),
807                pe.coefficients[9]
808                    .clone()
809                    .map(&|x| x.into_iter().map(Into::into).collect()),
810                pe.coefficients[10]
811                    .clone()
812                    .map(&|x| x.into_iter().map(Into::into).collect()),
813                pe.coefficients[11]
814                    .clone()
815                    .map(&|x| x.into_iter().map(Into::into).collect()),
816                pe.coefficients[12]
817                    .clone()
818                    .map(&|x| x.into_iter().map(Into::into).collect()),
819                pe.coefficients[13]
820                    .clone()
821                    .map(&|x| x.into_iter().map(Into::into).collect()),
822                pe.coefficients[14]
823                    .clone()
824                    .map(&|x| x.into_iter().map(Into::into).collect()),
825            );
826            let s = (
827                pe.s[0]
828                    .clone()
829                    .map(&|x| x.into_iter().map(Into::into).collect()),
830                pe.s[1]
831                    .clone()
832                    .map(&|x| x.into_iter().map(Into::into).collect()),
833                pe.s[2]
834                    .clone()
835                    .map(&|x| x.into_iter().map(Into::into).collect()),
836                pe.s[3]
837                    .clone()
838                    .map(&|x| x.into_iter().map(Into::into).collect()),
839                pe.s[4]
840                    .clone()
841                    .map(&|x| x.into_iter().map(Into::into).collect()),
842                pe.s[5]
843                    .clone()
844                    .map(&|x| x.into_iter().map(Into::into).collect()),
845            );
846
847            let second = CamlProofEvaluations {
848                w,
849                coefficients,
850                z: pe.z.map(&|x| x.into_iter().map(Into::into).collect()),
851                s,
852                generic_selector: pe
853                    .generic_selector
854                    .map(&|x| x.into_iter().map(Into::into).collect()),
855                poseidon_selector: pe
856                    .poseidon_selector
857                    .map(&|x| x.into_iter().map(Into::into).collect()),
858                complete_add_selector: pe
859                    .complete_add_selector
860                    .map(&|x| x.into_iter().map(Into::into).collect()),
861                mul_selector: pe
862                    .mul_selector
863                    .map(&|x| x.into_iter().map(Into::into).collect()),
864                emul_selector: pe
865                    .emul_selector
866                    .map(&|x| x.into_iter().map(Into::into).collect()),
867                endomul_scalar_selector: pe
868                    .endomul_scalar_selector
869                    .map(&|x| x.into_iter().map(Into::into).collect()),
870                range_check0_selector: pe
871                    .range_check0_selector
872                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
873                range_check1_selector: pe
874                    .range_check1_selector
875                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
876                foreign_field_add_selector: pe
877                    .foreign_field_add_selector
878                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
879                foreign_field_mul_selector: pe
880                    .foreign_field_mul_selector
881                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
882                xor_selector: pe
883                    .xor_selector
884                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
885                rot_selector: pe
886                    .rot_selector
887                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
888                lookup_aggregation: pe
889                    .lookup_aggregation
890                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
891                lookup_table: pe
892                    .lookup_table
893                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
894                lookup_sorted: pe
895                    .lookup_sorted
896                    .iter()
897                    .map(|x| {
898                        x.as_ref().map(|x| {
899                            x.map_ref(&|x| x.clone().into_iter().map(Into::into).collect())
900                        })
901                    })
902                    .collect::<Vec<_>>(),
903                runtime_lookup_table: pe
904                    .runtime_lookup_table
905                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
906                runtime_lookup_table_selector: pe
907                    .runtime_lookup_table_selector
908                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
909                xor_lookup_selector: pe
910                    .xor_lookup_selector
911                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
912                lookup_gate_lookup_selector: pe
913                    .lookup_gate_lookup_selector
914                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
915                range_check_lookup_selector: pe
916                    .range_check_lookup_selector
917                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
918                foreign_field_mul_lookup_selector: pe
919                    .foreign_field_mul_lookup_selector
920                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
921            };
922
923            (first, second)
924        }
925    }
926
927    impl<F, CamlF>
928        From<(
929            Option<PointEvaluations<Vec<CamlF>>>,
930            CamlProofEvaluations<CamlF>,
931        )> for ProofEvaluations<PointEvaluations<Vec<F>>>
932    where
933        F: Clone,
934        CamlF: Clone,
935        F: From<CamlF>,
936    {
937        fn from(
938            (public, cpe): (
939                Option<PointEvaluations<Vec<CamlF>>>,
940                CamlProofEvaluations<CamlF>,
941            ),
942        ) -> Self {
943            let w = [
944                cpe.w.0.map(&|x| x.into_iter().map(Into::into).collect()),
945                cpe.w.1.map(&|x| x.into_iter().map(Into::into).collect()),
946                cpe.w.2.map(&|x| x.into_iter().map(Into::into).collect()),
947                cpe.w.3.map(&|x| x.into_iter().map(Into::into).collect()),
948                cpe.w.4.map(&|x| x.into_iter().map(Into::into).collect()),
949                cpe.w.5.map(&|x| x.into_iter().map(Into::into).collect()),
950                cpe.w.6.map(&|x| x.into_iter().map(Into::into).collect()),
951                cpe.w.7.map(&|x| x.into_iter().map(Into::into).collect()),
952                cpe.w.8.map(&|x| x.into_iter().map(Into::into).collect()),
953                cpe.w.9.map(&|x| x.into_iter().map(Into::into).collect()),
954                cpe.w.10.map(&|x| x.into_iter().map(Into::into).collect()),
955                cpe.w.11.map(&|x| x.into_iter().map(Into::into).collect()),
956                cpe.w.12.map(&|x| x.into_iter().map(Into::into).collect()),
957                cpe.w.13.map(&|x| x.into_iter().map(Into::into).collect()),
958                cpe.w.14.map(&|x| x.into_iter().map(Into::into).collect()),
959            ];
960            let coefficients = [
961                cpe.coefficients
962                    .0
963                    .map(&|x| x.into_iter().map(Into::into).collect()),
964                cpe.coefficients
965                    .1
966                    .map(&|x| x.into_iter().map(Into::into).collect()),
967                cpe.coefficients
968                    .2
969                    .map(&|x| x.into_iter().map(Into::into).collect()),
970                cpe.coefficients
971                    .3
972                    .map(&|x| x.into_iter().map(Into::into).collect()),
973                cpe.coefficients
974                    .4
975                    .map(&|x| x.into_iter().map(Into::into).collect()),
976                cpe.coefficients
977                    .5
978                    .map(&|x| x.into_iter().map(Into::into).collect()),
979                cpe.coefficients
980                    .6
981                    .map(&|x| x.into_iter().map(Into::into).collect()),
982                cpe.coefficients
983                    .7
984                    .map(&|x| x.into_iter().map(Into::into).collect()),
985                cpe.coefficients
986                    .8
987                    .map(&|x| x.into_iter().map(Into::into).collect()),
988                cpe.coefficients
989                    .9
990                    .map(&|x| x.into_iter().map(Into::into).collect()),
991                cpe.coefficients
992                    .10
993                    .map(&|x| x.into_iter().map(Into::into).collect()),
994                cpe.coefficients
995                    .11
996                    .map(&|x| x.into_iter().map(Into::into).collect()),
997                cpe.coefficients
998                    .12
999                    .map(&|x| x.into_iter().map(Into::into).collect()),
1000                cpe.coefficients
1001                    .13
1002                    .map(&|x| x.into_iter().map(Into::into).collect()),
1003                cpe.coefficients
1004                    .14
1005                    .map(&|x| x.into_iter().map(Into::into).collect()),
1006            ];
1007            let s = [
1008                cpe.s.0.map(&|x| x.into_iter().map(Into::into).collect()),
1009                cpe.s.1.map(&|x| x.into_iter().map(Into::into).collect()),
1010                cpe.s.2.map(&|x| x.into_iter().map(Into::into).collect()),
1011                cpe.s.3.map(&|x| x.into_iter().map(Into::into).collect()),
1012                cpe.s.4.map(&|x| x.into_iter().map(Into::into).collect()),
1013                cpe.s.5.map(&|x| x.into_iter().map(Into::into).collect()),
1014            ];
1015
1016            Self {
1017                public: public.map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1018                w,
1019                coefficients,
1020                z: cpe.z.map(&|x| x.into_iter().map(Into::into).collect()),
1021                s,
1022                generic_selector: cpe
1023                    .generic_selector
1024                    .map(&|x| x.into_iter().map(Into::into).collect()),
1025                poseidon_selector: cpe
1026                    .poseidon_selector
1027                    .map(&|x| x.into_iter().map(Into::into).collect()),
1028                complete_add_selector: cpe
1029                    .complete_add_selector
1030                    .map(&|x| x.into_iter().map(Into::into).collect()),
1031                mul_selector: cpe
1032                    .mul_selector
1033                    .map(&|x| x.into_iter().map(Into::into).collect()),
1034                emul_selector: cpe
1035                    .emul_selector
1036                    .map(&|x| x.into_iter().map(Into::into).collect()),
1037                endomul_scalar_selector: cpe
1038                    .endomul_scalar_selector
1039                    .map(&|x| x.into_iter().map(Into::into).collect()),
1040                range_check0_selector: cpe
1041                    .range_check0_selector
1042                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1043                range_check1_selector: cpe
1044                    .range_check1_selector
1045                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1046                foreign_field_add_selector: cpe
1047                    .foreign_field_add_selector
1048                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1049                foreign_field_mul_selector: cpe
1050                    .foreign_field_mul_selector
1051                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1052                xor_selector: cpe
1053                    .xor_selector
1054                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1055                rot_selector: cpe
1056                    .rot_selector
1057                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1058                lookup_aggregation: cpe
1059                    .lookup_aggregation
1060                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1061                lookup_table: cpe
1062                    .lookup_table
1063                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1064                lookup_sorted: {
1065                    assert_eq!(cpe.lookup_sorted.len(), 5); // Invalid proof
1066                    array::from_fn(|i| {
1067                        cpe.lookup_sorted[i]
1068                            .as_ref()
1069                            .map(|x| x.clone().map(&|x| x.into_iter().map(Into::into).collect()))
1070                    })
1071                },
1072                runtime_lookup_table: cpe
1073                    .runtime_lookup_table
1074                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1075                runtime_lookup_table_selector: cpe
1076                    .runtime_lookup_table_selector
1077                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1078                xor_lookup_selector: cpe
1079                    .xor_lookup_selector
1080                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1081                lookup_gate_lookup_selector: cpe
1082                    .lookup_gate_lookup_selector
1083                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1084                range_check_lookup_selector: cpe
1085                    .range_check_lookup_selector
1086                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1087                foreign_field_mul_lookup_selector: cpe
1088                    .foreign_field_mul_lookup_selector
1089                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1090            }
1091        }
1092    }
1093}
1094
1095#[cfg(all(test, feature = "std"))]
1096mod tests {
1097    use super::*;
1098    use ark_ec::{CurveGroup, VariableBaseMSM};
1099    use ark_ff::Field;
1100    use mina_curves::pasta::{Fp, Fq, Vesta, VestaParameters};
1101    use poly_commitment::PolyComm;
1102    use std::str::FromStr;
1103
1104    type VestaGroup = ark_ec::short_weierstrass::Projective<VestaParameters>;
1105
1106    /// Regression test for RecursionChallenge::evals.
1107    ///
1108    /// This test verifies that the challenge polynomial evaluation remains
1109    /// consistent. The challenge polynomial b(X) is defined as:
1110    /// ```text
1111    /// b(X) = prod_{i=0}^{k-1} (1 + u_{k-i} * X^{2^i})
1112    /// ```
1113    /// See Section 3.2 of the [Halo paper](https://eprint.iacr.org/2019/1021.pdf).
1114    #[test]
1115    fn test_recursion_challenge_evals_regression() {
1116        // 15 challenges (typical for domain size 2^15)
1117        let chals: Vec<Fp> = (2..=16).map(|i| Fp::from(i as u64)).collect();
1118
1119        let comm = PolyComm::<Vesta> {
1120            chunks: vec![Vesta::generator()],
1121        };
1122
1123        let rec_chal = RecursionChallenge::<Vesta>::new(chals.clone(), comm);
1124
1125        let zeta = Fp::from(17u64);
1126        let zeta_omega = Fp::from(19u64);
1127        let evaluation_points = [zeta, zeta_omega];
1128
1129        let max_poly_size = 1 << 15;
1130        let powers_of_eval_points_for_chunks = [
1131            zeta.pow([max_poly_size as u64]),
1132            zeta_omega.pow([max_poly_size as u64]),
1133        ];
1134
1135        let evals = rec_chal.evals(
1136            max_poly_size,
1137            &evaluation_points,
1138            &powers_of_eval_points_for_chunks,
1139        );
1140
1141        // Expected values (computed and verified)
1142        let expected_eval_zeta = Fp::from_str(
1143            "21115683812642620361045381629886583866877919362491419134086003378733605776328",
1144        )
1145        .unwrap();
1146        let expected_eval_zeta_omega = Fp::from_str(
1147            "2298325069360593860729719174291433577456794311517767070156020442825391962511",
1148        )
1149        .unwrap();
1150
1151        assert_eq!(evals[0].len(), 1, "Should have single chunk");
1152        assert_eq!(evals[1].len(), 1, "Should have single chunk");
1153        assert_eq!(evals[0][0], expected_eval_zeta, "b(zeta) mismatch");
1154        assert_eq!(
1155            evals[1][0], expected_eval_zeta_omega,
1156            "b(zeta*omega) mismatch"
1157        );
1158
1159        // Verify consistency with b_poly
1160        assert_eq!(evals[0][0], b_poly(&chals, zeta));
1161        assert_eq!(evals[1][0], b_poly(&chals, zeta_omega));
1162    }
1163
1164    /// Regression test for accumulated commitment computation.
1165    ///
1166    /// The accumulated commitment U = <h, G> where h are the coefficients of
1167    /// b(X) and G is the SRS basis. This test uses a small example (4 challenges)
1168    /// to verify the MSM computation.
1169    #[test]
1170    fn test_recursion_challenge_commitment_regression() {
1171        // Use 4 challenges for a manageable test (2^4 = 16 coefficients)
1172        let chals: Vec<Fp> = vec![
1173            Fp::from(2u64),
1174            Fp::from(3u64),
1175            Fp::from(5u64),
1176            Fp::from(7u64),
1177        ];
1178
1179        // Compute b(X) coefficients
1180        let coeffs = b_poly_coefficients(&chals);
1181        assert_eq!(coeffs.len(), 16);
1182
1183        // Create a simple SRS-like basis (for testing, use multiples of generator)
1184        let g = Vesta::generator();
1185        let basis: Vec<Vesta> = (1..=16)
1186            .map(|i| (g * Fp::from(i as u64)).into_affine())
1187            .collect();
1188
1189        // Compute accumulated commitment U = <coeffs, basis>
1190        let accumulated_comm = VestaGroup::msm(&basis, &coeffs).unwrap().into_affine();
1191
1192        // Expected commitment coordinates (computed and verified)
1193        let expected_x = Fq::from_str(
1194            "3756288960823668761746459900985719106126835112055076922409498125279524024429",
1195        )
1196        .unwrap();
1197        let expected_y = Fq::from_str(
1198            "7540929664328976141648477194277016811781677917189411360504995258251130097840",
1199        )
1200        .unwrap();
1201
1202        assert_eq!(accumulated_comm.x, expected_x, "commitment x mismatch");
1203        assert_eq!(accumulated_comm.y, expected_y, "commitment y mismatch");
1204    }
1205}