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}