1use crypto_primitives::{PrimeField, Semiring};
2use derive_more::From;
3use num_traits::{CheckedAdd, CheckedMul, CheckedNeg, CheckedSub, ConstZero, Euclid, Zero};
4use std::{
5 fmt::Display,
6 ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Rem, Sub, SubAssign},
7};
8use zinc_transcript::traits::{ConstTranscribable, GenTranscribable, Transcribable};
9use zinc_utils::{
10 UNCHECKED, add,
11 inner_product::{InnerProduct, InnerProductError},
12 mul, rem,
13};
14
15use crate::{
16 EvaluatablePolynomial, EvaluationError, Polynomial,
17 univariate::{dense::DensePolynomial, dynamic},
18};
19
20#[derive(Debug, Clone, From, Hash, PartialEq, Eq)]
32pub struct DynamicPolynomialF<F: PrimeField> {
33 pub coeffs: Vec<F>,
34}
35
36impl<F: PrimeField> DynamicPolynomialF<F> {
37 #[inline(always)]
39 pub fn new_trimmed(coeffs: impl AsRef<[F]>) -> Self {
40 Self {
41 coeffs: dynamic::new_coeffs_trimmed(coeffs.as_ref(), F::is_zero),
42 }
43 }
44
45 #[inline(always)]
46 pub fn new(coeffs: impl AsRef<[F]>) -> Self {
47 Self {
48 coeffs: Vec::from(coeffs.as_ref()),
49 }
50 }
51
52 #[inline(always)]
53 pub fn degree(&self) -> Option<usize> {
54 dynamic::degree(&self.coeffs, F::is_zero)
55 }
56
57 #[inline(always)]
58 pub fn trim(&mut self) {
59 dynamic::trim(&mut self.coeffs, F::is_zero);
60 }
61
62 pub fn constant_poly(a: F) -> Self {
63 if F::is_zero(&a) {
64 Self::default()
65 } else {
66 DynamicPolynomialF { coeffs: vec![a] }
67 }
68 }
69
70 pub fn rotate_right<const D: usize>(&self, c: usize, field_cfg: &F::Config) -> Self {
76 assert!(
77 c > 0 && c < D,
78 "rotate_right count {c} out of range (must satisfy 0 < c < {D})",
79 );
80
81 let mut coeffs = self.coeffs.clone();
82 coeffs.resize(D, F::zero_with_cfg(field_cfg));
83 Self {
84 coeffs: (0..D)
85 .map(|i| coeffs[rem!(add!(i, c), D)].clone())
86 .collect(),
87 }
88 }
89
90 pub fn shr<const D: usize>(&self, c: usize, field_cfg: &F::Config) -> Self {
96 assert!(
97 c > 0 && c < D,
98 "shr count {c} out of range (must satisfy 0 < c < {D})",
99 );
100
101 let zero = F::zero_with_cfg(field_cfg);
102 let mut coeffs = self.coeffs.clone();
103 coeffs.resize(D, zero.clone());
104 Self {
105 coeffs: (0..D)
106 .map(|i| {
107 let j = add!(i, c);
108 if j < D {
109 coeffs[j].clone()
110 } else {
111 zero.clone()
112 }
113 })
114 .collect(),
115 }
116 }
117}
118
119pub struct DynamicPolyFInnerProduct;
121
122impl<F: PrimeField> InnerProduct<[F], F, F> for DynamicPolyFInnerProduct {
123 #[allow(clippy::arithmetic_side_effects)]
124 fn inner_product<const CHECK: bool>(
125 lhs: &[F],
126 rhs: &[F],
127 zero: F,
128 ) -> Result<F, InnerProductError> {
129 Ok(lhs
130 .iter()
131 .zip(rhs)
132 .fold(zero, |acc, (coeff, power)| acc + coeff.clone() * power))
133 }
134}
135
136impl<F: PrimeField> Display for DynamicPolynomialF<F> {
137 #[inline(always)]
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 dynamic::display(&self.coeffs, f)
140 }
141}
142
143impl<F: PrimeField> Default for DynamicPolynomialF<F> {
144 fn default() -> Self {
145 Self {
146 coeffs: Default::default(),
147 }
148 }
149}
150
151impl<F: PrimeField> Zero for DynamicPolynomialF<F> {
152 #[inline(always)]
153 fn zero() -> Self {
154 Default::default()
155 }
156
157 #[inline(always)]
158 fn is_zero(&self) -> bool {
159 dynamic::is_zero(&self.coeffs, F::is_zero)
160 }
161}
162
163impl<F: PrimeField> ConstZero for DynamicPolynomialF<F> {
164 const ZERO: Self = Self { coeffs: Vec::new() };
165}
166
167impl<F: PrimeField> Neg for DynamicPolynomialF<F> {
168 type Output = Self;
169
170 #[inline(always)]
171 fn neg(self) -> Self::Output {
172 Self {
173 coeffs: dynamic::neg(self.coeffs),
174 }
175 }
176}
177
178impl<F: PrimeField> Add for DynamicPolynomialF<F> {
179 type Output = Self;
180
181 fn add(self, rhs: Self) -> Self::Output {
182 self.add(&rhs)
183 }
184}
185
186impl<F: PrimeField> Add<&Self> for DynamicPolynomialF<F> {
187 type Output = Self;
188
189 #[inline(always)]
190 fn add(mut self, rhs: &Self) -> Self::Output {
191 self.add_assign(rhs);
192
193 self
194 }
195}
196
197impl<F: PrimeField> Sub for DynamicPolynomialF<F> {
198 type Output = Self;
199
200 fn sub(self, rhs: Self) -> Self::Output {
201 self.sub(&rhs)
202 }
203}
204
205impl<F: PrimeField> Sub<&Self> for DynamicPolynomialF<F> {
206 type Output = Self;
207
208 fn sub(mut self, rhs: &Self) -> Self::Output {
209 self.sub_assign(rhs);
210
211 self
212 }
213}
214
215impl<F: PrimeField> Mul for DynamicPolynomialF<F> {
216 type Output = Self;
217
218 #[allow(clippy::arithmetic_side_effects)]
219 fn mul(self, rhs: Self) -> Self::Output {
220 &self * &rhs
221 }
222}
223
224impl<F: PrimeField> Mul<&Self> for DynamicPolynomialF<F> {
225 type Output = Self;
226
227 #[allow(clippy::arithmetic_side_effects)]
228 fn mul(self, rhs: &Self) -> Self::Output {
229 &self * rhs
230 }
231}
232
233impl<'a, F: PrimeField> Mul<&'a DynamicPolynomialF<F>> for &'a DynamicPolynomialF<F> {
234 type Output = DynamicPolynomialF<F>;
235
236 #[allow(clippy::arithmetic_side_effects)]
237 fn mul(self, rhs: Self) -> Self::Output {
238 Self::Output {
239 coeffs: dynamic::mul::<_, UNCHECKED>(&self.coeffs, &rhs.coeffs, F::is_zero)
240 .expect("overflow in a field will not happen"),
241 }
242 }
243}
244
245impl<F: PrimeField> CheckedAdd for DynamicPolynomialF<F> {
246 #[allow(clippy::arithmetic_side_effects)] fn checked_add(&self, rhs: &Self) -> Option<Self> {
248 Some(self.clone() + rhs)
249 }
250}
251
252impl<F: PrimeField> CheckedSub for DynamicPolynomialF<F> {
253 #[allow(clippy::arithmetic_side_effects)] fn checked_sub(&self, rhs: &Self) -> Option<Self> {
255 Some(self.clone() - rhs)
256 }
257}
258
259impl<F: PrimeField> CheckedMul for DynamicPolynomialF<F> {
260 #[allow(clippy::arithmetic_side_effects)] fn checked_mul(&self, rhs: &Self) -> Option<Self> {
262 Some(self * rhs)
263 }
264}
265
266impl<F: PrimeField> CheckedNeg for DynamicPolynomialF<F> {
267 fn checked_neg(&self) -> Option<Self> {
268 Some(self.clone().neg())
270 }
271}
272
273impl<F: PrimeField> AddAssign for DynamicPolynomialF<F> {
274 fn add_assign(&mut self, rhs: Self) {
275 self.add_assign(&rhs);
276 }
277}
278
279impl<F: PrimeField> AddAssign<&Self> for DynamicPolynomialF<F> {
280 #[allow(clippy::arithmetic_side_effects)] fn add_assign(&mut self, rhs: &Self) {
282 dynamic::add_assign(&mut self.coeffs, &rhs.coeffs, |elem| {
283 F::zero_with_cfg(elem.cfg())
284 });
285 }
286}
287
288impl<F: PrimeField> SubAssign for DynamicPolynomialF<F> {
289 #[allow(clippy::arithmetic_side_effects)] fn sub_assign(&mut self, rhs: Self) {
291 self.sub_assign(&rhs);
292 }
293}
294
295impl<F: PrimeField> SubAssign<&Self> for DynamicPolynomialF<F> {
296 #[allow(clippy::arithmetic_side_effects)] fn sub_assign(&mut self, rhs: &Self) {
298 dynamic::sub_assign(&mut self.coeffs, &rhs.coeffs, |elem| {
299 F::zero_with_cfg(elem.cfg())
300 });
301 }
302}
303
304impl<F: PrimeField> MulAssign for DynamicPolynomialF<F> {
305 #[allow(clippy::arithmetic_side_effects)] fn mul_assign(&mut self, rhs: Self) {
307 let res = rhs * &*self;
308
309 *self = res
310 }
311}
312
313impl<F: PrimeField> MulAssign<&Self> for DynamicPolynomialF<F> {
314 #[allow(clippy::arithmetic_side_effects)] fn mul_assign(&mut self, rhs: &Self) {
316 let res = &*self * rhs;
317
318 *self = res;
319 }
320}
321
322impl<F: PrimeField> Semiring for DynamicPolynomialF<F> {}
323
324impl<F: PrimeField> Div for DynamicPolynomialF<F> {
325 type Output = Self;
326
327 fn div(self, rhs: Self) -> Self::Output {
328 self.div_rem_euclid(&rhs).0
329 }
330}
331
332impl<F: PrimeField> Rem for DynamicPolynomialF<F> {
333 type Output = Self;
334
335 fn rem(self, rhs: Self) -> Self::Output {
336 self.div_rem_euclid(&rhs).1
337 }
338}
339
340impl<F: PrimeField> Euclid for DynamicPolynomialF<F> {
341 fn div_euclid(&self, v: &Self) -> Self {
342 self.div_rem_euclid(v).0
343 }
344
345 fn rem_euclid(&self, v: &Self) -> Self {
346 self.div_rem_euclid(v).1
347 }
348
349 #[allow(clippy::arithmetic_side_effects)]
350 fn div_rem_euclid(&self, divisor: &Self) -> (Self, Self) {
351 let zero_poly = DynamicPolynomialF::zero();
352
353 let divisor_deg = divisor.degree().expect("division by zero polynomial");
354
355 let Some(dividend_deg) = self.degree() else {
356 return (zero_poly.clone(), zero_poly);
357 };
358
359 let cfg = self.coeffs[0].cfg();
360 let zero = F::zero_with_cfg(cfg);
361
362 if dividend_deg < divisor_deg {
363 return (zero_poly.clone(), self.clone());
364 }
365
366 let mut remainder = self.coeffs.clone();
368 let quotient_len = dividend_deg - divisor_deg + 1;
369 let mut quotient = vec![zero.clone(); quotient_len];
370
371 let divisor_lead_inv = F::one_with_cfg(cfg) / divisor.coeffs[divisor_deg].clone();
372
373 for i in (0..quotient_len).rev() {
374 let rem_idx = i + divisor_deg;
375 if remainder[rem_idx] == zero {
376 continue;
377 }
378
379 let coeff = remainder[rem_idx].clone() * &divisor_lead_inv;
381
382 for j in 0..divisor_deg {
384 remainder[i + j] -= coeff.clone() * &divisor.coeffs[j];
385 }
386 remainder[rem_idx] = zero.clone();
388
389 quotient[i] = coeff;
390 }
391
392 (
393 DynamicPolynomialF { coeffs: quotient },
394 DynamicPolynomialF { coeffs: remainder },
395 )
396 }
397}
398
399impl<F: PrimeField, const DEGFEE_PLUS_ONE: usize> From<DensePolynomial<F, DEGFEE_PLUS_ONE>>
400 for DynamicPolynomialF<F>
401{
402 fn from(dense_poly: DensePolynomial<F, DEGFEE_PLUS_ONE>) -> Self {
403 Self {
404 coeffs: Vec::from(dense_poly.coeffs),
405 }
406 }
407}
408
409impl<F: PrimeField> Polynomial<F> for DynamicPolynomialF<F> {
410 const DEGREE_BOUND: usize = usize::MAX;
411}
412
413impl<F: PrimeField> EvaluatablePolynomial<F, F> for DynamicPolynomialF<F> {
414 type EvaluationPoint = F;
415
416 fn evaluate_at_point(&self, point: &F) -> Result<F, EvaluationError> {
417 let mut result = self
419 .coeffs
420 .last()
421 .cloned()
422 .unwrap_or(F::zero_with_cfg(point.cfg()));
423
424 for coeff in self.coeffs.iter().rev().skip(1) {
425 result *= point;
426 result += coeff;
427 }
428
429 Ok(result)
430 }
431}
432
433impl<F: PrimeField> FromIterator<F> for DynamicPolynomialF<F> {
434 #[inline(always)]
435 fn from_iter<T: IntoIterator<Item = F>>(iter: T) -> Self {
436 Self {
437 coeffs: iter.into_iter().collect(),
438 }
439 }
440}
441
442#[derive(Debug, Default, Clone, From, Hash, PartialEq, Eq)]
446#[repr(transparent)]
447pub struct DynamicPolyVecF<F: PrimeField>(pub Vec<DynamicPolynomialF<F>>);
448
449impl<F: PrimeField> DynamicPolyVecF<F> {
450 pub fn reinterpret(value: &Vec<DynamicPolynomialF<F>>) -> &Self {
451 unsafe { &*(value as *const Vec<DynamicPolynomialF<F>> as *const Self) }
454 }
455}
456
457impl<F> GenTranscribable for DynamicPolyVecF<F>
458where
459 F: PrimeField,
460 F::Inner: ConstTranscribable,
461 F::Modulus: ConstTranscribable,
462{
463 fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
464 if bytes.is_empty() {
465 return vec![].into();
466 }
467
468 let modulus_present = match bytes[0] {
469 0 => false,
470 1 => true,
471 v => panic!("Invalid modulus presence flag: expected 0 or 1, got {v}"),
472 };
473 let mut bytes = &bytes[1..];
474 let cfg_option = if modulus_present {
475 let mod_size = F::Modulus::NUM_BYTES;
476 let cfg = zinc_transcript::read_field_cfg::<F>(&bytes[0..mod_size]);
477 bytes = &bytes[mod_size..];
478 Some(cfg)
479 } else {
480 None
481 };
482 let mut result = Vec::new();
483 while !bytes.is_empty() {
484 let (len, rest) = u32::read_transcription_bytes_subset(bytes);
485 let len = usize::try_from(len).expect("polynomial length must fit into usize");
486 bytes = rest;
487 let end = mul!(len, F::Inner::NUM_BYTES);
488 let coeffs = if let Some(ref cfg) = cfg_option {
489 zinc_transcript::read_field_vec_with_cfg(&bytes[..end], cfg)
490 } else {
491 assert_eq!(len, 0, "modulus must be present when poly is non-empty");
492 Vec::new()
493 };
494 result.push(DynamicPolynomialF { coeffs });
495 bytes = &bytes[end..];
496 }
497 assert!(bytes.is_empty(), "All bytes should be consumed");
498 result.into()
499 }
500
501 fn write_transcription_bytes_exact(&self, mut buf: &mut [u8]) {
502 if self.0.is_empty() {
503 return;
504 }
505 if let Some(element) = self.0.iter().find_map(|poly| poly.coeffs.first()) {
506 buf[0] = 1; buf = zinc_transcript::append_field_cfg::<F>(&mut buf[1..], &element.modulus());
508 } else {
509 buf[0] = 0;
510 buf = &mut buf[1..];
511 }
512 for poly in self.0.iter() {
513 let len = poly.degree().map(|d| add!(d, 1)).unwrap_or(0);
515 buf = {
516 let len = u32::try_from(len).expect("polynomial length must fit into u32");
517 len.write_transcription_bytes_exact(&mut buf[0..u32::NUM_BYTES]);
518 &mut buf[u32::NUM_BYTES..]
519 };
520 buf = zinc_transcript::append_field_vec_inner(buf, &poly.coeffs[0..len]);
521 }
522 assert!(buf.is_empty(), "Entire buffer should be used");
523 }
524}
525
526impl<F> Transcribable for DynamicPolyVecF<F>
527where
528 F: PrimeField,
529 F::Inner: ConstTranscribable,
530 F::Modulus: ConstTranscribable,
531{
532 fn get_num_bytes(&self) -> usize {
533 if self.0.is_empty() {
534 return 0;
535 }
536 let has_modulus = self.0.iter().any(|p| !p.coeffs.is_empty());
538 let modulus_bytes = if has_modulus {
539 F::Modulus::NUM_BYTES
540 } else {
541 0
542 };
543 let poly_bytes: usize = self
544 .0
545 .iter()
546 .map(|poly| {
547 let len = poly.degree().map(|d| add!(d, 1)).unwrap_or(0);
548 add!(u32::NUM_BYTES, mul!(len, F::Inner::NUM_BYTES))
549 })
550 .sum();
551 add!(add!(1, modulus_bytes), poly_bytes)
552 }
553}
554
555#[cfg(test)]
556#[allow(clippy::arithmetic_side_effects)]
557mod tests {
558 use crypto_bigint::{Odd, modular::MontyParams};
559 use crypto_primitives::{FromWithConfig, crypto_bigint_monty::F256};
560 use proptest::prelude::*;
561 use rand::Rng;
562
563 use super::*;
564
565 const LIMBS: usize = 4;
566 type F = F256;
567
568 fn test_config() -> MontyParams<LIMBS> {
569 let modulus = crypto_bigint::Uint::<LIMBS>::from_be_hex(
570 "0000000000000000000000000000000000860995AE68FC80E1B1BD1E39D54B33",
571 );
572 let modulus = Odd::new(modulus).expect("modulus should be odd");
573 MontyParams::new(modulus)
574 }
575
576 #[test]
577 fn new_creates_correctly() {
578 let field_cfg = test_config();
579 assert_eq!(
580 DynamicPolynomialF::new_trimmed([
581 F::from_with_cfg(1i32, &field_cfg),
582 F::from_with_cfg(2i32, &field_cfg),
583 F::from_with_cfg(3i32, &field_cfg),
584 F::zero_with_cfg(&field_cfg),
585 F::zero_with_cfg(&field_cfg),
586 ]),
587 DynamicPolynomialF {
588 coeffs: vec![
589 F::from_with_cfg(1i32, &field_cfg),
590 F::from_with_cfg(2i32, &field_cfg),
591 F::from_with_cfg(3i32, &field_cfg),
592 ]
593 }
594 );
595 }
596
597 fn get_2_test_polynomial() -> (DynamicPolynomialF<F>, DynamicPolynomialF<F>) {
598 let field_cfg = test_config();
599
600 let x = DynamicPolynomialF::new(vec![
601 F::from_with_cfg(2i32, &field_cfg),
602 F::zero_with_cfg(&field_cfg),
603 F::from_with_cfg(2i32, &field_cfg),
604 F::zero_with_cfg(&field_cfg),
605 F::zero_with_cfg(&field_cfg),
606 ]);
607
608 let y = DynamicPolynomialF::new(vec![
609 F::from_with_cfg(1i32, &field_cfg),
610 F::from_with_cfg(2i32, &field_cfg),
611 F::from_with_cfg(3i32, &field_cfg),
612 ]);
613
614 (x, y)
615 }
616
617 #[test]
618 fn add_zero() {
619 let field_cfg = test_config();
620
621 assert_eq!(
622 DynamicPolynomialF::<F>::ZERO + DynamicPolynomialF::ZERO,
623 DynamicPolynomialF::ZERO
624 );
625
626 let x = DynamicPolynomialF::new(vec![
627 F::from_with_cfg(2i32, &field_cfg),
628 F::zero_with_cfg(&field_cfg),
629 F::from_with_cfg(2i32, &field_cfg),
630 F::zero_with_cfg(&field_cfg),
631 F::zero_with_cfg(&field_cfg),
632 ]);
633
634 assert_eq!(x.clone() + &DynamicPolynomialF::ZERO, x);
635 assert_eq!(DynamicPolynomialF::ZERO + x.clone(), x);
636 assert_eq!(DynamicPolynomialF::ZERO + &x, x);
637
638 let mut y = x.clone();
639
640 y += DynamicPolynomialF::ZERO;
641
642 assert_eq!(y, x);
643
644 y += &DynamicPolynomialF::ZERO;
645
646 assert_eq!(y, x);
647 }
648
649 #[test]
650 fn addition_is_correct() {
651 let (x, y) = get_2_test_polynomial();
652
653 let field_cfg = test_config();
654
655 let res = DynamicPolynomialF::new(vec![
656 F::from_with_cfg(3i32, &field_cfg),
657 F::from_with_cfg(2i32, &field_cfg),
658 F::from_with_cfg(5i32, &field_cfg),
659 F::zero_with_cfg(&field_cfg),
660 F::zero_with_cfg(&field_cfg),
661 ]);
662
663 assert_eq!(x.clone() + &y, res);
664 assert_eq!(y.clone() + &x, res);
665 assert_eq!(x.clone() + y.clone(), res);
666 assert_eq!(x.checked_add(&y), Some(res.clone()));
667
668 let mut z = x.clone();
669 z += y.clone();
670 assert_eq!(z, res);
671
672 let mut z = x.clone();
673 z += &y;
674 assert_eq!(z, res);
675
676 let mut z = y.clone();
677 z += x.clone();
678 assert_eq!(z, res);
679
680 let mut z = y.clone();
681 z += &x;
682 assert_eq!(z, res);
683 }
684
685 #[test]
686 fn subtraction_is_correct() {
687 let (x, y) = get_2_test_polynomial();
688
689 let field_cfg = test_config();
690
691 let res = DynamicPolynomialF::new(vec![
692 F::from_with_cfg(1i32, &field_cfg),
693 F::from_with_cfg(-2i32, &field_cfg),
694 F::from_with_cfg(-1i32, &field_cfg),
695 F::zero_with_cfg(&field_cfg),
696 F::zero_with_cfg(&field_cfg),
697 ]);
698
699 assert_eq!(x.clone() - &y, res);
700 assert_eq!(x.clone() - y.clone(), res);
701 assert_eq!(x.checked_sub(&y), Some(res.clone()));
702
703 let mut z = x.clone();
704 z -= y.clone();
705 assert_eq!(z, res);
706
707 let mut z = x.clone();
708 z -= &y;
709 assert_eq!(z, res);
710
711 let x = y;
712 let y = DynamicPolynomialF::new(vec![
713 F::from_with_cfg(2i32, &field_cfg),
714 F::from_with_cfg(0i32, &field_cfg),
715 F::from_with_cfg(2i32, &field_cfg),
716 F::from_with_cfg(-1i32, &field_cfg),
717 F::zero_with_cfg(&field_cfg),
718 ]);
719
720 let res = DynamicPolynomialF::new(vec![
721 F::from_with_cfg(-1i32, &field_cfg),
722 F::from_with_cfg(2i32, &field_cfg),
723 F::from_with_cfg(1i32, &field_cfg),
724 F::from_with_cfg(1i32, &field_cfg),
725 F::zero_with_cfg(&field_cfg),
726 ]);
727
728 assert_eq!(x.clone() - &y, res);
729 assert_eq!(x.clone() - y.clone(), res);
730 assert_eq!(x.checked_sub(&y), Some(res.clone()));
731
732 let mut z = x.clone();
733 z -= y.clone();
734 assert_eq!(z, res);
735
736 let mut z = x.clone();
737 z -= &y;
738 assert_eq!(z, res);
739 }
740
741 #[test]
742 fn mul_zero() {
743 assert_eq!(
744 DynamicPolynomialF::<F>::ZERO * DynamicPolynomialF::ZERO,
745 DynamicPolynomialF::ZERO
746 );
747
748 let field_cfg = test_config();
749
750 let x = DynamicPolynomialF::new(vec![
751 F::from_with_cfg(2i32, &field_cfg),
752 F::zero_with_cfg(&field_cfg),
753 F::from_with_cfg(2i32, &field_cfg),
754 F::zero_with_cfg(&field_cfg),
755 F::zero_with_cfg(&field_cfg),
756 ]);
757
758 assert_eq!(
759 x.clone() * &DynamicPolynomialF::ZERO,
760 DynamicPolynomialF::ZERO
761 );
762 assert_eq!(
763 DynamicPolynomialF::ZERO * x.clone(),
764 DynamicPolynomialF::ZERO
765 );
766 assert_eq!(DynamicPolynomialF::ZERO * &x, DynamicPolynomialF::ZERO);
767
768 let mut y = x.clone();
769
770 y *= DynamicPolynomialF::ZERO;
771
772 assert_eq!(y, DynamicPolynomialF::ZERO);
773
774 y *= &DynamicPolynomialF::ZERO;
775
776 assert_eq!(y, DynamicPolynomialF::ZERO);
777 }
778
779 #[test]
780 fn multiplication_is_correct() {
781 let (x, y) = get_2_test_polynomial();
782
783 let field_cfg = test_config();
784
785 let res = DynamicPolynomialF::new(vec![
786 F::from_with_cfg(2i32, &field_cfg),
787 F::from_with_cfg(4i32, &field_cfg),
788 F::from_with_cfg(8i32, &field_cfg),
789 F::from_with_cfg(4i32, &field_cfg),
790 F::from_with_cfg(6i32, &field_cfg),
791 F::zero_with_cfg(&field_cfg),
792 F::zero_with_cfg(&field_cfg),
793 ]);
794
795 assert_eq!(x.clone() * y.clone(), res);
796 assert_eq!(x.clone() * &y, res);
797 assert_eq!(&x * &y, res);
798 assert_eq!(y.clone() * x.clone(), res);
799 assert_eq!(y.clone() * &x, res);
800 assert_eq!(&y * &x, res);
801 assert_eq!(x.checked_mul(&y), Some(res.clone()));
802 assert_eq!(y.checked_mul(&x), Some(res.clone()));
803
804 let mut z = x.clone();
805 z *= y.clone();
806 assert_eq!(z, res);
807
808 let mut z = x.clone();
809 z *= &y;
810 assert_eq!(z, res);
811
812 let mut z = y.clone();
813 z *= x.clone();
814 assert_eq!(z, res);
815
816 let mut z = y.clone();
817 z *= &x;
818 assert_eq!(z, res);
819 }
820
821 #[test]
822 fn test_trim() {
823 let field_cfg = test_config();
824
825 let mut x = DynamicPolynomialF::new([
826 F::zero_with_cfg(&field_cfg),
827 F::zero_with_cfg(&field_cfg),
828 F::zero_with_cfg(&field_cfg),
829 F::zero_with_cfg(&field_cfg),
830 F::zero_with_cfg(&field_cfg),
831 ]);
832 x.trim();
833 assert_eq!(x, DynamicPolynomialF::ZERO);
834
835 let mut x = DynamicPolynomialF::new([
836 F::from_with_cfg(2i32, &field_cfg),
837 F::from_with_cfg(3i32, &field_cfg),
838 F::zero_with_cfg(&field_cfg),
839 F::zero_with_cfg(&field_cfg),
840 F::zero_with_cfg(&field_cfg),
841 ]);
842 x.trim();
843 assert_eq!(
844 x,
845 DynamicPolynomialF::new([
846 F::from_with_cfg(2i32, &field_cfg),
847 F::from_with_cfg(3i32, &field_cfg),
848 ])
849 );
850 }
851
852 #[test]
853 fn evaluate_zero_poly() {
854 assert_eq!(
855 DynamicPolynomialF::<F>::ZERO.evaluate_at_point(&F::one_with_cfg(&test_config())),
856 Ok(F::zero_with_cfg(&test_config()))
857 )
858 }
859
860 fn f(v: u64) -> F {
861 F::from_with_cfg(v, &test_config())
862 }
863
864 #[test]
865 fn rotate_right_pads_and_permutes_coefficients() {
866 let field_cfg = test_config();
867 let poly = DynamicPolynomialF::new([f(1), f(2), f(3)]);
868
869 assert_eq!(
870 poly.rotate_right::<5>(2, &field_cfg),
871 DynamicPolynomialF::new([f(3), f(0), f(0), f(1), f(2)])
872 );
873 }
874
875 #[test]
876 fn shr_pads_and_drops_coefficients() {
877 let field_cfg = test_config();
878 let poly = DynamicPolynomialF::new([f(1), f(2), f(3)]);
879
880 assert_eq!(
881 poly.shr::<5>(2, &field_cfg),
882 DynamicPolynomialF::new([f(3), f(0), f(0), f(0), f(0)])
883 );
884 }
885
886 #[test]
887 #[should_panic(expected = "rotate_right count 0 out of range")]
888 fn rotate_right_panics_on_zero() {
889 let field_cfg = test_config();
890 let _ = DynamicPolynomialF::new([f(1)]).rotate_right::<5>(0, &field_cfg);
891 }
892
893 #[test]
894 #[should_panic(expected = "rotate_right count 5 out of range")]
895 fn rotate_right_panics_on_full_width() {
896 let field_cfg = test_config();
897 let _ = DynamicPolynomialF::new([f(1)]).rotate_right::<5>(5, &field_cfg);
898 }
899
900 #[test]
901 #[should_panic(expected = "shr count 0 out of range")]
902 fn shr_panics_on_zero() {
903 let field_cfg = test_config();
904 let _ = DynamicPolynomialF::new([f(1)]).shr::<5>(0, &field_cfg);
905 }
906
907 #[test]
908 #[should_panic(expected = "shr count 5 out of range")]
909 fn shr_panics_on_full_width() {
910 let field_cfg = test_config();
911 let _ = DynamicPolynomialF::new([f(1)]).shr::<5>(5, &field_cfg);
912 }
913
914 #[test]
915 #[should_panic(expected = "division by zero polynomial")]
916 fn test_div_rem_zero_divisor() {
917 let dividend = DynamicPolynomialF {
918 coeffs: vec![f(1), f(1)],
919 };
920 let divisor = DynamicPolynomialF {
921 coeffs: vec![f(0), f(0)],
922 };
923
924 let _ = dividend.div_rem_euclid(&divisor);
925 }
926
927 #[test]
928 fn test_div_euclid_and_rem_euclid() {
929 let dividend = DynamicPolynomialF {
930 coeffs: vec![f(1), f(2), f(1)],
931 };
932 let divisor = DynamicPolynomialF {
933 coeffs: vec![f(1), f(1)],
934 };
935
936 let (q, r) = dividend.div_rem_euclid(&divisor);
937 let q2 = dividend.div_euclid(&divisor);
938 let r2 = dividend.rem_euclid(&divisor);
939
940 assert_eq!(q, q2);
941 assert_eq!(r, r2);
942 }
943
944 fn any_f() -> impl Strategy<Value = F> {
945 let cfg = test_config();
946 any::<u64>().prop_map(move |v| F::from_with_cfg(v, &cfg))
947 }
948
949 fn any_poly(max_degree: usize) -> impl Strategy<Value = DynamicPolynomialF<F>> {
950 let cfg = test_config();
951 (0usize..=max_degree).prop_flat_map(move |degree| {
952 let cfg = cfg;
953 prop::collection::vec(any_f(), degree + 1).prop_map(move |mut coeffs| {
954 let mut rng = rand::rngs::ThreadRng::default();
955 let zero = F::from_with_cfg(0, &cfg);
956 while coeffs[degree] == zero {
957 let val: u64 = rng.random();
958 coeffs[degree] = F::from_with_cfg(val, &cfg);
959 }
960 DynamicPolynomialF { coeffs }
961 })
962 })
963 }
964
965 fn sparse_poly(max_degree: usize) -> impl Strategy<Value = DynamicPolynomialF<F>> {
966 let cfg = test_config();
967 (1usize..=max_degree).prop_flat_map(move |degree| {
968 let cfg = cfg;
969 let num_nonzero = std::cmp::min(3, degree);
970 (
971 prop::collection::vec(0usize..degree, num_nonzero),
972 prop::collection::vec(any_f(), num_nonzero + 1),
973 )
974 .prop_map(move |(positions, values)| {
975 let mut rng = rand::rngs::ThreadRng::default();
976 let zero = F::from_with_cfg(0, &cfg);
977 let mut coeffs = vec![zero.clone(); degree + 1];
978 positions
979 .iter()
980 .zip(values.iter())
981 .for_each(|(p, v)| coeffs[*p] = v.clone());
982 coeffs[degree] = values.last().unwrap().clone();
983 while coeffs[degree] == zero {
984 let val: u64 = rng.random();
985 coeffs[degree] = F::from_with_cfg(val, &cfg);
986 }
987 DynamicPolynomialF { coeffs }
988 })
989 })
990 }
991
992 fn small_coeff_poly(max_degree: usize) -> impl Strategy<Value = DynamicPolynomialF<F>> {
993 let cfg = test_config();
994 (0usize..=max_degree).prop_flat_map(move |degree| {
995 let cfg = cfg;
996 prop::collection::vec(0u64..=2, degree + 1).prop_map(move |raw| {
997 let mut rng = rand::rngs::ThreadRng::default();
998 let zero = F::from_with_cfg(0, &cfg);
999 let mut coeffs: Vec<F> = raw.iter().map(|&v| F::from_with_cfg(v, &cfg)).collect();
1000 while coeffs[degree] == zero {
1001 let val: u64 = rng.random::<u64>() % 2 + 1;
1002 coeffs[degree] = F::from_with_cfg(val, &cfg);
1003 }
1004 DynamicPolynomialF { coeffs }
1005 })
1006 })
1007 }
1008
1009 fn any_poly_varied(max_degree: usize) -> impl Strategy<Value = DynamicPolynomialF<F>> {
1010 prop_oneof![
1011 2 => any_poly(max_degree),
1012 1 => sparse_poly(max_degree),
1013 1 => small_coeff_poly(max_degree),
1014 ]
1015 }
1016
1017 proptest! {
1018 #![proptest_config(ProptestConfig::with_cases(500))]
1019
1020 #[test]
1021 #[cfg_attr(miri, ignore)] fn prop_div_rem_random(
1023 dividend in any_poly_varied(50),
1024 divisor in any_poly_varied(50)
1025 ) {
1026 let (quotient, remainder) = dividend.div_rem_euclid(&divisor);
1027
1028 let product = "ient * &divisor;
1029 let reconstructed = product + &remainder;
1030
1031 let mut dividend_trimmed = dividend.clone();
1032 let mut reconstructed_trimmed = reconstructed;
1033 dividend_trimmed.trim();
1034 reconstructed_trimmed.trim();
1035
1036 prop_assert_eq!(dividend_trimmed, reconstructed_trimmed);
1037 }
1038
1039 #[test]
1040 #[cfg_attr(miri, ignore)] fn prop_div_rem_exact_division(
1042 q in any_poly_varied(25),
1043 d in any_poly_varied(25)
1044 ) {
1045 let dividend = &q * &d;
1046 let (quotient, remainder) = dividend.div_rem_euclid(&d);
1047
1048 let mut remainder_trimmed = remainder;
1049 let mut quotient_trimmed = quotient;
1050 remainder_trimmed.trim();
1051 quotient_trimmed.trim();
1052
1053 let mut q_trimmed = q.clone();
1054 q_trimmed.trim();
1055
1056 prop_assert_eq!(remainder_trimmed, DynamicPolynomialF::ZERO);
1057 prop_assert_eq!(quotient_trimmed, q_trimmed);
1058 }
1059 }
1060}