1use std::{
5 cmp, iter,
6 ops::{Add, Div, Mul, Neg, Sub},
7 pin::Pin,
8 task::{Context, Poll},
9};
10
11use ark_ec::CurveGroup;
12use ark_ff::{Field, One, Zero};
13use ark_poly::{
14 univariate::{DenseOrSparsePolynomial, DensePolynomial},
15 DenseUVPolynomial,
16};
17use futures::FutureExt;
18use futures::{ready, Future};
19use itertools::Itertools;
20
21use crate::{
22 algebra::{
23 macros::{impl_borrow_variants, impl_commutative},
24 AuthenticatedScalarResult, Scalar, ScalarResult,
25 },
26 MpcFabric, ResultValue,
27};
28
29use super::AuthenticatedDensePoly;
30
31fn x_to_t<C: CurveGroup>(t: usize) -> DensePolynomial<C::ScalarField> {
37 let mut coeffs = vec![C::ScalarField::zero(); t];
38 coeffs.push(C::ScalarField::one());
39 DensePolynomial::from_coefficients_vec(coeffs)
40}
41
42#[derive(Clone, Debug, Default)]
48pub struct DensePolynomialResult<C: CurveGroup> {
49 pub coeffs: Vec<ScalarResult<C>>,
52}
53
54impl<C: CurveGroup> DensePolynomialResult<C> {
55 pub fn from_coeffs(coeffs: Vec<ScalarResult<C>>) -> Self {
57 assert!(!coeffs.is_empty(), "cannot construct an empty polynomial");
58 Self { coeffs }
59 }
60
61 pub fn zero(fabric: &MpcFabric<C>) -> Self {
63 Self::from_coeffs(vec![fabric.zero()])
64 }
65
66 pub fn one(fabric: &MpcFabric<C>) -> Self {
68 Self::from_coeffs(vec![fabric.one()])
69 }
70
71 pub fn degree(&self) -> usize {
73 self.coeffs.len() - 1
74 }
75
76 pub(crate) fn fabric(&self) -> &MpcFabric<C> {
78 self.coeffs[0].fabric()
79 }
80
81 pub fn eval(&self, point: ScalarResult<C>) -> ScalarResult<C> {
83 let fabric = self.fabric();
84 let mut deps = self.coeffs.iter().map(|coeff| coeff.id()).collect_vec();
85 deps.push(point.id());
86
87 let n_coeffs = self.coeffs.len();
88 fabric.new_gate_op(deps, move |mut args| {
89 let coeffs: Vec<Scalar<C>> = args.drain(..n_coeffs).map(|res| res.into()).collect_vec();
90 let point: Scalar<C> = args.pop().unwrap().into();
91
92 let mut res = Scalar::zero();
93 for coeff in coeffs.iter().rev() {
94 res = res * point + coeff;
95 }
96
97 ResultValue::Scalar(res)
98 })
99 }
100}
101
102impl<C: CurveGroup> DensePolynomialResult<C> {
104 pub fn mul_inverse_mod_t(&self, t: usize) -> Self {
108 let ids = self.coeffs.iter().map(|c| c.id()).collect_vec();
109 let n_result_coeffs = t;
110
111 let res_coeffs = self.fabric().new_batch_gate_op(
112 ids,
113 n_result_coeffs, move |args| {
115 let x_to_t = x_to_t::<C>(t);
116
117 let self_coeffs = args
118 .into_iter()
119 .map(|res| Scalar::<C>::from(res).inner())
120 .collect_vec();
121 let self_poly = DensePolynomial::from_coefficients_vec(self_coeffs);
122
123 let (inverse_poly, _) = Self::compute_bezout_polynomials(&self_poly, &x_to_t);
125
126 let self_constant_coeff = self_poly.coeffs[0];
131 let inverse_constant_coeff = inverse_poly.coeffs[0];
132 let constant_coeff_inv = (self_constant_coeff * inverse_constant_coeff)
133 .inverse()
134 .unwrap();
135
136 inverse_poly
137 .coeffs
138 .into_iter()
139 .take(n_result_coeffs)
140 .map(|c| c * constant_coeff_inv)
141 .map(Scalar::new)
142 .map(ResultValue::Scalar)
143 .collect_vec()
144 },
145 );
146
147 Self::from_coeffs(res_coeffs)
148 }
149
150 fn compute_bezout_polynomials(
156 a: &DensePolynomial<C::ScalarField>,
157 b: &DensePolynomial<C::ScalarField>,
158 ) -> (
159 DensePolynomial<C::ScalarField>,
160 DensePolynomial<C::ScalarField>,
161 ) {
162 if b.is_zero() {
163 return (
164 DensePolynomial::from_coefficients_vec(vec![C::ScalarField::one()]), DensePolynomial::zero(), );
167 }
168
169 let a_transformed = DenseOrSparsePolynomial::from(a);
170 let b_transformed = DenseOrSparsePolynomial::from(b);
171 let (quotient, remainder) = a_transformed.divide_with_q_and_r(&b_transformed).unwrap();
172
173 let (f, g) = Self::compute_bezout_polynomials(b, &remainder);
174 let next_g = &f - &("ient * &g);
175
176 (g, next_g)
177 }
178}
179
180impl<C: CurveGroup> Future for DensePolynomialResult<C>
181where
182 C::ScalarField: Unpin,
183{
184 type Output = DensePolynomial<C::ScalarField>;
185 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
186 let mut coeffs = Vec::with_capacity(self.coeffs.len());
187 for coeff in self.coeffs.iter_mut() {
188 let ready_coeff = ready!(coeff.poll_unpin(cx));
189 coeffs.push(ready_coeff.inner());
190 }
191
192 Poll::Ready(DensePolynomial::from_coefficients_vec(coeffs))
193 }
194}
195
196impl<C: CurveGroup> Add<&DensePolynomial<C::ScalarField>> for &DensePolynomialResult<C> {
203 type Output = DensePolynomialResult<C>;
204 fn add(self, rhs: &DensePolynomial<C::ScalarField>) -> Self::Output {
205 assert!(!self.coeffs.is_empty(), "cannot add an empty polynomial");
206 let fabric = self.coeffs[0].fabric();
207
208 let mut coeffs = Vec::new();
209 let max_degree = cmp::max(self.coeffs.len(), rhs.coeffs.len());
210
211 let padded_coeffs0 = self
213 .coeffs
214 .iter()
215 .cloned()
216 .chain(iter::repeat(fabric.zero()));
217 let padded_coeffs1 = rhs
218 .coeffs
219 .iter()
220 .copied()
221 .map(Scalar::<C>::new)
222 .chain(iter::repeat(Scalar::zero()));
223
224 for (lhs_coeff, rhs_coeff) in padded_coeffs0.zip(padded_coeffs1).take(max_degree) {
226 coeffs.push(lhs_coeff + rhs_coeff);
227 }
228
229 DensePolynomialResult::from_coeffs(coeffs)
230 }
231}
232impl_borrow_variants!(DensePolynomialResult<C>, Add, add, +, DensePolynomial<C::ScalarField>, C: CurveGroup);
233impl_commutative!(DensePolynomialResult<C>, Add, add, +, DensePolynomial<C::ScalarField>, C: CurveGroup);
234
235impl<C: CurveGroup> Add<&DensePolynomialResult<C>> for &DensePolynomialResult<C> {
236 type Output = DensePolynomialResult<C>;
237 fn add(self, rhs: &DensePolynomialResult<C>) -> Self::Output {
238 let mut coeffs = Vec::new();
241 let (shorter, longer) = if self.coeffs.len() < rhs.coeffs.len() {
242 (&self.coeffs, &rhs.coeffs)
243 } else {
244 (&rhs.coeffs, &self.coeffs)
245 };
246
247 for (i, longer_coeff) in longer.iter().enumerate() {
248 let new_coeff = if i < shorter.len() {
249 &shorter[i] + longer_coeff
250 } else {
251 longer_coeff.clone()
252 };
253
254 coeffs.push(new_coeff);
255 }
256
257 DensePolynomialResult::from_coeffs(coeffs)
258 }
259}
260impl_borrow_variants!(DensePolynomialResult<C>, Add, add, +, DensePolynomialResult<C>, C: CurveGroup);
261
262impl<C: CurveGroup> Neg for &DensePolynomialResult<C> {
264 type Output = DensePolynomialResult<C>;
265 fn neg(self) -> Self::Output {
266 let mut coeffs = Vec::with_capacity(self.coeffs.len());
267 for coeff in self.coeffs.iter() {
268 coeffs.push(-coeff);
269 }
270
271 DensePolynomialResult::from_coeffs(coeffs)
272 }
273}
274impl_borrow_variants!(DensePolynomialResult<C>, Neg, neg, -, C: CurveGroup);
275
276#[allow(clippy::suspicious_arithmetic_impl)]
278impl<C: CurveGroup> Sub<&DensePolynomial<C::ScalarField>> for &DensePolynomialResult<C> {
279 type Output = DensePolynomialResult<C>;
280 fn sub(self, rhs: &DensePolynomial<C::ScalarField>) -> Self::Output {
281 let negated_rhs_coeffs = rhs.coeffs.iter().map(|coeff| -(*coeff)).collect();
282 let negated_rhs = DensePolynomial::from_coefficients_vec(negated_rhs_coeffs);
283
284 self + negated_rhs
285 }
286}
287impl_borrow_variants!(DensePolynomialResult<C>, Sub, sub, -, DensePolynomial<C::ScalarField>, C: CurveGroup);
288
289#[allow(clippy::suspicious_arithmetic_impl)]
290impl<C: CurveGroup> Sub<&DensePolynomialResult<C>> for &DensePolynomialResult<C> {
291 type Output = DensePolynomialResult<C>;
292 fn sub(self, rhs: &DensePolynomialResult<C>) -> Self::Output {
293 self + (-rhs)
295 }
296}
297
298impl<C: CurveGroup> Mul<&DensePolynomial<C::ScalarField>> for &DensePolynomialResult<C> {
304 type Output = DensePolynomialResult<C>;
305
306 fn mul(self, rhs: &DensePolynomial<C::ScalarField>) -> Self::Output {
307 let fabric = self.coeffs[0].fabric();
308
309 let mut coeffs = Vec::with_capacity(self.coeffs.len() + rhs.coeffs.len() - 1);
310 for _ in 0..self.coeffs.len() + rhs.coeffs.len() - 1 {
311 coeffs.push(fabric.zero());
312 }
313
314 for (i, lhs_coeff) in self.coeffs.iter().enumerate() {
315 for (j, rhs_coeff) in rhs.coeffs.iter().copied().map(Scalar::new).enumerate() {
316 coeffs[i + j] = &coeffs[i + j] + lhs_coeff * rhs_coeff;
317 }
318 }
319
320 DensePolynomialResult::from_coeffs(coeffs)
321 }
322}
323impl_borrow_variants!(DensePolynomialResult<C>, Mul, mul, *, DensePolynomial<C::ScalarField>, C: CurveGroup);
324impl_commutative!(DensePolynomialResult<C>, Mul, mul, *, DensePolynomial<C::ScalarField>, C: CurveGroup);
325
326impl<C: CurveGroup> Mul<&DensePolynomialResult<C>> for &DensePolynomialResult<C> {
327 type Output = DensePolynomialResult<C>;
328
329 fn mul(self, rhs: &DensePolynomialResult<C>) -> Self::Output {
330 let fabric = self.coeffs[0].fabric();
331
332 let mut coeffs = Vec::with_capacity(self.coeffs.len() + rhs.coeffs.len() - 1);
333 for _ in 0..self.coeffs.len() + rhs.coeffs.len() - 1 {
334 coeffs.push(fabric.zero());
335 }
336
337 for (i, lhs_coeff) in self.coeffs.iter().enumerate() {
338 for (j, rhs_coeff) in rhs.coeffs.iter().enumerate() {
339 coeffs[i + j] = &coeffs[i + j] + lhs_coeff * rhs_coeff;
340 }
341 }
342
343 DensePolynomialResult::from_coeffs(coeffs)
344 }
345}
346impl_borrow_variants!(DensePolynomialResult<C>, Mul, mul, *, DensePolynomialResult<C>, C: CurveGroup);
347
348impl<C: CurveGroup> Mul<&Scalar<C>> for &DensePolynomialResult<C> {
351 type Output = DensePolynomialResult<C>;
352
353 fn mul(self, rhs: &Scalar<C>) -> Self::Output {
354 let new_coeffs = self.coeffs.iter().map(|coeff| coeff * rhs).collect_vec();
355 DensePolynomialResult::from_coeffs(new_coeffs)
356 }
357}
358impl_borrow_variants!(DensePolynomialResult<C>, Mul, mul, *, Scalar<C>, C: CurveGroup);
359impl_commutative!(DensePolynomialResult<C>, Mul, mul, *, Scalar<C>, C: CurveGroup);
360
361impl<C: CurveGroup> Mul<&ScalarResult<C>> for &DensePolynomialResult<C> {
362 type Output = DensePolynomialResult<C>;
363
364 fn mul(self, rhs: &ScalarResult<C>) -> Self::Output {
365 let new_coeffs = self.coeffs.iter().map(|coeff| coeff * rhs).collect_vec();
366 DensePolynomialResult::from_coeffs(new_coeffs)
367 }
368}
369impl_borrow_variants!(DensePolynomialResult<C>, Mul, mul, *, ScalarResult<C>, C: CurveGroup);
370impl_commutative!(DensePolynomialResult<C>, Mul, mul, *, ScalarResult<C>, C: CurveGroup);
371
372impl<C: CurveGroup> Mul<&AuthenticatedScalarResult<C>> for &DensePolynomialResult<C> {
373 type Output = AuthenticatedDensePoly<C>;
374
375 fn mul(self, rhs: &AuthenticatedScalarResult<C>) -> Self::Output {
376 let new_coeffs = self.coeffs.iter().map(|coeff| coeff * rhs).collect_vec();
377 AuthenticatedDensePoly::from_coeffs(new_coeffs)
378 }
379}
380impl_borrow_variants!(DensePolynomialResult<C>, Mul, mul, *, AuthenticatedScalarResult<C>, Output=AuthenticatedDensePoly<C>, C: CurveGroup);
381impl_commutative!(DensePolynomialResult<C>, Mul, mul, *, AuthenticatedScalarResult<C>, Output=AuthenticatedDensePoly<C>, C: CurveGroup);
382
383#[allow(clippy::suspicious_arithmetic_impl)]
387impl<C: CurveGroup> Div<&DensePolynomialResult<C>> for &DensePolynomialResult<C> {
388 type Output = DensePolynomialResult<C>;
389
390 fn div(self, rhs: &DensePolynomialResult<C>) -> Self::Output {
391 let fabric = self.coeffs[0].fabric();
392 if self.degree() < rhs.degree() {
393 return DensePolynomialResult::zero(fabric);
394 }
395
396 let n_lhs_coeffs = self.coeffs.len();
397 let n_rhs_coeffs = rhs.coeffs.len();
398
399 let mut deps = self.coeffs.iter().map(|coeff| coeff.id()).collect_vec();
400 deps.extend(rhs.coeffs.iter().map(|coeff| coeff.id()));
401
402 let result_degree = self.degree().saturating_sub(rhs.degree());
404 let coeff_results =
405 fabric.new_batch_gate_op(deps, result_degree + 1 , move |mut args| {
406 let lhs_coeffs: Vec<C::ScalarField> = args
407 .drain(..n_lhs_coeffs)
408 .map(|res| Scalar::<C>::from(res).inner())
409 .collect_vec();
410 let rhs_coeffs = args
411 .drain(..n_rhs_coeffs)
412 .map(|res| Scalar::<C>::from(res).inner())
413 .collect_vec();
414
415 let lhs_poly = DensePolynomial::from_coefficients_vec(lhs_coeffs);
416 let rhs_poly = DensePolynomial::from_coefficients_vec(rhs_coeffs);
417
418 let res = &lhs_poly / &rhs_poly;
419 res.coeffs
420 .iter()
421 .map(|coeff| ResultValue::Scalar(Scalar::new(*coeff)))
422 .collect_vec()
423 });
424
425 DensePolynomialResult::from_coeffs(coeff_results)
426 }
427}
428
429#[cfg(test)]
430pub(crate) mod test {
431 use ark_ff::{One, Zero};
432 use ark_poly::Polynomial;
433 use itertools::Itertools;
434 use rand::{thread_rng, Rng};
435
436 use crate::{
437 algebra::{
438 poly_test_helpers::{allocate_poly, random_poly},
439 Scalar,
440 },
441 test_helpers::execute_mock_mpc,
442 PARTY0,
443 };
444
445 const DEGREE_BOUND: usize = 100;
447
448 #[tokio::test]
452 async fn test_constant_poly_add() {
453 let poly1 = random_poly(DEGREE_BOUND);
454 let poly2 = random_poly(DEGREE_BOUND);
455
456 let expected_res = &poly1 + &poly2;
457
458 let (res, _) = execute_mock_mpc(|fabric| {
459 let poly1 = poly1.clone();
460 let poly2 = poly2.clone();
461
462 async move {
463 let poly1 = allocate_poly(&poly1, &fabric);
464 let res = &poly1 + &poly2;
465
466 res.await
467 }
468 })
469 .await;
470
471 assert_eq!(res, expected_res);
472 }
473
474 #[tokio::test]
476 async fn test_poly_add() {
477 let poly1 = random_poly(DEGREE_BOUND);
478 let poly2 = random_poly(DEGREE_BOUND);
479
480 let expected_res = &poly1 + &poly2;
481
482 let (res, _) = execute_mock_mpc(|fabric| {
483 let poly1 = poly1.clone();
484 let poly2 = poly2.clone();
485
486 async move {
487 let poly1 = allocate_poly(&poly1, &fabric);
488 let poly2 = allocate_poly(&poly2, &fabric);
489 let res = &poly1 + &poly2;
490
491 res.await
492 }
493 })
494 .await;
495
496 assert_eq!(res, expected_res);
497 }
498
499 #[tokio::test]
501 async fn test_poly_sub_constant() {
502 let poly1 = random_poly(DEGREE_BOUND);
503 let poly2 = random_poly(DEGREE_BOUND);
504
505 let expected_res = &poly1 - &poly2;
506
507 let (res, _) = execute_mock_mpc(|fabric| {
508 let poly1 = poly1.clone();
509 let poly2 = poly2.clone();
510
511 async move {
512 let poly1 = allocate_poly(&poly1, &fabric);
513 let res = &poly1 - &poly2;
514
515 res.await
516 }
517 })
518 .await;
519
520 assert_eq!(res, expected_res);
521 }
522
523 #[tokio::test]
525 async fn test_poly_sub() {
526 let poly1 = random_poly(DEGREE_BOUND);
527 let poly2 = random_poly(DEGREE_BOUND);
528
529 let expected_res = &poly1 - &poly2;
530
531 let (res, _) = execute_mock_mpc(|fabric| {
532 let poly1 = poly1.clone();
533 let poly2 = poly2.clone();
534
535 async move {
536 let poly1 = allocate_poly(&poly1, &fabric);
537 let poly2 = allocate_poly(&poly2, &fabric);
538 let res = &poly1 - &poly2;
539
540 res.await
541 }
542 })
543 .await;
544
545 assert_eq!(res, expected_res);
546 }
547
548 #[tokio::test]
550 async fn test_poly_mul_constant() {
551 let poly1 = random_poly(DEGREE_BOUND);
552 let poly2 = random_poly(DEGREE_BOUND);
553
554 let expected_res = &poly1 * &poly2;
555
556 let (res, _) = execute_mock_mpc(|fabric| {
557 let poly1 = poly1.clone();
558 let poly2 = poly2.clone();
559
560 async move {
561 let poly1 = allocate_poly(&poly1, &fabric);
562 let res = &poly1 * &poly2;
563
564 res.await
565 }
566 })
567 .await;
568
569 assert_eq!(res, expected_res);
570 }
571
572 #[tokio::test]
574 async fn test_poly_mul() {
575 let poly1 = random_poly(DEGREE_BOUND);
576 let poly2 = random_poly(DEGREE_BOUND);
577
578 let expected_res = &poly1 * &poly2;
579
580 let (res, _) = execute_mock_mpc(|fabric| {
581 let poly1 = poly1.clone();
582 let poly2 = poly2.clone();
583
584 async move {
585 let poly1 = allocate_poly(&poly1, &fabric);
586 let poly2 = allocate_poly(&poly2, &fabric);
587 let res = &poly1 * &poly2;
588
589 res.await
590 }
591 })
592 .await;
593
594 assert_eq!(res, expected_res);
595 }
596
597 #[tokio::test]
599 async fn test_poly_div() {
600 let poly1 = random_poly(DEGREE_BOUND);
601 let poly2 = random_poly(DEGREE_BOUND);
602
603 let expected_res = &poly1 / &poly2;
604
605 let (res, _) = execute_mock_mpc(|fabric| {
606 let poly1 = poly1.clone();
607 let poly2 = poly2.clone();
608
609 async move {
610 let poly1 = allocate_poly(&poly1, &fabric);
611 let poly2 = allocate_poly(&poly2, &fabric);
612 let res = &poly1 / &poly2;
613
614 res.await
615 }
616 })
617 .await;
618
619 assert_eq!(res, expected_res);
620 }
621
622 #[tokio::test]
624 async fn test_scalar_mul_constant() {
625 let poly = random_poly(DEGREE_BOUND);
626
627 let mut rng = thread_rng();
628 let scaling_factor = Scalar::random(&mut rng);
629
630 let expected_res = &poly * scaling_factor.inner();
631
632 let (res, _) = execute_mock_mpc(|fabric| {
633 let poly = poly.clone();
634 async move {
635 let poly = allocate_poly(&poly, &fabric);
636
637 (poly * scaling_factor).await
638 }
639 })
640 .await;
641
642 assert_eq!(res, expected_res);
643 }
644
645 #[tokio::test]
647 async fn test_scalar_mul() {
648 let poly = random_poly(DEGREE_BOUND);
649
650 let mut rng = thread_rng();
651 let scaling_factor = Scalar::random(&mut rng);
652
653 let expected_res = &poly * scaling_factor.inner();
654
655 let (res, _) = execute_mock_mpc(|fabric| {
656 let poly = poly.clone();
657 async move {
658 let poly = allocate_poly(&poly, &fabric);
659 let scaling_factor = fabric.allocate_scalar(scaling_factor);
660
661 (poly * scaling_factor).await
662 }
663 })
664 .await;
665
666 assert_eq!(res, expected_res);
667 }
668
669 #[tokio::test]
671 async fn test_scalar_mul_shared() {
672 let poly = random_poly(DEGREE_BOUND);
673
674 let mut rng = thread_rng();
675 let scaling_factor = Scalar::random(&mut rng);
676
677 let expected_res = &poly * scaling_factor.inner();
678
679 let (res, _) = execute_mock_mpc(|fabric| {
680 let poly = poly.clone();
681 async move {
682 let poly = allocate_poly(&poly, &fabric);
683 let scaling_factor = fabric.share_scalar(scaling_factor, PARTY0);
684
685 (poly * scaling_factor).open_authenticated().await
686 }
687 })
688 .await;
689
690 assert!(res.is_ok());
691 assert_eq!(res.unwrap(), expected_res);
692 }
693
694 #[tokio::test]
696 async fn test_eval() {
697 let poly = random_poly(DEGREE_BOUND);
698
699 let mut rng = thread_rng();
700 let eval_point = Scalar::random(&mut rng);
701
702 let expected_eval = poly.evaluate(&eval_point.inner());
703
704 let (eval, _) = execute_mock_mpc(|fabric| {
705 let poly = poly.clone();
706 async move {
707 let point_res = fabric.allocate_scalar(eval_point);
708 let poly = allocate_poly(&poly, &fabric);
709
710 poly.eval(point_res).await
711 }
712 })
713 .await;
714
715 assert_eq!(eval.inner(), expected_eval);
716 }
717
718 #[tokio::test]
720 async fn test_mod_inv() {
721 let poly = random_poly(DEGREE_BOUND);
722
723 let mut rng = thread_rng();
724 let t = rng.gen_range(1..(DEGREE_BOUND * 2));
725
726 let (res, _) = execute_mock_mpc(|fabric| {
727 let poly = poly.clone();
728 async move {
729 let poly = allocate_poly(&poly, &fabric);
730
731 poly.mul_inverse_mod_t(t).await
732 }
733 })
734 .await;
735
736 let inverted = &poly * &res;
738 let mut first_t_coeffs = inverted.coeffs.into_iter().take(t).collect_vec();
739
740 assert!(first_t_coeffs.remove(0).is_one());
741 assert!(first_t_coeffs.into_iter().all(|coeff| coeff.is_zero()));
742 }
743}