jf_pcs/multilinear_kzg/
util.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the Jellyfish library.
3
4// You should have received a copy of the MIT License
5// along with the Jellyfish library. If not, see <https://mit-license.org/>.
6
7//! Useful utilities for KZG PCS
8
9use crate::prelude::PCSError;
10use ark_ff::PrimeField;
11use ark_poly::{
12    univariate::DensePolynomial, DenseMultilinearExtension, EvaluationDomain, Evaluations,
13    MultilinearExtension, Polynomial, Radix2EvaluationDomain,
14};
15use ark_std::{end_timer, format, log2, start_timer, string::ToString, vec, vec::Vec};
16
17use super::MLE;
18
19/// Evaluate eq polynomial. use the public one later
20#[cfg(any(test, feature = "test-srs"))]
21pub(crate) fn eq_eval<F: PrimeField>(x: &[F], y: &[F]) -> Result<F, PCSError> {
22    if x.len() != y.len() {
23        return Err(PCSError::InvalidParameters(
24            "x and y have different length".to_string(),
25        ));
26    }
27    let start = start_timer!(|| "eq_eval");
28    let mut res = F::one();
29    for (&xi, &yi) in x.iter().zip(y.iter()) {
30        let xi_yi = xi * yi;
31        res *= xi_yi + xi_yi - xi - yi + F::one();
32    }
33    end_timer!(start);
34    Ok(res)
35}
36
37/// Decompose an integer into a binary vector in little endian.
38pub(crate) fn bit_decompose(input: u64, num_var: usize) -> Vec<bool> {
39    let mut res = Vec::with_capacity(num_var);
40    let mut i = input;
41    for _ in 0..num_var {
42        res.push(i & 1 == 1);
43        i >>= 1;
44    }
45    res
46}
47
48/// For an MLE w with `mle_num_vars` variables, and `point_len` number of
49/// points, compute the degree of the univariate polynomial `q(x):= w(l(x))`
50/// where l(x) is a list of polynomials that go through all points.
51// uni_degree is computed as `mle_num_vars * point_len`:
52// - each l(x) is of degree `point_len`
53// - mle has degree one
54// - worst case is `\prod_{i=0}^{mle_num_vars-1} l_i(x) < point_len * mle_num_vars`
55#[inline]
56#[cfg(test)]
57pub fn compute_qx_degree(mle_num_vars: usize, point_len: usize) -> usize {
58    mle_num_vars * point_len
59}
60
61/// get the domain for the univariate polynomial
62#[inline]
63pub(crate) fn get_uni_domain<F: PrimeField>(
64    uni_poly_degree: usize,
65) -> Result<Radix2EvaluationDomain<F>, PCSError> {
66    let domain = match Radix2EvaluationDomain::<F>::new(uni_poly_degree) {
67        Some(p) => p,
68        None => {
69            return Err(PCSError::InvalidParameters(
70                "failed to build radix 2 domain".to_string(),
71            ))
72        },
73    };
74    Ok(domain)
75}
76
77/// Compute W \circ l.
78///
79/// Given an MLE W, and a list of univariate polynomials l, generate the
80/// univariate polynomial that composes W with l.
81///
82/// Returns an error if l's length does not matches number of variables in W.
83pub(crate) fn compute_w_circ_l<F: PrimeField>(
84    w: &DenseMultilinearExtension<F>,
85    l: &[DensePolynomial<F>],
86) -> Result<DensePolynomial<F>, PCSError> {
87    let timer = start_timer!(|| "compute W \\circ l");
88
89    if w.num_vars != l.len() {
90        return Err(PCSError::InvalidParameters(format!(
91            "l's length ({}) does not match num_variables ({})",
92            l.len(),
93            w.num_vars(),
94        )));
95    }
96
97    let mut res_eval: Vec<F> = vec![];
98
99    // TODO: consider to pass this in from caller
100    // uni_degree is (product of each prefix's) + (2 * MLEs)
101    // = (l.len() - (num_vars - log(l.len())) + 2) * l[0].degree
102    let uni_degree = (l.len() - w.num_vars + log2(l.len()) as usize + 2) * l[0].degree();
103
104    let domain = match Radix2EvaluationDomain::<F>::new(uni_degree) {
105        Some(p) => p,
106        None => {
107            return Err(PCSError::InvalidParameters(
108                "failed to build radix 2 domain".to_string(),
109            ))
110        },
111    };
112    for point in domain.elements() {
113        // we reverse the order here because the coefficient vec are stored in
114        // bit-reversed order
115        let l_eval: Vec<F> = l.iter().rev().map(|x| x.evaluate(&point)).collect();
116        res_eval.push(w.evaluate(l_eval.as_ref()).unwrap())
117    }
118    let evaluation = Evaluations::from_vec_and_domain(res_eval, domain);
119    let res = evaluation.interpolate();
120
121    end_timer!(timer);
122    Ok(res)
123}
124
125/// Return the number of variables that one need for an MLE to
126/// batch the list of MLEs
127#[inline]
128pub fn get_batched_nv(num_var: usize, polynomials_len: usize) -> usize {
129    num_var + log2(polynomials_len) as usize
130}
131
132/// merge a set of polynomials. Returns an error if the
133/// polynomials do not share a same number of nvs.
134pub fn merge_polynomials<F: PrimeField>(
135    polynomials: &[MLE<F>],
136) -> Result<DenseMultilinearExtension<F>, PCSError> {
137    let nv = polynomials[0].num_vars();
138    for poly in polynomials.iter() {
139        if nv != poly.num_vars() {
140            return Err(PCSError::InvalidParameters(
141                "num_vars do not match for polynomials".to_string(),
142            ));
143        }
144    }
145
146    let merged_nv = get_batched_nv(nv, polynomials.len());
147    let mut scalars = vec![];
148    for poly in polynomials.iter() {
149        scalars.extend_from_slice(poly.to_evaluations().as_slice());
150    }
151    scalars.extend_from_slice(vec![F::zero(); (1 << merged_nv) - scalars.len()].as_ref());
152    Ok(DenseMultilinearExtension::from_evaluations_vec(
153        merged_nv, scalars,
154    ))
155}
156
157/// Given a list of points, build `l(points)` which is a list of univariate
158/// polynomials that goes through the points
159pub(crate) fn build_l<F: PrimeField>(
160    num_var: usize,
161    points: &[Vec<F>],
162    domain: &Radix2EvaluationDomain<F>,
163) -> Result<Vec<DensePolynomial<F>>, PCSError> {
164    let prefix_len = log2(points.len()) as usize;
165    let mut uni_polys = Vec::new();
166
167    // 1.1 build the indexes and the univariate polys that go through the indexes
168    let indexes: Vec<Vec<bool>> = (0..points.len())
169        .map(|x| bit_decompose(x as u64, prefix_len))
170        .collect();
171    for i in 0..prefix_len {
172        let eval: Vec<F> = indexes
173            .iter()
174            .map(|x| F::from(x[prefix_len - i - 1]))
175            .collect();
176
177        uni_polys.push(Evaluations::from_vec_and_domain(eval, *domain).interpolate());
178    }
179
180    // 1.2 build the actual univariate polys that go through the points
181    for i in 0..num_var {
182        let mut eval: Vec<F> = points.iter().map(|x| x[i]).collect();
183        eval.extend_from_slice(vec![F::zero(); domain.size as usize - eval.len()].as_slice());
184        uni_polys.push(Evaluations::from_vec_and_domain(eval, *domain).interpolate())
185    }
186
187    Ok(uni_polys)
188}
189
190/// Input a list of multilinear polynomials and a list of points,
191/// generate a list of evaluations.
192// Note that this function is only used for testing verifications.
193// In practice verifier does not see polynomials, and the `mle_values`
194// are included in the `batch_proof`.
195#[cfg(test)]
196pub(crate) fn generate_evaluations<F: PrimeField>(
197    polynomials: &[MLE<F>],
198    points: &[Vec<F>],
199) -> Result<Vec<F>, PCSError> {
200    if polynomials.len() != points.len() {
201        return Err(PCSError::InvalidParameters(
202            "polynomial length does not match point length".to_string(),
203        ));
204    }
205
206    let num_var = polynomials[0].num_vars;
207    let uni_poly_degree = points.len();
208    let merge_poly = merge_polynomials(polynomials)?;
209
210    let domain = get_uni_domain::<F>(uni_poly_degree)?;
211    let uni_polys = build_l(num_var, points, &domain)?;
212    let mut mle_values = vec![];
213
214    for i in 0..uni_poly_degree {
215        let point: Vec<F> = uni_polys
216            .iter()
217            .rev()
218            .map(|poly| poly.evaluate(&domain.element(i)))
219            .collect();
220
221        let mle_value = merge_poly.evaluate(&point).unwrap();
222        mle_values.push(mle_value)
223    }
224    Ok(mle_values)
225}
226
227#[cfg(test)]
228mod test {
229    use super::*;
230    use ark_bls12_381::Fr;
231    use ark_ff::MontFp;
232    use ark_poly::DenseUVPolynomial;
233    use ark_std::{One, Zero};
234
235    #[test]
236    fn test_w_circ_l() -> Result<(), PCSError> {
237        test_w_circ_l_helper::<Fr>()
238    }
239
240    fn test_w_circ_l_helper<F: PrimeField>() -> Result<(), PCSError> {
241        {
242            // Example from page 53:
243            // W = 3x1x2 + 2x2 whose evaluations are
244            // 0, 0 |-> 0
245            // 0, 1 |-> 2
246            // 1, 0 |-> 0
247            // 1, 1 |-> 5
248            let w_eval = vec![F::zero(), F::from(2u64), F::zero(), F::from(5u64)];
249            let w = DenseMultilinearExtension::from_evaluations_vec(2, w_eval);
250
251            // l0 =   t + 2
252            // l1 = -2t + 4
253            let l0 = DensePolynomial::from_coefficients_vec(vec![F::from(2u64), F::one()]);
254            let l1 = DensePolynomial::from_coefficients_vec(vec![F::from(4u64), -F::from(2u64)]);
255
256            // res = -6t^2 - 4t + 32
257            let res = compute_w_circ_l(&w, [l0, l1].as_ref())?;
258            let res_rec = DensePolynomial::from_coefficients_vec(vec![
259                F::from(32u64),
260                -F::from(4u64),
261                -F::from(6u64),
262            ]);
263            assert_eq!(res, res_rec);
264        }
265        {
266            // A random example
267            // W = x1x2x3 - 2x1x2 + 3x2x3 - 4x1x3 + 5x1 - 6x2 + 7x3
268            // 0, 0, 0 |->  0
269            // 0, 0, 1 |->  7
270            // 0, 1, 0 |-> -6
271            // 0, 1, 1 |->  4
272            // 1, 0, 0 |->  5
273            // 1, 0, 1 |->  8
274            // 1, 1, 0 |-> -3
275            // 1, 1, 1 |->  4
276            let w_eval = vec![
277                F::zero(),
278                F::from(7u64),
279                -F::from(6u64),
280                F::from(4u64),
281                F::from(5u64),
282                F::from(8u64),
283                -F::from(3u64),
284                F::from(4u64),
285            ];
286            let w = DenseMultilinearExtension::from_evaluations_vec(3, w_eval);
287
288            // l0 =   t + 2
289            // l1 =  3t - 4
290            // l2 = -5t + 6
291            let l0 = DensePolynomial::from_coefficients_vec(vec![F::from(2u64), F::one()]);
292            let l1 = DensePolynomial::from_coefficients_vec(vec![-F::from(4u64), F::from(3u64)]);
293            let l2 = DensePolynomial::from_coefficients_vec(vec![F::from(6u64), -F::from(5u64)]);
294            let res = compute_w_circ_l(&w, [l0, l1, l2].as_ref())?;
295
296            // res = -15t^3 - 23t^2 + 130t - 76
297            let res_rec = DensePolynomial::from_coefficients_vec(vec![
298                -F::from(76u64),
299                F::from(130u64),
300                -F::from(23u64),
301                -F::from(15u64),
302            ]);
303
304            assert_eq!(res, res_rec);
305        }
306        Ok(())
307    }
308
309    #[test]
310    fn test_merge_poly() -> Result<(), PCSError> {
311        test_merge_poly_helper::<Fr>()
312    }
313    fn test_merge_poly_helper<F: PrimeField>() -> Result<(), PCSError> {
314        // Example from page 53:
315        // W1 = 3x1x2 + 2x2 whose evaluations are
316        // 0, 0 |-> 0
317        // 0, 1 |-> 2
318        // 1, 0 |-> 0
319        // 1, 1 |-> 5
320        let w_eval = vec![F::zero(), F::from(2u64), F::zero(), F::from(5u64)];
321        let w1 = MLE::from(DenseMultilinearExtension::from_evaluations_vec(2, w_eval));
322
323        // W2 = x1x2 + x1 whose evaluations are
324        // 0, 0 |-> 0
325        // 0, 1 |-> 0
326        // 1, 0 |-> 1
327        // 1, 1 |-> 2
328        let w_eval = vec![F::zero(), F::zero(), F::from(1u64), F::from(2u64)];
329        let w2 = MLE::from(DenseMultilinearExtension::from_evaluations_vec(2, w_eval));
330
331        // W3 = x1 + x2 whose evaluations are
332        // 0, 0 |-> 0
333        // 0, 1 |-> 1
334        // 1, 0 |-> 1
335        // 1, 1 |-> 2
336        let w_eval = vec![F::zero(), F::one(), F::from(1u64), F::from(2u64)];
337        let w3 = MLE::from(DenseMultilinearExtension::from_evaluations_vec(2, w_eval));
338
339        {
340            // W = (3x1x2 + 2x2)(1-x0) + (x1x2 + x1)x0
341            //   = -2x0x1x2 + x0x1 - 2x0x2 + 3x1x2 + 2x2
342            // with evaluation map
343            //
344            // x0 x1 x2
345            // 0, 0, 0 |->  0
346            // 0, 0, 1 |->  2
347            // 0, 1, 0 |->  0
348            // 0, 1, 1 |->  5
349            // 1, 0, 0 |->  0
350            // 1, 0, 1 |->  0
351            // 1, 1, 0 |->  1
352            // 1, 1, 1 |->  2
353            //
354            let w = merge_polynomials(&[w1.clone(), w2.clone()])?;
355            // w is [0,2,0,5,0,0,1,2]
356            let w_eval = vec![
357                F::zero(),
358                F::from(2u64),
359                F::zero(),
360                F::from(5u64),
361                F::zero(),
362                F::zero(),
363                F::from(1u64),
364                F::from(2u64),
365            ];
366            let w_rec = DenseMultilinearExtension::from_evaluations_vec(3, w_eval);
367
368            assert_eq!(w, w_rec);
369        }
370
371        {
372            // W = (3x1x2 + 2x2)    * (1-y1)    * (1-y2)
373            //   + (x1x2 + x1)      * (1-y1)    * y2
374            //   + (x1 + x2)        * y1        * (1-y2)
375            //
376            // with evaluation map
377            //
378            // y1 y2 x1 x2
379            // 0, 0, 0, 0 |->  0
380            // 0, 0, 0, 1 |->  2
381            // 0, 0, 1, 0 |->  0
382            // 0, 0, 1, 1 |->  5
383            // 0, 1, 0, 0 |->  0
384            // 0, 1, 0, 1 |->  0
385            // 0, 1, 1, 0 |->  1
386            // 0, 1, 1, 1 |->  2
387            // 1, 0, 0, 0 |->  0
388            // 1, 0, 0, 1 |->  1
389            // 1, 0, 1, 0 |->  1
390            // 1, 0, 1, 1 |->  2
391            // 1, 1, 0, 0 |->  0
392            // 1, 1, 0, 1 |->  0
393            // 1, 1, 1, 0 |->  0
394            // 1, 1, 1, 1 |->  0
395            //
396            let w = merge_polynomials(&[w1, w2, w3])?;
397            // w is [0,2,0,5,0,0,1,2, 0,1,1,2]
398            let w_eval = vec![
399                F::zero(),
400                F::from(2u64),
401                F::zero(),
402                F::from(5u64),
403                F::zero(),
404                F::zero(),
405                F::from(1u64),
406                F::from(2u64),
407                F::zero(),
408                F::one(),
409                F::from(1u64),
410                F::from(2u64),
411                F::zero(),
412                F::zero(),
413                F::zero(),
414                F::zero(),
415            ];
416            let w_rec = DenseMultilinearExtension::from_evaluations_vec(4, w_eval);
417
418            assert_eq!(w, w_rec);
419        }
420        Ok(())
421    }
422
423    #[test]
424    fn test_build_l() -> Result<(), PCSError> {
425        test_build_l_helper()
426    }
427
428    fn test_build_l_helper() -> Result<(), PCSError> {
429        // point 1 is [1, 2]
430        let point1 = vec![Fr::from(1u64), Fr::from(2u64)];
431
432        // point 2 is [3, 4]
433        let point2 = vec![Fr::from(3u64), Fr::from(4u64)];
434
435        // point 3 is [5, 6]
436        let point3 = vec![Fr::from(5u64), Fr::from(6u64)];
437
438        {
439            let domain = get_uni_domain::<Fr>(2)?;
440            let l = build_l(2, &[point1.clone(), point2.clone()], &domain)?;
441
442            // roots: [1, -1]
443            // l0 = -1/2 * x + 1/2
444            // l1 = -x + 2
445            // l2 = -x + 3
446            let l0 = DensePolynomial::from_coefficients_vec(vec![
447                Fr::one() / Fr::from(2u64),
448                -Fr::one() / Fr::from(2u64),
449            ]);
450            let l1 = DensePolynomial::from_coefficients_vec(vec![Fr::from(2u64), -Fr::one()]);
451            let l2 = DensePolynomial::from_coefficients_vec(vec![Fr::from(3u64), -Fr::one()]);
452
453            assert_eq!(l0, l[0], "l0 not equal");
454            assert_eq!(l1, l[1], "l1 not equal");
455            assert_eq!(l2, l[2], "l2 not equal");
456        }
457
458        {
459            let domain = get_uni_domain::<Fr>(3)?;
460            let l = build_l(2, &[point1, point2, point3], &domain)?;
461
462            // sage: q = 52435875175126190479447740508185965837690552500527637822603658699938581184513
463            // sage: P.<x> = PolynomialRing(Zmod(q))
464            // sage: root1 = 1
465            // sage: root2 = 0x8D51CCCE760304D0EC030002760300000001000000000000
466            // sage: root3 = -1
467            // sage: root4 = -root2
468            // Arkwork's code is a bit wired: it also interpolate (root4, 0)
469            // which returns a degree 3 polynomial, instead of degree 2
470
471            // ========================
472            // l0: [0, 0, 1]
473            // ========================
474            // sage: points = [(root1, 0), (root2, 0), (root3, 1), (root4, 0)]
475            // sage: P.lagrange_polynomial(points)
476            // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^3 +
477            // 39326906381344642859585805381139474378267914375395728366952744024953935888385*x^2 +
478            // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x +
479            // 39326906381344642859585805381139474378267914375395728366952744024953935888385
480            let l0 = DensePolynomial::from_coefficients_vec(vec![
481                MontFp!(
482                    "39326906381344642859585805381139474378267914375395728366952744024953935888385"
483                ),
484                MontFp!(
485                    "13108968793781547619861935127046491459422638125131909455650914674984645296128"
486                ),
487                MontFp!(
488                    "39326906381344642859585805381139474378267914375395728366952744024953935888385"
489                ),
490                MontFp!(
491                    "13108968793781547619861935127046491459422638125131909455650914674984645296128"
492                ),
493            ]);
494
495            // ========================
496            // l1: [0, 1, 0]
497            // ========================
498            // sage: points = [(root1, 0), (root2, 1), (root3, 0), (root4, 0)]
499            // sage: P.lagrange_polynomial(points)
500            // 866286206518413079694067382671935694567563117191340490752*x^3 +
501            // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^2 +
502            // 52435875175126190478581454301667552757996485117855702128036095582747240693761*x +
503            // 39326906381344642859585805381139474378267914375395728366952744024953935888385
504            let l1 = DensePolynomial::from_coefficients_vec(vec![
505                MontFp!(
506                    "39326906381344642859585805381139474378267914375395728366952744024953935888385"
507                ),
508                MontFp!(
509                    "52435875175126190478581454301667552757996485117855702128036095582747240693761"
510                ),
511                MontFp!(
512                    "13108968793781547619861935127046491459422638125131909455650914674984645296128"
513                ),
514                MontFp!("866286206518413079694067382671935694567563117191340490752"),
515            ]);
516
517            // ========================
518            // l2: [1, 3, 5]
519            // ========================
520            // sage: points = [(root1, 1), (root2, 3), (root3, 5), (root4, 0)]
521            // sage: P.lagrange_polynomial(points)
522            // 2598858619555239239082202148015807083702689351574021472255*x^3 +
523            // 13108968793781547619861935127046491459422638125131909455650914674984645296129*x^2 +
524            // 52435875175126190476848881888630726598608350352511830738900969348364559712256*x +
525            // 39326906381344642859585805381139474378267914375395728366952744024953935888387
526            let l2 = DensePolynomial::from_coefficients_vec(vec![
527                MontFp!(
528                    "39326906381344642859585805381139474378267914375395728366952744024953935888387"
529                ),
530                MontFp!(
531                    "52435875175126190476848881888630726598608350352511830738900969348364559712256"
532                ),
533                MontFp!(
534                    "13108968793781547619861935127046491459422638125131909455650914674984645296129"
535                ),
536                MontFp!("2598858619555239239082202148015807083702689351574021472255"),
537            ]);
538
539            // ========================
540            // l3: [2, 4, 6]
541            // ========================
542            // sage: points = [(root1, 2), (root2, 4), (root3, 6), (root4, 0)]
543            // sage: P.lagrange_polynomial(points)
544            // 3465144826073652318776269530687742778270252468765361963007*x^3 +
545            // x^2 +
546            // 52435875175126190475982595682112313518914282969839895044333406231173219221504*x +
547            // 3
548            let l3 = DensePolynomial::from_coefficients_vec(vec![
549                Fr::from(3u64),
550                MontFp!(
551                    "52435875175126190475982595682112313518914282969839895044333406231173219221504"
552                ),
553                Fr::one(),
554                MontFp!("3465144826073652318776269530687742778270252468765361963007"),
555            ]);
556
557            assert_eq!(l0, l[0], "l0 not equal");
558            assert_eq!(l1, l[1], "l1 not equal");
559            assert_eq!(l2, l[2], "l2 not equal");
560            assert_eq!(l3, l[3], "l3 not equal");
561        }
562        Ok(())
563    }
564
565    #[test]
566    fn test_qx() -> Result<(), PCSError> {
567        // Example from page 53:
568        // W1 = 3x1x2 + 2x2
569        let w_eval = vec![Fr::zero(), Fr::from(2u64), Fr::zero(), Fr::from(5u64)];
570        let w1 = MLE::from(DenseMultilinearExtension::from_evaluations_vec(2, w_eval));
571
572        // W2 = x1x2 + x1
573        let w_eval = vec![Fr::zero(), Fr::zero(), Fr::from(1u64), Fr::from(2u64)];
574        let w2 = MLE::from(DenseMultilinearExtension::from_evaluations_vec(2, w_eval));
575
576        // W3 = x1 + x2
577        let w_eval = vec![Fr::zero(), Fr::one(), Fr::from(1u64), Fr::from(2u64)];
578        let w3 = MLE::from(DenseMultilinearExtension::from_evaluations_vec(2, w_eval));
579
580        let r = Fr::from(42u64);
581
582        // point 1 is [1, 2]
583        let point1 = vec![Fr::from(1u64), Fr::from(2u64)];
584
585        // point 2 is [3, 4]
586        let point2 = vec![Fr::from(3u64), Fr::from(4u64)];
587
588        // point 3 is [5, 6]
589        let point3 = vec![Fr::from(5u64), Fr::from(6u64)];
590
591        {
592            let domain = get_uni_domain::<Fr>(2)?;
593            // w = (3x1x2 + 2x2)(1-x0) + (x1x2 + x1)x0
594            // with evaluations: [0,2,0,5,0,0,1,2]
595            let w = merge_polynomials(&[w1.clone(), w2.clone()])?;
596
597            let l = build_l(2, &[point1.clone(), point2.clone()], &domain)?;
598
599            // sage: P.<x> = PolynomialRing(ZZ)
600            // sage: l0 = -1/2 * x + 1/2
601            // sage: l1 = -x + 2
602            // sage: l2 = -x + 3
603            // sage: w = (3 * l1 * l2 + 2 * l2) * (1-l0) + (l1 * l2 + l1) * l0
604            // sage: w
605            // x^3 - 7/2*x^2 - 7/2*x + 16
606            //
607            // q(x) = x^3 - 7/2*x^2 - 7/2*x + 16
608            let q_x = compute_w_circ_l(&w, &l)?;
609
610            let point: Vec<Fr> = l.iter().rev().map(|poly| poly.evaluate(&r)).collect();
611
612            assert_eq!(
613                q_x.evaluate(&r),
614                w.evaluate(&point).unwrap(),
615                "q(r) != w(l(r))"
616            );
617        }
618
619        {
620            let domain = get_uni_domain::<Fr>(3)?;
621            // W = (3x1x2 + 2x2)    * (1-y1)    * (1-y2)
622            //   + (x1x2 + x1)      * (1-y1)    * y2
623            //   + (x1 + x2)        * y1        * (1-y2)
624            let w = merge_polynomials(&[w1, w2, w3])?;
625
626            let l = build_l(2, &[point1, point2, point3], &domain)?;
627
628            // l0 =
629            // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^3 +
630            // 39326906381344642859585805381139474378267914375395728366952744024953935888385*x^2 +
631            // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x +
632            // 39326906381344642859585805381139474378267914375395728366952744024953935888385
633            //
634            // l1 =
635            // 866286206518413079694067382671935694567563117191340490752*x^3 +
636            // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^2 +
637            // 52435875175126190478581454301667552757996485117855702128036095582747240693761*x +
638            // 39326906381344642859585805381139474378267914375395728366952744024953935888385
639            //
640            // l2 =
641            // 2598858619555239239082202148015807083702689351574021472255*x^3 +
642            // 13108968793781547619861935127046491459422638125131909455650914674984645296129*x^2 +
643            // 52435875175126190476848881888630726598608350352511830738900969348364559712256*x +
644            // 39326906381344642859585805381139474378267914375395728366952744024953935888387
645            //
646            // l3 =
647            // 3465144826073652318776269530687742778270252468765361963007*x^3 +
648            // x^2 +
649            // 52435875175126190475982595682112313518914282969839895044333406231173219221504*x +
650            // 3
651            //
652            // q_x = (3*l2*l3 + 2*l3) * (1-l0) *(1-l1)
653            //     + (l2*l3+l2)*(1-l0)*l1
654            //     + (l2+l3)*l0*(1-l1)
655            // q_x(42) = 42675783400755005965526147011103024780845819057955866345013183657072368533932
656            let q_x = compute_w_circ_l(&w, &l)?;
657
658            let point: Vec<Fr> = vec![
659                l[3].evaluate(&r),
660                l[2].evaluate(&r),
661                l[1].evaluate(&r),
662                l[0].evaluate(&r),
663            ];
664
665            assert_eq!(
666                q_x.evaluate(&r),
667                MontFp!(
668                    "42675783400755005965526147011103024780845819057955866345013183657072368533932"
669                ),
670            );
671            assert_eq!(
672                q_x.evaluate(&r),
673                w.evaluate(&point).unwrap(),
674                "q(r) != w(l(r))"
675            );
676        }
677        Ok(())
678    }
679}