1use crate::ring::zq::Zq;
22use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
23use rand::distr::{Distribution, Uniform};
24use rand::{CryptoRng, Rng};
25use std::iter::Sum;
26
27#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct Rq<const D: usize> {
31 coeffs: [Zq; D],
32}
33
34impl<const D: usize> Rq<D> {
35 pub const fn new(coeffs: [Zq; D]) -> Self {
37 Rq { coeffs }
38 }
39 pub fn get_coefficients(&self) -> &[Zq; D] {
41 &self.coeffs
42 }
43
44 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, Zq> {
45 self.coeffs.iter_mut()
46 }
47
48 fn addition(&self, other: &Self) -> Self {
50 let mut result = [Zq::ZERO; D];
51 for (r, (a, b)) in result
52 .iter_mut()
53 .zip(self.coeffs.iter().zip(other.coeffs.iter()))
54 {
55 *r = *a + *b;
56 }
57 Rq::new(result)
58 }
59
60 fn subtraction(&self, other: &Self) -> Self {
62 let mut result = [Zq::ZERO; D];
63 for (r, (a, b)) in result
64 .iter_mut()
65 .zip(self.coeffs.iter().zip(other.coeffs.iter()))
66 {
67 *r = *a - *b;
68 }
69 Rq::new(result)
70 }
71
72 fn multiplication(&self, other: &Self) -> Self {
74 let mut result = [Zq::ZERO; D];
75 let mut out_of_field = [Zq::ZERO; D];
76 for (i, &self_coeff) in self.coeffs.iter().enumerate() {
77 for (j, &other_coeff) in other.coeffs.iter().enumerate() {
78 if i + j < D {
79 result[i + j] += self_coeff * other_coeff;
80 } else {
81 out_of_field[(i + j) % D] += self_coeff * other_coeff;
82 }
83 }
84 }
85 for i in (0..D).rev() {
87 let m = i / D;
88 let r = i % D;
89 let sign = if (m + 1) % 2 == 0 { 1 } else { -1 };
90 if sign == 1 {
91 result[r] += out_of_field[i];
92 } else {
93 result[r] -= out_of_field[i];
94 }
95 }
96 Rq::new(result)
97 }
98
99 pub fn inner_product(&self, other: &Self) -> Zq {
101 self.coeffs
102 .iter()
103 .zip(other.coeffs.iter())
104 .map(|(&a, &b)| a * b)
105 .fold(Zq::ZERO, |acc, x| acc + x)
106 }
107
108 pub fn scalar_mul(&self, s: Zq) -> Self {
110 let mut result = [Zq::ZERO; D];
111 for (i, &coeff) in self.coeffs.iter().enumerate() {
112 result[i] = s * (coeff);
113 }
114 Rq::new(result)
115 }
116
117 pub fn eval(&self, x: Zq) -> Zq {
119 let mut result = Zq::ZERO;
120 for coeff in self.coeffs.iter().rev() {
121 result = result * x + *coeff;
122 }
123
124 result
125 }
126
127 pub fn is_zero(&self) -> bool {
129 self.coeffs.iter().all(|&coeff| coeff == Zq::ZERO)
130 }
131
132 pub fn is_equal(&self, other: &Self) -> bool {
134 self.coeffs == other.coeffs
135 }
136
137 pub fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
139 let uniform = Uniform::new_inclusive(Zq::ZERO, Zq::MAX).unwrap();
140 let mut coeffs = [Zq::ZERO; D];
141 coeffs.iter_mut().for_each(|c| *c = uniform.sample(rng));
142 Self { coeffs }
143 }
144
145 pub fn random_ternary<R: Rng + CryptoRng>(rng: &mut R) -> Self {
147 let mut coeffs = [Zq::ZERO; D];
148
149 for coeff in coeffs.iter_mut() {
150 let val = match rng.random_range(0..3) {
152 0 => Zq::MAX, 1 => Zq::ZERO, 2 => Zq::ONE, _ => unreachable!(),
156 };
157 *coeff = val;
158 }
159
160 Rq::new(coeffs)
161 }
162
163 pub fn decompose(&self, base: Zq, num_parts: usize) -> Vec<Self> {
167 let mut parts = Vec::with_capacity(num_parts);
168 let mut current = self.clone();
169
170 for i in 0..num_parts {
171 if i == num_parts - 1 {
172 parts.push(current.clone());
173 } else {
174 let mut low_coeffs = [Zq::ZERO; D];
176
177 for (j, coeff) in current.get_coefficients().iter().enumerate() {
178 low_coeffs[j] = coeff.centered_mod(base);
179 }
180
181 let low_part = Self::new(low_coeffs);
182 parts.push(low_part.clone());
183
184 current -= low_part;
186
187 let mut scaled_coeffs = [Zq::ZERO; D];
189 for (j, coeff) in current.get_coefficients().iter().enumerate() {
190 scaled_coeffs[j] = coeff.scale_by(base);
191 }
192 current = Self::new(scaled_coeffs);
193 }
194 }
195
196 parts
197 }
198
199 pub fn encode_message(message: &[bool]) -> Option<Self> {
213 if message.len() > D {
214 return None;
215 }
216
217 let mut coeffs = [Zq::ZERO; D];
218 for (i, &bit) in message.iter().enumerate() {
219 coeffs[i] = Zq::new(u32::from(bit));
220 }
221 Some(Rq::new(coeffs))
222 }
223
224 pub fn iter(&self) -> std::slice::Iter<'_, Zq> {
226 self.coeffs.iter()
227 }
228
229 pub fn check_bounds(&self, bound: Zq) -> bool {
231 self.iter().all(|coeff| coeff <= &bound || coeff >= &-bound)
232 }
233
234 pub const fn zero() -> Self {
235 Self::new([Zq::ZERO; D])
236 }
237}
238
239macro_rules! impl_arithmetic {
240 ($trait:ident, $assign_trait:ident, $method:ident, $assign_method:ident, $op_method:ident) => {
241 impl<const D: usize> $trait for Rq<{ D }> {
242 type Output = Self;
243
244 fn $method(self, rhs: Self) -> Self::Output {
245 self.$op_method(&rhs)
246 }
247 }
248
249 impl<const D: usize> $assign_trait for Rq<{ D }> {
250 fn $assign_method(&mut self, rhs: Self) {
251 let result = self.$op_method(&rhs);
252 self.coeffs = result.coeffs;
253 }
254 }
255 };
256}
257
258impl_arithmetic!(Add, AddAssign, add, add_assign, addition);
259impl_arithmetic!(Sub, SubAssign, sub, sub_assign, subtraction);
260impl_arithmetic!(Mul, MulAssign, mul, mul_assign, multiplication);
261
262impl<const D: usize> From<Vec<Zq>> for Rq<D> {
263 fn from(vec: Vec<Zq>) -> Self {
264 let mut temp = [Zq::ZERO; D];
265 for i in (0..vec.len()).rev() {
267 let m = i / D;
268 let r = i % D;
269 let sign = if m % 2 == 0 { 1 } else { -1 };
270 if sign == 1 {
271 temp[r] += vec[i];
272 } else {
273 temp[r] -= vec[i];
274 }
275 }
276 Rq::new(temp)
277 }
278}
279
280impl Sum for Zq {
281 fn sum<I>(iter: I) -> Self
283 where
284 I: Iterator<Item = Zq>,
285 {
286 iter.fold(Zq::ZERO, |acc, x| acc + x)
287 }
288}
289
290impl<const D: usize> Neg for Rq<D> {
292 type Output = Self;
293
294 fn neg(self) -> Self {
296 let mut result = [Zq::ZERO; D];
297 for (i, &coeff) in self.coeffs.iter().enumerate() {
298 result[i] = Zq::ZERO - coeff;
299 }
300 Rq::new(result)
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
310 fn test_new_and_create_poly() {
311 let poly = Rq::new([Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]);
312 assert_eq!(poly.coeffs, [Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]);
313
314 let poly_from_vec_direct: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
316 assert_eq!(
317 poly_from_vec_direct.coeffs,
318 [Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]
319 );
320 let poly_from_vec_wrapping: Rq<4> =
322 vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4), Zq::ONE].into();
323 assert_eq!(
324 poly_from_vec_wrapping.coeffs,
325 [Zq::ZERO, Zq::new(2), Zq::new(3), Zq::new(4)]
326 );
327 let poly_from_vec_zeros: Rq<4> = vec![Zq::ONE, Zq::new(2)].into();
329 assert_eq!(
330 poly_from_vec_zeros.coeffs,
331 [Zq::ONE, Zq::new(2), Zq::ZERO, Zq::ZERO]
332 );
333 let poly_high_degree_reduction: Rq<2> = vec![
335 Zq::ONE,
336 Zq::new(2),
337 Zq::ZERO,
338 Zq::ZERO,
339 Zq::ZERO,
340 Zq::ZERO,
341 Zq::ONE,
342 ]
343 .into();
344 assert_eq!(poly_high_degree_reduction.coeffs, [Zq::ZERO, Zq::new(2)]);
345 }
346
347 #[test]
349 fn test_add() {
350 let poly1: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
352 let poly2: Rq<4> = vec![Zq::new(4), Zq::new(3), Zq::new(2), Zq::ONE].into();
353 let result = poly1 + poly2;
354 assert_eq!(
355 result.coeffs,
356 [Zq::new(5), Zq::new(5), Zq::new(5), Zq::new(5)]
357 );
358
359 let poly3: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
361 let poly4: Rq<4> = vec![Zq::MAX, Zq::new(3), Zq::MAX, Zq::ONE].into();
362 let result2 = poly3 + poly4;
363 assert_eq!(
364 result2.coeffs,
365 [Zq::ZERO, Zq::new(5), Zq::new(2), Zq::new(5)]
366 );
367 let poly5: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
369 let poly6: Rq<4> = vec![Zq::ZERO].into();
370 let result3 = poly5 + poly6;
371 assert_eq!(
372 result3.coeffs,
373 [Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]
374 );
375 let poly7: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::MAX].into();
377 let poly8: Rq<4> = vec![Zq::MAX, Zq::MAX, Zq::MAX, Zq::MAX].into();
378 let result3 = poly7 + poly8;
379 assert_eq!(
380 result3.coeffs,
381 [
382 Zq::ZERO,
383 Zq::ONE,
384 Zq::new(2),
385 Zq::new(u32::MAX.wrapping_add(u32::MAX))
386 ]
387 );
388 }
389 #[test]
391
392 fn test_mul() {
393 let poly1: Rq<3> = vec![Zq::ONE, Zq::ONE, Zq::new(2)].into();
395 let poly2: Rq<3> = vec![Zq::ONE, Zq::ONE].into();
396 let result = poly1 * poly2;
397 assert_eq!(result.coeffs, [Zq::MAX, Zq::new(2), Zq::new(3)]);
398
399 let poly3: Rq<3> = vec![Zq::ONE, Zq::ONE, Zq::new(2)].into();
401 let poly4: Rq<3> = vec![Zq::ZERO].into();
402 let result2 = poly3 * poly4;
403 assert_eq!(result2.coeffs, [Zq::ZERO, Zq::ZERO, Zq::ZERO]);
404
405 let poly5: Rq<3> = vec![Zq::ONE, Zq::ONE, Zq::new(2)].into();
407 let poly6: Rq<3> = vec![Zq::ONE, Zq::ONE, Zq::new(7), Zq::new(5)].into();
408 let result3 = poly5 * poly6;
409 assert_eq!(
410 result3.coeffs,
411 [Zq::new(u32::MAX - 12), Zq::new(u32::MAX - 16), Zq::ZERO]
412 );
413 }
414
415 #[test]
417 fn test_sub() {
418 let poly1: Rq<4> = vec![Zq::new(5), Zq::new(10), Zq::new(15), Zq::new(20)].into();
420 let poly2: Rq<4> = vec![Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)].into();
421 let result = poly1 - poly2;
422 assert_eq!(
423 result.coeffs,
424 [Zq::new(3), Zq::new(6), Zq::new(9), Zq::new(12)]
425 );
426
427 let poly3: Rq<4> = vec![Zq::ONE, Zq::ONE, Zq::new(3), Zq::new(2)].into();
429 let poly4: Rq<4> = vec![Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)].into();
430 let result2 = poly3 - poly4;
431 assert_eq!(
432 result2.coeffs,
433 [
434 Zq::MAX,
435 Zq::new(u32::MAX - 2),
436 Zq::new(u32::MAX - 2),
437 Zq::new(u32::MAX - 5)
438 ]
439 );
440 let poly5: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
442 let poly6: Rq<4> = vec![Zq::ZERO].into();
443 let result3 = poly6.clone() - poly5.clone();
444 let result4 = poly5.clone() - poly6.clone();
445 assert_eq!(
446 result3.coeffs,
447 [
448 Zq::MAX,
449 Zq::new(u32::MAX - 1),
450 Zq::new(u32::MAX - 2),
451 Zq::new(u32::MAX - 3)
452 ]
453 );
454 assert_eq!(
455 result4.coeffs,
456 [Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]
457 );
458 }
459
460 #[test]
462 fn test_neg() {
463 let poly: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
464 let result = -poly;
465 assert_eq!(
466 result.coeffs,
467 [
468 Zq::MAX,
469 Zq::new(u32::MAX - 1),
470 Zq::new(u32::MAX - 2),
471 Zq::new(u32::MAX - 3)
472 ]
473 );
474 }
475
476 #[test]
478 fn test_scalar_mul() {
479 let poly: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
480 let result = poly.scalar_mul(Zq::new(2));
481 assert_eq!(
482 result.coeffs,
483 [Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)]
484 );
485 }
486
487 #[test]
489 fn test_eval() {
490 let poly: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
491 let result = poly.eval(Zq::new(2));
492 assert_eq!(result, Zq::new(49));
493 }
494
495 #[test]
497 fn test_is_equal() {
498 let poly1: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
499 let poly2: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
500 let poly3: Rq<4> = vec![Zq::new(4), Zq::new(3), Zq::new(2), Zq::ONE].into();
501 assert!(poly1.is_equal(&poly2));
502 assert!(!poly1.is_equal(&poly3));
503 }
504
505 #[test]
507 fn test_is_zero_poly() {
508 let zero_poly: Rq<4> = vec![Zq::ZERO; 4].into();
509 let non_zero_poly: Rq<4> = vec![Zq::ONE, Zq::ZERO, Zq::ZERO, Zq::ZERO].into();
510 assert!(zero_poly.is_zero());
511 assert!(!non_zero_poly.is_zero());
512 }
513
514 #[test]
515 fn test_encode_message() {
516 let message = vec![true, false, true, false];
518 let encoded = Rq::<4>::encode_message(&message).unwrap();
519 assert_eq!(encoded.coeffs, [Zq::ONE, Zq::ZERO, Zq::ONE, Zq::ZERO]);
520
521 let short_message = vec![true, false];
523 let encoded_short = Rq::<4>::encode_message(&short_message).unwrap();
524 assert_eq!(
525 encoded_short.coeffs,
526 [Zq::ONE, Zq::ZERO, Zq::ZERO, Zq::ZERO]
527 );
528
529 let long_message = vec![true; 5];
531 assert!(Rq::<4>::encode_message(&long_message).is_none());
532
533 let empty_message: Vec<bool> = vec![];
535 let encoded_empty = Rq::<4>::encode_message(&empty_message).unwrap();
536 assert!(encoded_empty.is_zero());
537 }
538
539 #[test]
541 fn test_get_coefficient() {
542 let poly: Rq<4> = vec![Zq::ONE, Zq::ZERO, Zq::new(5), Zq::MAX].into();
543 let vec = vec![Zq::ONE, Zq::ZERO, Zq::new(5), Zq::MAX];
544 assert!(poly.get_coefficients().to_vec() == vec);
545
546 let poly_zero: Rq<4> = vec![Zq::ZERO, Zq::ZERO, Zq::ZERO, Zq::ZERO].into();
547 let vec_zero = vec![Zq::ZERO, Zq::ZERO, Zq::ZERO, Zq::ZERO];
548 assert!(poly_zero.get_coefficients().to_vec() == vec_zero);
549 }
550
551 #[test]
552 fn test_base2_decomposition() {
553 let poly: Rq<4> = vec![Zq::new(5), Zq::new(3), Zq::new(7), Zq::new(1)].into();
555 let parts = poly.decompose(Zq::TWO, 2);
556
557 assert_eq!(
559 parts[0].coeffs,
560 [
561 Zq::ONE, Zq::ONE, Zq::ONE, Zq::ONE, ]
566 );
567
568 assert_eq!(
570 parts[1].coeffs,
571 [
572 Zq::new(2), Zq::ONE, Zq::new(3), Zq::ZERO, ]
577 );
578
579 for i in 0..4 {
581 let expected = poly.coeffs[i];
582 let actual = parts[0].coeffs[i] + parts[1].coeffs[i] * Zq::TWO;
583 assert_eq!(actual, expected, "Base 2: Coefficient {} mismatch", i);
584 }
585 }
586
587 #[test]
588 fn test_base3_decomposition() {
589 let specific_poly: Rq<4> = vec![Zq::new(8), Zq::new(11), Zq::new(4), Zq::new(15)].into();
591 let parts = specific_poly.decompose(Zq::new(3), 2);
592
593 assert_eq!(
595 parts[0].coeffs,
596 [
597 Zq::MAX, Zq::MAX, Zq::ONE, Zq::ZERO, ]
602 );
603
604 assert_eq!(
606 parts[1].coeffs,
607 [
608 Zq::new(3), Zq::new(4), Zq::ONE, Zq::new(5), ]
613 );
614
615 for i in 0..4 {
617 let expected = specific_poly.coeffs[i];
618 let p0 = parts[0].coeffs[i];
619 let p1 = parts[1].coeffs[i];
620 let actual = p0 + p1 * Zq::new(3);
621 assert_eq!(actual, expected, "Base 3: Coefficient {} mismatch", i);
622 }
623 }
624
625 #[test]
626 fn test_decomposition_edge_cases() {
627 let zero_poly: Rq<4> = vec![Zq::ZERO; 4].into();
629 let parts = zero_poly.decompose(Zq::TWO, 2);
630 assert!(
631 parts.iter().all(|p| p.is_zero()),
632 "Zero polynomial decomposition failed"
633 );
634
635 let simple_poly: Rq<4> = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
637 let parts = simple_poly.decompose(Zq::TWO, 1);
638 assert_eq!(parts.len(), 1, "Single part decomposition length incorrect");
639 assert_eq!(
640 parts[0], simple_poly,
641 "Single part decomposition value incorrect"
642 );
643 }
644
645 #[test]
646 fn test_large_base_decomposition() {
647 let poly: Rq<4> = vec![Zq::new(120), Zq::new(33), Zq::new(255), Zq::new(19)].into();
649
650 let parts_base8 = poly.decompose(Zq::new(8), 2);
652
653 assert_eq!(
655 parts_base8[0].coeffs,
656 [
657 Zq::ZERO, Zq::ONE, Zq::MAX, Zq::new(3), ]
662 );
663
664 assert_eq!(
666 parts_base8[1].coeffs,
667 [
668 Zq::new(15), Zq::new(4), Zq::new(32), Zq::new(2), ]
673 );
674
675 for i in 0..4 {
677 let expected = poly.coeffs[i];
678 let p0 = parts_base8[0].coeffs[i];
679 let p1 = parts_base8[1].coeffs[i];
680 let actual = p0 + p1 * Zq::new(8);
681 assert_eq!(actual, expected, "Base 8: Coefficient {} mismatch", i);
682 }
683
684 let parts_base16 = poly.decompose(Zq::new(16), 2);
686
687 for i in 0..4 {
689 let expected = poly.coeffs[i];
690 let p0 = parts_base16[0].coeffs[i];
691 let p1 = parts_base16[1].coeffs[i];
692 let actual = p0 + p1 * Zq::new(16);
693 assert_eq!(actual, expected, "Base 16: Coefficient {} mismatch", i);
694 }
695 }
696
697 #[test]
698 fn test_multi_part_decomposition() {
699 let poly: Rq<4> = vec![Zq::new(123), Zq::new(456), Zq::new(789), Zq::new(101112)].into();
701
702 let parts = poly.decompose(Zq::new(4), 3);
704 assert_eq!(parts.len(), 3, "Should have 3 parts");
705
706 let reconstructed = parts[0].clone()
708 + parts[1].clone().scalar_mul(Zq::new(4))
709 + parts[2].clone().scalar_mul(Zq::new(16)); for i in 0..4 {
713 assert_eq!(
714 reconstructed.coeffs[i], poly.coeffs[i],
715 "3-part base 4: Coefficient {} mismatch",
716 i
717 );
718 }
719 }
720
721 #[test]
722 fn test_centering_properties() {
723 let values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
726 let poly: Rq<11> = values
727 .iter()
728 .map(|&v| Zq::new(v))
729 .collect::<Vec<Zq>>()
730 .into();
731
732 let parts = poly.decompose(Zq::new(5), 2);
733
734 let expected_centered = [
743 Zq::ZERO, Zq::ONE, Zq::new(2), -Zq::new(2), -Zq::ONE, Zq::ZERO, Zq::ONE, Zq::new(2), -Zq::new(2), -Zq::ONE, Zq::ZERO, ];
755
756 for (i, &expected) in expected_centered.iter().enumerate() {
757 assert_eq!(
758 parts[0].coeffs[i], expected,
759 "Base 5 centering: Coefficient {} incorrectly centered",
760 i
761 );
762 }
763 }
764
765 #[test]
766 fn test_extreme_values() {
767 let poly: Rq<3> = vec![Zq::ZERO, Zq::MAX, Zq::MAX - Zq::ONE].into();
769
770 let parts = poly.decompose(Zq::new(3), 2);
772
773 let reconstructed = parts[0].clone() + parts[1].clone().scalar_mul(Zq::new(3));
775
776 for i in 0..3 {
777 assert_eq!(
778 reconstructed.coeffs[i], poly.coeffs[i],
779 "Extreme values: Coefficient {} mismatch",
780 i
781 );
782 }
783
784 assert_eq!(parts[0].coeffs[1], Zq::ZERO); assert_eq!(parts[1].coeffs[1], Zq::new(1431655765)); assert_eq!(parts[0].coeffs[2], Zq::MAX); assert_eq!(parts[1].coeffs[2], Zq::new(1431655765)); }
795
796 #[test]
797 fn test_decomposition_properties() {
798 let poly: Rq<8> = vec![
800 Zq::new(100),
801 Zq::new(200),
802 Zq::new(300),
803 Zq::new(400),
804 Zq::new(500),
805 Zq::new(600),
806 Zq::new(700),
807 Zq::new(800),
808 ]
809 .into();
810
811 for base in [2, 3, 4, 5, 8, 10, 16].iter() {
812 let parts = poly.decompose(Zq::new(*base), 2);
813 let half_base = Zq::new(*base).scale_by(Zq::TWO);
814
815 for coeff in parts[0].coeffs.iter() {
817 let abs_coeff = if *coeff > Zq::new(u32::MAX / 2) {
819 Zq::ZERO - *coeff } else {
821 *coeff
822 };
823
824 assert!(
825 abs_coeff <= half_base,
826 "Base {}: First part coefficient {} exceeds half-base {}",
827 base,
828 coeff,
829 half_base
830 );
831 }
832
833 let reconstructed = parts[0].clone() + parts[1].clone().scalar_mul(Zq::new(*base));
835 assert_eq!(reconstructed, poly, "Base {}: Reconstruction failed", base);
836 }
837 }
838}