1use crypto_primitives::{
2 FieldConfig, ProjectElementWithConfig, RingConfig, SemiringConfig, SetConfig, boolean::Boolean,
3};
4use derive_more::From;
5use itertools::Itertools;
6use std::fmt::Display;
7use zinc_transcript::traits::{ConstTranscribable, GenTranscribable, Transcribable};
8use zinc_utils::{add, mul, projectable_to_field::ProjectableToField, rem};
9
10use crate::{
11 EvaluationError,
12 univariate::{binary::BinaryPoly, dense::DensePolynomial},
13};
14
15#[allow(clippy::arithmetic_side_effects)]
16fn new_coeffs_trimmed<R: Clone>(coeffs: &[R], is_zero: impl Fn(&R) -> bool) -> Vec<R> {
17 if let Some((non_zero, _)) = coeffs.iter().rev().find_position(|&coeff| !is_zero(coeff)) {
18 let deg_plus_one = coeffs.len() - non_zero;
19
20 coeffs.iter().take(deg_plus_one).cloned().collect()
21 } else {
22 Vec::new()
23 }
24}
25
26#[allow(clippy::arithmetic_side_effects)]
27fn degree<R>(coeffs: &[R], is_zero: impl Fn(&R) -> bool) -> Option<usize> {
28 coeffs
29 .iter()
30 .rev()
31 .find_position(|coeff| !is_zero(coeff))
32 .map(|(non_zero, _)| coeffs.len() - non_zero - 1)
33}
34
35#[allow(clippy::arithmetic_side_effects)]
36fn trim<R>(coeffs: &mut Vec<R>, is_zero: impl Fn(&R) -> bool) {
37 coeffs.truncate(degree(coeffs, is_zero).map_or(0, |degree| degree + 1))
38}
39
40fn is_zero<R>(coeffs: &[R], is_zero: impl Fn(&R) -> bool) -> bool {
41 coeffs.iter().all(is_zero)
42}
43
44#[derive(Debug, Clone, From, Hash, PartialEq, Eq)]
59pub struct DynamicPolynomial<E> {
60 pub coeffs: Vec<E>,
61}
62
63impl<E> DynamicPolynomial<E> {
64 pub const ZERO: Self = Self { coeffs: Vec::new() };
65
66 pub fn try_map<T, Er>(
70 &self,
71 f: impl FnMut(&E) -> Result<T, Er> + Copy,
72 ) -> Result<DynamicPolynomial<T>, Er> {
73 Ok(DynamicPolynomial {
74 coeffs: self.coeffs.iter().map(f).try_collect()?,
75 })
76 }
77
78 #[inline(always)]
80 pub fn new(coeffs: impl AsRef<[E]>) -> Self
81 where
82 E: Clone,
83 {
84 Self {
85 coeffs: Vec::from(coeffs.as_ref()),
86 }
87 }
88}
89
90impl<E> Default for DynamicPolynomial<E> {
91 fn default() -> Self {
92 Self::ZERO
93 }
94}
95
96impl<E> FromIterator<E> for DynamicPolynomial<E> {
97 #[inline(always)]
98 fn from_iter<T: IntoIterator<Item = E>>(iter: T) -> Self {
99 Self {
100 coeffs: iter.into_iter().collect(),
101 }
102 }
103}
104
105impl<E: Display> Display for DynamicPolynomial<E> {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 write!(f, "[")?;
108 let mut first = true;
109
110 for coeff in self.coeffs.iter() {
111 if first {
112 first = false;
113 } else {
114 write!(f, ", ")?;
115 }
116 write!(f, "{}", coeff)?;
117 }
118
119 write!(f, "]")
120 }
121}
122
123impl<E, const DEGREE_PLUS_ONE: usize> From<DensePolynomial<E, DEGREE_PLUS_ONE>>
124 for DynamicPolynomial<E>
125{
126 fn from(dense_poly: DensePolynomial<E, DEGREE_PLUS_ONE>) -> Self {
127 Self {
128 coeffs: Vec::from(dense_poly.coeffs),
129 }
130 }
131}
132
133impl<const DEGREE_PLUS_ONE: usize> From<BinaryPoly<DEGREE_PLUS_ONE>>
134 for DynamicPolynomial<Boolean>
135{
136 fn from(binary_poly: BinaryPoly<DEGREE_PLUS_ONE>) -> Self {
137 Self::from(DensePolynomial::from(binary_poly))
138 }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
152pub struct DynamicPolynomialConfig<'a, S: SemiringConfig> {
153 pub cfg: &'a S,
154}
155
156pub trait HasDynamicPolynomialConfig: SemiringConfig + Sized {
160 #[inline(always)]
161 fn dyn_poly_cfg(&self) -> DynamicPolynomialConfig<'_, Self> {
162 DynamicPolynomialConfig { cfg: self }
163 }
164}
165
166impl<S: SemiringConfig> HasDynamicPolynomialConfig for S {}
167
168impl<'a, S: SemiringConfig> SetConfig for DynamicPolynomialConfig<'a, S> {
169 type Element = DynamicPolynomial<S::Element>;
170}
171
172impl<'a, S: SemiringConfig> SemiringConfig for DynamicPolynomialConfig<'a, S> {
173 fn is_zero(&self, value: &Self::Element) -> bool {
174 is_zero(&value.coeffs, |e| self.cfg.is_zero(e))
175 }
176
177 fn zero(&self) -> Self::Element {
178 DynamicPolynomial::ZERO
179 }
180
181 fn one(&self) -> Self::Element {
182 DynamicPolynomial {
183 coeffs: vec![self.cfg.one()],
184 }
185 }
186
187 fn add(&self, x: &Self::Element, y: &Self::Element) -> Self::Element {
188 let mut res = x.clone();
189 self.add_assign(&mut res, y);
190 res
191 }
192
193 fn sub(&self, x: &Self::Element, y: &Self::Element) -> Self::Element {
194 let mut res = x.clone();
195 self.sub_assign(&mut res, y);
196 res
197 }
198
199 fn mul(&self, x: &Self::Element, y: &Self::Element) -> Self::Element {
200 if self.is_zero(x) || self.is_zero(y) {
201 return self.zero();
202 }
203 let mut coeffs =
204 vec![self.cfg.zero(); add!(x.coeffs.len(), y.coeffs.len()).saturating_sub(1)];
205 for (i, a) in x.coeffs.iter().enumerate() {
206 for (j, b) in y.coeffs.iter().enumerate() {
207 let prod = self.cfg.mul(a, b);
208 self.cfg.add_assign(&mut coeffs[add!(i, j)], &prod);
209 }
210 }
211 DynamicPolynomial { coeffs }
212 }
213
214 fn pow_u32(&self, x: &Self::Element, y: u32) -> Self::Element {
215 let mut res = self.one();
216 for _ in 0..y {
217 res = self.mul(&res, x);
218 }
219 res
220 }
221
222 fn checked_add(&self, x: &Self::Element, y: &Self::Element) -> Option<Self::Element> {
223 let mut res = x.clone();
224 if res.coeffs.len() < y.coeffs.len() {
225 res.coeffs.resize(y.coeffs.len(), self.cfg.zero());
226 }
227 for (xc, yc) in res.coeffs.iter_mut().zip(&y.coeffs) {
228 *xc = self.cfg.checked_add(xc, yc)?;
229 }
230 Some(res)
231 }
232
233 fn checked_sub(&self, x: &Self::Element, y: &Self::Element) -> Option<Self::Element> {
234 let mut res = x.clone();
235 if res.coeffs.len() < y.coeffs.len() {
236 res.coeffs.resize(y.coeffs.len(), self.cfg.zero());
237 }
238 for (xc, yc) in res.coeffs.iter_mut().zip(&y.coeffs) {
239 *xc = self.cfg.checked_sub(xc, yc)?;
240 }
241 Some(res)
242 }
243
244 fn checked_mul(&self, x: &Self::Element, y: &Self::Element) -> Option<Self::Element> {
245 if self.is_zero(x) || self.is_zero(y) {
246 return Some(self.zero());
247 }
248 let mut coeffs =
249 vec![self.cfg.zero(); add!(x.coeffs.len(), y.coeffs.len()).saturating_sub(1)];
250 for (i, a) in x.coeffs.iter().enumerate() {
251 for (j, b) in y.coeffs.iter().enumerate() {
252 let prod = self.cfg.checked_mul(a, b)?;
253 let idx = add!(i, j);
254 coeffs[idx] = self.cfg.checked_add(&coeffs[idx], &prod)?;
255 }
256 }
257 Some(DynamicPolynomial { coeffs })
258 }
259
260 fn checked_pow_u32(&self, x: &Self::Element, y: u32) -> Option<Self::Element> {
261 let mut res = self.one();
262 for _ in 0..y {
263 res = self.checked_mul(&res, x)?;
264 }
265 Some(res)
266 }
267
268 fn add_assign(&self, x: &mut Self::Element, y: &Self::Element) {
269 if x.coeffs.len() < y.coeffs.len() {
270 x.coeffs.resize(y.coeffs.len(), self.cfg.zero());
271 }
272 for (xc, yc) in x.coeffs.iter_mut().zip(&y.coeffs) {
273 self.cfg.add_assign(xc, yc);
274 }
275 }
276
277 fn sub_assign(&self, x: &mut Self::Element, y: &Self::Element) {
278 if x.coeffs.len() < y.coeffs.len() {
279 x.coeffs.resize(y.coeffs.len(), self.cfg.zero());
280 }
281 for (xc, yc) in x.coeffs.iter_mut().zip(&y.coeffs) {
282 self.cfg.sub_assign(xc, yc);
283 }
284 }
285}
286
287impl<'a, S: RingConfig> RingConfig for DynamicPolynomialConfig<'a, S> {
288 fn neg(&self, x: &Self::Element) -> Self::Element {
289 DynamicPolynomial {
290 coeffs: x.coeffs.iter().map(|c| self.cfg.neg(c)).collect(),
291 }
292 }
293
294 fn checked_neg(&self, x: &Self::Element) -> Option<Self::Element> {
295 Some(DynamicPolynomial {
296 coeffs: x
297 .coeffs
298 .iter()
299 .map(|c| self.cfg.checked_neg(c))
300 .collect::<Option<Vec<_>>>()?,
301 })
302 }
303}
304
305impl<'a, S: SemiringConfig> DynamicPolynomialConfig<'a, S> {
306 #[inline(always)]
309 pub fn new_trimmed(&self, coeffs: impl AsRef<[S::Element]>) -> DynamicPolynomial<S::Element> {
310 DynamicPolynomial {
311 coeffs: new_coeffs_trimmed(coeffs.as_ref(), |e| self.cfg.is_zero(e)),
312 }
313 }
314
315 #[inline(always)]
316 pub fn degree(&self, poly: &DynamicPolynomial<S::Element>) -> Option<usize> {
317 degree(&poly.coeffs, |e| self.cfg.is_zero(e))
318 }
319
320 #[inline(always)]
321 pub fn trim(&self, poly: &mut DynamicPolynomial<S::Element>) {
322 trim(&mut poly.coeffs, |e| self.cfg.is_zero(e));
323 }
324
325 pub fn evaluate_at_point(
327 &self,
328 poly: &DynamicPolynomial<S::Element>,
329 point: &S::Element,
330 ) -> Result<S::Element, EvaluationError> {
331 let mut result = poly.coeffs.last().cloned().unwrap_or(self.cfg.zero());
332
333 for coeff in poly.coeffs.iter().rev().skip(1) {
334 self.cfg.mul_assign(&mut result, point);
335 self.cfg.add_assign(&mut result, coeff);
336 }
337
338 Ok(result)
339 }
340
341 pub fn checked_evaluate_at_point(
344 &self,
345 poly: &DynamicPolynomial<S::Element>,
346 point: &S::Element,
347 ) -> Result<S::Element, EvaluationError> {
348 let mut result = poly.coeffs.last().cloned().unwrap_or(self.cfg.zero());
349
350 for coeff in poly.coeffs.iter().rev().skip(1) {
351 let term = self
352 .cfg
353 .checked_mul(&result, point)
354 .ok_or(EvaluationError::Overflow)?;
355 result = self
356 .cfg
357 .checked_add(&term, coeff)
358 .ok_or(EvaluationError::Overflow)?;
359 }
360
361 Ok(result)
362 }
363
364 pub fn rotate_right<const D: usize>(
370 &self,
371 poly: &DynamicPolynomial<S::Element>,
372 c: usize,
373 ) -> DynamicPolynomial<S::Element> {
374 assert!(
375 c > 0 && c < D,
376 "rotate_right count {c} out of range (must satisfy 0 < c < {D})",
377 );
378 assert!(
379 poly.coeffs.len() <= D,
380 "rotate_right coefficient length {} exceeds width {D}",
381 poly.coeffs.len(),
382 );
383
384 let mut coeffs = poly.coeffs.clone();
385 coeffs.resize(D, self.cfg.zero());
386 DynamicPolynomial {
387 coeffs: (0..D)
388 .map(|i| coeffs[rem!(add!(i, c), D)].clone())
389 .collect(),
390 }
391 }
392
393 pub fn shr<const D: usize>(
399 &self,
400 poly: &DynamicPolynomial<S::Element>,
401 c: usize,
402 ) -> DynamicPolynomial<S::Element> {
403 assert!(
404 c > 0 && c < D,
405 "shr count {c} out of range (must satisfy 0 < c < {D})",
406 );
407 assert!(
408 poly.coeffs.len() <= D,
409 "shr coefficient length {} exceeds width {D}",
410 poly.coeffs.len(),
411 );
412
413 let zero = self.cfg.zero();
414 let mut coeffs = poly.coeffs.clone();
415 coeffs.resize(D, zero.clone());
416 DynamicPolynomial {
417 coeffs: (0..D)
418 .map(|i| {
419 let j = add!(i, c);
420 if j < D {
421 coeffs[j].clone()
422 } else {
423 zero.clone()
424 }
425 })
426 .collect(),
427 }
428 }
429}
430
431impl<E, C> ProjectableToField<C> for DynamicPolynomial<E>
435where
436 C: FieldConfig + ProjectElementWithConfig<E> + Clone + Send + Sync + 'static,
437{
438 fn prepare_projection(cfg: &C, sampled_value: &C::Element) -> impl Fn(&Self) -> C::Element {
439 let cfg = cfg.clone();
440 let sampled_value = sampled_value.clone();
441
442 move |poly: &Self| {
443 let mut result = cfg.zero();
445 for coeff in poly.coeffs.iter().rev() {
446 cfg.mul_assign(&mut result, &sampled_value);
447 let projected = cfg.project(coeff);
448 cfg.add_assign(&mut result, &projected);
449 }
450 result
451 }
452 }
453}
454
455#[derive(Debug, Default, Clone, From, Hash, PartialEq, Eq)]
464#[repr(transparent)]
465pub struct DynamicPolyVec<E>(pub Vec<DynamicPolynomial<E>>);
466
467impl<E> DynamicPolyVec<E> {
468 pub fn reinterpret(value: &Vec<DynamicPolynomial<E>>) -> &Self {
469 unsafe { &*(value as *const Vec<DynamicPolynomial<E>> as *const Self) }
472 }
473}
474
475impl<E> GenTranscribable for DynamicPolyVec<E>
476where
477 E: ConstTranscribable,
478{
479 fn read_transcription_bytes_exact(mut bytes: &[u8]) -> Self {
480 let mut result = Vec::new();
481 while !bytes.is_empty() {
482 let (len, rest) = u32::read_transcription_bytes_subset(bytes);
483 let len = usize::try_from(len).expect("polynomial length must fit into usize");
484 bytes = rest;
485 let end = mul!(len, E::NUM_BYTES);
486 let coeffs: Vec<E> = Vec::read_transcription_bytes_exact(&bytes[..end]);
487 result.push(DynamicPolynomial { coeffs });
488 bytes = &bytes[end..];
489 }
490 result.into()
491 }
492
493 fn write_transcription_bytes_exact(&self, mut buf: &mut [u8]) {
494 for poly in self.0.iter() {
495 let len = u32::try_from(poly.coeffs.len()).expect("poly length must fit into u32");
496 len.write_transcription_bytes_exact(&mut buf[0..u32::NUM_BYTES]);
497 buf = &mut buf[u32::NUM_BYTES..];
498
499 let end = mul!(poly.coeffs.len(), E::NUM_BYTES);
500 poly.coeffs.write_transcription_bytes_exact(&mut buf[..end]);
501 buf = &mut buf[end..];
502 }
503 assert!(buf.is_empty(), "Entire buffer should be used");
504 }
505}
506
507impl<E> Transcribable for DynamicPolyVec<E>
508where
509 E: ConstTranscribable,
510{
511 fn get_num_bytes(&self) -> usize {
512 self.0
513 .iter()
514 .map(|poly| add!(u32::NUM_BYTES, mul!(poly.coeffs.len(), E::NUM_BYTES)))
515 .sum()
516 }
517}
518
519#[cfg(test)]
520#[allow(
521 clippy::arithmetic_side_effects,
522 clippy::clone_on_copy,
523 clippy::redundant_clone
524)]
525mod field_tests {
526 use crypto_primitives::{
527 BaseFieldConfig, ProjectElementWithConfig,
528 crypto_bigint_monty::{MontyField, MontyFieldElement},
529 crypto_bigint_uint::Uint,
530 };
531
532 use super::*;
533
534 const LIMBS: usize = 4;
535 type F = MontyField<LIMBS>;
536 type P = DynamicPolynomial<MontyFieldElement<LIMBS>>;
537
538 fn field_config() -> F {
539 let modulus =
540 Uint::from_be_hex("0000000000000000000000000000000000860995AE68FC80E1B1BD1E39D54B33");
541 F::new(&modulus).expect("modulus should be a valid odd prime")
542 }
543
544 fn f(v: i64) -> MontyFieldElement<LIMBS> {
545 field_config().project(&v)
546 }
547
548 fn p(coeffs: impl IntoIterator<Item = i64>) -> P {
549 coeffs.into_iter().map(f).collect()
550 }
551
552 #[test]
553 fn new_trimmed_creates_correctly() {
554 let field_cfg = field_config();
555 let cfg = field_cfg.dyn_poly_cfg();
556 assert_eq!(cfg.new_trimmed(p([1, 2, 3, 0, 0]).coeffs), p([1, 2, 3]));
557 }
558
559 #[test]
560 fn add_zero() {
561 let field_cfg = field_config();
562 let cfg = field_cfg.dyn_poly_cfg();
563
564 assert_eq!(cfg.add(&P::ZERO, &P::ZERO), P::ZERO);
565
566 let x = p([2, 0, 2, 0, 0]);
567 assert_eq!(cfg.add(&x, &P::ZERO), x);
568 assert_eq!(cfg.add(&P::ZERO, &x), x);
569
570 let mut y = x.clone();
571 cfg.add_assign(&mut y, &P::ZERO);
572 assert_eq!(y, x);
573 }
574
575 #[test]
576 fn addition_is_correct() {
577 let field_cfg = field_config();
578 let cfg = field_cfg.dyn_poly_cfg();
579 let (x, y) = (p([2, 0, 2, 0, 0]), p([1, 2, 3]));
580
581 let res = p([3, 2, 5, 0, 0]);
582
583 assert_eq!(cfg.add(&x, &y), res);
584 assert_eq!(cfg.add(&y, &x), res);
585 assert_eq!(cfg.checked_add(&x, &y), Some(res.clone()));
586
587 let mut z = x.clone();
588 cfg.add_assign(&mut z, &y);
589 assert_eq!(z, res);
590 }
591
592 #[test]
593 fn subtraction_is_correct() {
594 let field_cfg = field_config();
595 let cfg = field_cfg.dyn_poly_cfg();
596 let (x, y) = (p([2, 0, 2, 0, 0]), p([1, 2, 3]));
597
598 let res = p([1, -2, -1, 0, 0]);
599
600 assert_eq!(cfg.sub(&x, &y), res);
601 assert_eq!(cfg.checked_sub(&x, &y), Some(res.clone()));
602
603 let mut z = x.clone();
604 cfg.sub_assign(&mut z, &y);
605 assert_eq!(z, res);
606
607 assert_eq!(
609 cfg.sub(&p([1, 2, 3]), &p([2, 0, 2, -1, 0])),
610 p([-1, 2, 1, 1, 0])
611 );
612 }
613
614 #[test]
615 fn multiplication_is_correct() {
616 let field_cfg = field_config();
617 let cfg = field_cfg.dyn_poly_cfg();
618 let (x, y) = (p([2, 0, 2]), p([1, 2, 3]));
619
620 let res = p([2, 4, 8, 4, 6]);
621
622 assert_eq!(cfg.mul(&x, &y), res);
623 assert_eq!(cfg.mul(&y, &x), res);
624 assert_eq!(cfg.checked_mul(&x, &y), Some(res));
625
626 assert_eq!(cfg.mul(&x, &cfg.zero()), cfg.zero());
627 assert_eq!(cfg.mul(&cfg.zero(), &x), cfg.zero());
628 }
629
630 #[test]
631 fn test_trim() {
632 let field_cfg = field_config();
633 let cfg = field_cfg.dyn_poly_cfg();
634
635 let mut x = p([0, 0, 0, 0, 0]);
636 cfg.trim(&mut x);
637 assert_eq!(x, P::ZERO);
638
639 let mut x = p([2, 3, 0, 0, 0]);
640 cfg.trim(&mut x);
641 assert_eq!(x, p([2, 3]));
642 }
643
644 #[test]
645 fn evaluate_zero_poly() {
646 let field_cfg = field_config();
647 let cfg = field_cfg.dyn_poly_cfg();
648 assert_eq!(cfg.evaluate_at_point(&P::ZERO, &f(1)), Ok(f(0)))
649 }
650
651 #[test]
652 fn evaluation_is_correct() {
653 let field_cfg = field_config();
654 let cfg = field_cfg.dyn_poly_cfg();
655 assert_eq!(cfg.evaluate_at_point(&p([1, 2, 3]), &f(2)), Ok(f(17)));
657 }
658
659 #[test]
660 fn projection_evaluates_at_sampled_point() {
661 use crypto_primitives::crypto_bigint_int::Int;
662 type IntPoly = DynamicPolynomial<Int<4>>;
663
664 let field_cfg = field_config();
665 let sampled = f(2);
666 let project = IntPoly::prepare_projection(&field_cfg, &sampled);
667 let poly: IntPoly = [1i8, 2, 3].map(Int::from_i8).into_iter().collect();
669 assert_eq!(project(&poly), f(17));
670 assert_eq!(project(&IntPoly::ZERO), f(0));
671 }
672
673 #[test]
674 fn rotate_right_pads_and_permutes_coefficients() {
675 let field_cfg = field_config();
676 let cfg = field_cfg.dyn_poly_cfg();
677 assert_eq!(cfg.rotate_right::<5>(&p([1, 2, 3]), 2), p([3, 0, 0, 1, 2]));
678 }
679
680 #[test]
681 fn shr_pads_and_drops_coefficients() {
682 let field_cfg = field_config();
683 let cfg = field_cfg.dyn_poly_cfg();
684 assert_eq!(cfg.shr::<5>(&p([1, 2, 3]), 2), p([3, 0, 0, 0, 0]));
685 }
686
687 #[test]
688 #[should_panic(expected = "rotate_right count 0 out of range")]
689 fn rotate_right_panics_on_zero() {
690 let field_cfg = field_config();
691 let _ = field_cfg.dyn_poly_cfg().rotate_right::<5>(&p([1]), 0);
692 }
693
694 #[test]
695 #[should_panic(expected = "shr count 5 out of range")]
696 fn shr_panics_on_full_width() {
697 let field_cfg = field_config();
698 let _ = field_cfg.dyn_poly_cfg().shr::<5>(&p([1]), 5);
699 }
700}
701
702#[cfg(test)]
703#[allow(clippy::clone_on_copy, clippy::redundant_clone)]
704mod semiring_tests {
705 use crypto_primitives::{FixedConfig, crypto_bigint_int::Int};
706 use num_traits::ConstZero;
707
708 use super::*;
709
710 type R = Int<4>;
711 type P = DynamicPolynomial<R>;
712 type FC = FixedConfig<R>;
713
714 fn p(coeffs: impl IntoIterator<Item = i8>) -> P {
715 coeffs.into_iter().map(Int::from_i8).collect()
716 }
717
718 fn get_2_test_polynomials() -> (P, P) {
719 (p([2, 0, 2, 0, 0]), p([1, 2, 3]))
720 }
721
722 #[test]
723 fn new_trimmed_creates_correctly() {
724 let int_cfg = FC::default();
725 let cfg = int_cfg.dyn_poly_cfg();
726 assert_eq!(cfg.new_trimmed(p([1, 2, 3, 0, 0]).coeffs), p([1, 2, 3]));
727 }
728
729 #[test]
730 fn add_zero() {
731 let int_cfg = FC::default();
732 let cfg = int_cfg.dyn_poly_cfg();
733
734 assert_eq!(cfg.add(&P::ZERO, &P::ZERO), P::ZERO);
735
736 let x = p([2, 0, 2, 0, 0]);
737 assert_eq!(cfg.add(&x, &P::ZERO), x);
738 assert_eq!(cfg.add(&P::ZERO, &x), x);
739
740 let mut y = x.clone();
741 cfg.add_assign(&mut y, &P::ZERO);
742 assert_eq!(y, x);
743 }
744
745 #[test]
746 fn addition_is_correct() {
747 let int_cfg = FC::default();
748 let cfg = int_cfg.dyn_poly_cfg();
749 let (x, y) = get_2_test_polynomials();
750
751 let res = p([3, 2, 5, 0, 0]);
752
753 assert_eq!(cfg.add(&x, &y), res);
754 assert_eq!(cfg.add(&y, &x), res);
755 assert_eq!(cfg.checked_add(&x, &y), Some(res.clone()));
756 assert_eq!(cfg.checked_add(&y, &x), Some(res.clone()));
757
758 let mut z = x.clone();
759 cfg.add_assign(&mut z, &y);
760 assert_eq!(z, res);
761
762 let mut z = y.clone();
763 cfg.add_assign(&mut z, &x);
764 assert_eq!(z, res);
765 }
766
767 #[test]
768 fn subtraction_is_correct() {
769 let int_cfg = FC::default();
770 let cfg = int_cfg.dyn_poly_cfg();
771 let (x, y) = get_2_test_polynomials();
772
773 let res = p([1, -2, -1, 0, 0]);
774
775 assert_eq!(cfg.sub(&x, &y), res);
776 assert_eq!(cfg.checked_sub(&x, &y), Some(res.clone()));
777
778 let mut z = x.clone();
779 cfg.sub_assign(&mut z, &y);
780 assert_eq!(z, res);
781
782 assert_eq!(
784 cfg.sub(&p([1, 2, 3]), &p([2, 0, 2, -1, 0])),
785 p([-1, 2, 1, 1, 0])
786 );
787 }
788
789 #[test]
790 fn mul_zero() {
791 let int_cfg = FC::default();
792 let cfg = int_cfg.dyn_poly_cfg();
793
794 assert_eq!(cfg.mul(&P::ZERO, &P::ZERO), P::ZERO);
795
796 let x = p([2, 0, 2, 0, 0]);
797 assert_eq!(cfg.mul(&x, &P::ZERO), P::ZERO);
798 assert_eq!(cfg.mul(&P::ZERO, &x), P::ZERO);
799 assert_eq!(cfg.checked_mul(&x, &P::ZERO), Some(P::ZERO));
800 }
801
802 #[test]
803 fn multiplication_is_correct() {
804 let int_cfg = FC::default();
805 let cfg = int_cfg.dyn_poly_cfg();
806 let (x, y) = get_2_test_polynomials();
807
808 let res = p([2, 4, 8, 4, 6, 0, 0]);
810
811 assert_eq!(cfg.mul(&x, &y), res);
812 assert_eq!(cfg.mul(&y, &x), res);
813 assert_eq!(cfg.checked_mul(&x, &y), Some(res.clone()));
814 assert_eq!(cfg.checked_mul(&y, &x), Some(res));
815 }
816
817 #[test]
818 fn negation_is_correct() {
819 let int_cfg = FC::default();
820 let cfg = int_cfg.dyn_poly_cfg();
821 let x = p([1, -2, 3]);
822
823 assert_eq!(cfg.neg(&x), p([-1, 2, -3]));
824 assert_eq!(cfg.checked_neg(&x), Some(p([-1, 2, -3])));
825 }
826
827 #[test]
828 fn checked_ops_detect_coefficient_overflow() {
829 let int_cfg = FC::default();
830 let cfg = int_cfg.dyn_poly_cfg();
831
832 let max = P::new([Int::MAX]);
833 let one = cfg.one();
834
835 assert_eq!(cfg.checked_add(&max, &one), None);
836 assert_eq!(cfg.checked_mul(&max, &p([2])), None);
837
838 let min = P::new([Int::MIN]);
839 assert_eq!(cfg.checked_sub(&min, &one), None);
840 assert_eq!(cfg.checked_neg(&min), None);
841 }
842
843 #[test]
844 fn checked_evaluation_detects_overflow() {
845 let int_cfg = FC::default();
846 let cfg = int_cfg.dyn_poly_cfg();
847
848 assert_eq!(
850 cfg.checked_evaluate_at_point(&p([1, 2, 3]), &Int::from_i8(2)),
851 Ok(Int::from_i8(17))
852 );
853 assert_eq!(
854 cfg.checked_evaluate_at_point(&P::new([Int::ZERO, Int::MAX]), &Int::from_i8(2)),
855 Err(EvaluationError::Overflow)
856 );
857 }
858
859 #[test]
860 fn test_trim() {
861 let int_cfg = FC::default();
862 let cfg = int_cfg.dyn_poly_cfg();
863
864 let mut x = p([0, 0, 0, 0, 0]);
865 cfg.trim(&mut x);
866 assert_eq!(x, P::ZERO);
867
868 let mut x = p([2, 3, 0, 0, 0]);
869 cfg.trim(&mut x);
870 assert_eq!(x, p([2, 3]));
871 }
872}