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 rustfft::num_complex::Complex;
26use rustfft::FftPlanner;
27use std::iter::Sum;
28
29use super::rq_vector::RqVector;
30
31#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct Rq {
35 coeffs: [Zq; Self::DEGREE],
36}
37
38impl Rq {
39 pub const DEGREE: usize = 64;
40 pub const fn new(coeffs: [Zq; Self::DEGREE]) -> Self {
42 Rq { coeffs }
43 }
44
45 pub const fn zero() -> Self {
47 Self {
48 coeffs: [Zq::ZERO; Self::DEGREE],
49 }
50 }
51
52 pub fn get_coefficients(&self) -> &[Zq; Self::DEGREE] {
54 &self.coeffs
55 }
56
57 fn addition(&self, other: &Self) -> Self {
59 let mut result = [Zq::ZERO; Self::DEGREE];
60 for (r, (a, b)) in result
61 .iter_mut()
62 .zip(self.coeffs.iter().zip(other.coeffs.iter()))
63 {
64 *r = *a + *b;
65 }
66 Rq::new(result)
67 }
68
69 fn subtraction(&self, other: &Self) -> Self {
71 let mut result = [Zq::ZERO; Self::DEGREE];
72 for (r, (a, b)) in result
73 .iter_mut()
74 .zip(self.coeffs.iter().zip(other.coeffs.iter()))
75 {
76 *r = *a - *b;
77 }
78 Rq::new(result)
79 }
80
81 pub fn multiplication(&self, other: &Self) -> Self {
83 let mut result = [Zq::ZERO; Self::DEGREE];
84 let mut out_of_field = [Zq::ZERO; Self::DEGREE];
85 for (i, &self_coeff) in self.coeffs.iter().enumerate() {
86 for (j, &other_coeff) in other.coeffs.iter().enumerate() {
87 if i + j < Self::DEGREE {
88 result[i + j] += self_coeff * other_coeff;
89 } else {
90 out_of_field[(i + j) % Self::DEGREE] += self_coeff * other_coeff;
91 }
92 }
93 }
94 for i in (0..Self::DEGREE).rev() {
96 let m = i / Self::DEGREE;
97 let r = i % Self::DEGREE;
98 let sign = if (m + 1) % 2 == 0 { 1 } else { -1 };
99 if sign == 1 {
100 result[r] += out_of_field[i];
101 } else {
102 result[r] -= out_of_field[i];
103 }
104 }
105 Rq::new(result)
106 }
107
108 pub fn inner_product(&self, other: &Self) -> Zq {
110 self.coeffs
111 .iter()
112 .zip(other.coeffs.iter())
113 .map(|(&a, &b)| a * b)
114 .fold(Zq::ZERO, |acc, x| acc + x)
115 }
116
117 pub fn scalar_mul(&self, s: Zq) -> Self {
119 let mut result = [Zq::ZERO; Self::DEGREE];
120 for (i, &coeff) in self.coeffs.iter().enumerate() {
121 result[i] = s * (coeff);
122 }
123 Rq::new(result)
124 }
125
126 pub fn eval(&self, x: Zq) -> Zq {
128 let mut result = Zq::ZERO;
129 for coeff in self.coeffs.iter().rev() {
130 result = result * x + *coeff;
131 }
132
133 result
134 }
135
136 pub fn is_zero(&self) -> bool {
138 self.coeffs.iter().all(|&coeff| coeff == Zq::ZERO)
139 }
140
141 pub fn is_equal(&self, other: &Self) -> bool {
143 self.coeffs == other.coeffs
144 }
145
146 pub fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
148 let uniform = Uniform::new_inclusive(Zq::ZERO, Zq::MAX).unwrap();
149 let mut coeffs = [Zq::ZERO; Self::DEGREE];
150 coeffs.iter_mut().for_each(|c| *c = uniform.sample(rng));
151 Self { coeffs }
152 }
153
154 pub fn random_ternary<R: Rng + CryptoRng>(rng: &mut R) -> Self {
156 let mut coeffs = [Zq::ZERO; Self::DEGREE];
157
158 for coeff in coeffs.iter_mut() {
159 let val = match rng.random_range(0..3) {
161 0 => Zq::MAX, 1 => Zq::ZERO, 2 => Zq::ONE, _ => unreachable!(),
165 };
166 *coeff = val;
167 }
168
169 Rq::new(coeffs)
170 }
171
172 pub fn decompose(&self, base: Zq, num_parts: usize) -> RqVector {
176 let mut parts = Vec::with_capacity(num_parts);
177 let mut current = self.clone();
178
179 for i in 0..num_parts {
180 if i == num_parts - 1 {
181 parts.push(current.clone());
182 } else {
183 let mut low_coeffs = [Zq::ZERO; Self::DEGREE];
185
186 for (j, coeff) in current.get_coefficients().iter().enumerate() {
187 low_coeffs[j] = coeff.centered_mod(base);
188 }
189
190 let low_part = Self::new(low_coeffs);
191 parts.push(low_part.clone());
192
193 current -= low_part;
195
196 let mut scaled_coeffs = [Zq::ZERO; Self::DEGREE];
198 for (j, coeff) in current.get_coefficients().iter().enumerate() {
199 scaled_coeffs[j] = coeff.scale_by(base);
200 }
201 current = Self::new(scaled_coeffs);
202 }
203 }
204
205 RqVector::new(parts)
206 }
207
208 pub fn encode_message(message: &[bool]) -> Option<Self> {
222 if message.len() > Self::DEGREE {
223 return None;
224 }
225
226 let mut coeffs = [Zq::ZERO; Self::DEGREE];
227 for (i, &bit) in message.iter().enumerate() {
228 coeffs[i] = Zq::new(u32::from(bit));
229 }
230 Some(Rq::new(coeffs))
231 }
232
233 pub fn iter(&self) -> std::slice::Iter<'_, Zq> {
235 self.coeffs.iter()
236 }
237
238 pub fn check_bounds(&self, bound: Zq) -> bool {
240 self.iter().all(|coeff| coeff <= &bound || coeff >= &-bound)
241 }
242
243 pub fn conjugate_automorphism(&self) -> Self {
245 let q_minus_1 = Zq::MAX;
246 let mut new_coeffs = [Zq::ZERO; Self::DEGREE];
247 for (i, new_coeff) in new_coeffs.iter_mut().enumerate().take(Self::DEGREE) {
248 if i < self.get_coefficients().len() {
249 if i == 0 {
250 *new_coeff = self.get_coefficients()[i];
251 } else {
252 *new_coeff = self.get_coefficients()[i] * q_minus_1;
253 }
254 } else {
255 *new_coeff = Zq::ZERO;
256 }
257 }
258 debug_assert_eq!(new_coeffs.len(), Self::DEGREE);
259 let mut reversed_coefficients = new_coeffs; reversed_coefficients[1..].reverse(); Self::new(reversed_coefficients)
262 }
263
264 #[allow(clippy::as_conversions)]
272 pub fn operator_norm(&self) -> f64 {
273 let coeffs = self.get_coefficients();
274 let n = coeffs.len();
275 let mut planner = FftPlanner::new();
276 let fft = planner.plan_fft_forward(n);
277
278 let mut buffer: Vec<Complex<f64>> = coeffs
280 .iter()
281 .map(|&x| {
282 let half = Zq::MAX.scale_by(Zq::TWO);
283 let converted_value = if x > half {
284 x.to_u128() as f64 - Zq::MAX.to_u128() as f64 - 1.0
285 } else {
286 x.to_u128() as f64
287 };
288 Complex {
289 re: converted_value,
290 im: 0.0,
291 }
292 })
293 .collect();
294
295 fft.process(&mut buffer);
297
298 buffer
300 .iter()
301 .map(|c| c.norm())
302 .fold(0.0, |max, x| max.max(x))
303 }
304}
305
306macro_rules! impl_arithmetic {
307 ($trait:ident, $assign_trait:ident, $method:ident, $assign_method:ident, $op_method:ident) => {
308 impl $trait for Rq {
309 type Output = Self;
310
311 fn $method(self, rhs: Self) -> Self::Output {
312 self.$op_method(&rhs)
313 }
314 }
315
316 impl $assign_trait for Rq {
317 fn $assign_method(&mut self, rhs: Self) {
318 let result = self.$op_method(&rhs);
319 self.coeffs = result.coeffs;
320 }
321 }
322 };
323}
324
325impl_arithmetic!(Add, AddAssign, add, add_assign, addition);
326impl_arithmetic!(Sub, SubAssign, sub, sub_assign, subtraction);
327impl_arithmetic!(Mul, MulAssign, mul, mul_assign, multiplication);
328
329impl From<Vec<Zq>> for Rq {
330 fn from(vec: Vec<Zq>) -> Self {
331 let mut temp = [Zq::ZERO; Self::DEGREE];
332 for i in (0..vec.len()).rev() {
334 let m = i / Self::DEGREE;
335 let r = i % Self::DEGREE;
336 let sign = if m % 2 == 0 { 1 } else { -1 };
337 if sign == 1 {
338 temp[r] += vec[i];
339 } else {
340 temp[r] -= vec[i];
341 }
342 }
343 Rq::new(temp)
344 }
345}
346
347impl Mul<&Zq> for &Rq {
348 type Output = Rq;
349 fn mul(self, other: &Zq) -> Rq {
351 let mut copied_coeffs = self.coeffs;
352 for elem in copied_coeffs.iter_mut() {
353 *elem *= *other;
354 }
355 Rq::new(copied_coeffs)
356 }
357}
358
359impl Add<&Rq> for &Rq {
360 type Output = Rq;
361 fn add(self, other: &Rq) -> Rq {
363 let mut coeffs = [Zq::ZERO; Rq::DEGREE];
364 for (idx, item) in coeffs.iter_mut().enumerate().take(Rq::DEGREE) {
365 *item = self.get_coefficients()[idx] + other.get_coefficients()[idx];
366 }
367 Rq::new(coeffs)
368 }
369}
370
371impl Sum for Zq {
372 fn sum<I>(iter: I) -> Self
374 where
375 I: Iterator<Item = Zq>,
376 {
377 iter.fold(Zq::ZERO, |acc, x| acc + x)
378 }
379}
380
381impl Mul<&Rq> for &Rq {
382 type Output = Rq;
383 fn mul(self, other: &Rq) -> Rq {
385 let mut result_coefficients =
387 vec![Zq::new(0); self.get_coefficients().len() + other.get_coefficients().len() - 1];
388 for (i, &coeff1) in self.get_coefficients().iter().enumerate() {
389 for (j, &coeff2) in other.get_coefficients().iter().enumerate() {
390 result_coefficients[i + j] += coeff1 * coeff2;
391 }
392 }
393
394 if result_coefficients.len() > Rq::DEGREE {
396 let q_minus_1 = Zq::MAX;
397 let (left, right) = result_coefficients.split_at_mut(Rq::DEGREE);
398 for (i, &overflow) in right.iter().enumerate() {
399 left[i] += overflow * q_minus_1;
400 }
401 result_coefficients.truncate(Rq::DEGREE);
402 }
403 Rq::new(result_coefficients[..Rq::DEGREE].try_into().unwrap())
404 }
405}
406
407impl Neg for Rq {
409 type Output = Self;
410
411 fn neg(self) -> Self {
413 let mut result = [Zq::ZERO; Rq::DEGREE];
414 for (i, &coeff) in self.coeffs.iter().enumerate() {
415 result[i] = Zq::ZERO - coeff;
416 }
417 Rq::new(result)
418 }
419}
420
421impl FromIterator<Zq> for Rq {
422 fn from_iter<T: IntoIterator<Item = Zq>>(iter: T) -> Self {
423 let coeffs_vec: Vec<Zq> = iter.into_iter().collect();
424 assert_eq!(
425 coeffs_vec.len(),
426 Self::DEGREE,
427 "Iterator must contain exactly {} elements to create Rq<{}>",
428 Self::DEGREE,
429 Self::DEGREE
430 );
431
432 let mut coeffs = [Zq::default(); Self::DEGREE];
434 for (i, coeff) in coeffs_vec.into_iter().enumerate().take(Self::DEGREE) {
435 coeffs[i] = coeff;
436 }
437
438 Rq::new(coeffs)
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445
446 mod helper {
447 use super::*;
448 pub fn padded(prefix: &[Zq]) -> [Zq; Rq::DEGREE] {
449 assert!(
450 prefix.len() <= Rq::DEGREE,
451 "too many coefficients for degree {}",
452 Rq::DEGREE
453 );
454
455 let mut out = [Zq::ZERO; Rq::DEGREE];
456 out[..prefix.len()].copy_from_slice(prefix);
457 out
458 }
459
460 pub fn rq_from(prefix: &[Zq]) -> Rq {
461 Rq {
462 coeffs: padded(prefix),
463 }
464 }
465 }
466
467 #[test]
469 fn test_new_and_create_poly() {
470 let coeffs = [Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)];
471 let poly = helper::rq_from(&coeffs);
472 assert_eq!(poly.coeffs, helper::padded(&coeffs));
473
474 let coeffs2 = [Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)];
476 let poly_from_vec_direct: Rq = coeffs.to_vec().into();
477 assert_eq!(poly_from_vec_direct.coeffs, helper::padded(&coeffs2));
478 }
479
480 #[test]
482 fn test_add() {
483 let poly1: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
485 let poly2: Rq = vec![Zq::new(4), Zq::new(3), Zq::new(2), Zq::ONE].into();
486 let result = poly1 + poly2;
487 assert_eq!(
488 result.coeffs,
489 helper::padded(&[Zq::new(5), Zq::new(5), Zq::new(5), Zq::new(5)])
490 );
491
492 let poly3: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
494 let poly4: Rq = vec![Zq::MAX, Zq::new(3), Zq::MAX, Zq::ONE].into();
495 let result2 = poly3 + poly4;
496 assert_eq!(
497 result2.coeffs,
498 helper::padded(&[Zq::ZERO, Zq::new(5), Zq::new(2), Zq::new(5)])
499 );
500 let poly5: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
502 let poly6: Rq = vec![Zq::ZERO].into();
503 let result3 = poly5 + poly6;
504 assert_eq!(
505 result3.coeffs,
506 helper::padded(&[Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)])
507 );
508 let poly7: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::MAX].into();
510 let poly8: Rq = vec![Zq::MAX, Zq::MAX, Zq::MAX, Zq::MAX].into();
511 let result3 = poly7 + poly8;
512 assert_eq!(
513 result3.coeffs,
514 helper::padded(&[
515 Zq::ZERO,
516 Zq::ONE,
517 Zq::new(2),
518 Zq::new(u32::MAX.wrapping_add(u32::MAX))
519 ])
520 );
521 }
522
523 #[test]
525 fn test_mul() {
526 let poly1: Rq = vec![Zq::ONE, Zq::ONE, Zq::new(2)].into();
528 let poly2: Rq = vec![Zq::ONE, Zq::ONE].into();
529 let result = poly1 * poly2;
530 assert_eq!(
531 result.coeffs,
532 helper::padded(&[Zq::new(1), Zq::new(2), Zq::new(3), Zq::new(2)])
533 );
534
535 let poly3: Rq = vec![Zq::ONE, Zq::ONE, Zq::new(2)].into();
537 let poly4: Rq = vec![Zq::ZERO].into();
538 let result2 = poly3 * poly4;
539 assert_eq!(
540 result2.coeffs,
541 helper::padded(&[Zq::ZERO, Zq::ZERO, Zq::ZERO])
542 );
543
544 }
554
555 #[test]
557 fn test_sub() {
558 let poly1: Rq = vec![Zq::new(5), Zq::new(10), Zq::new(15), Zq::new(20)].into();
560 let poly2: Rq = vec![Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)].into();
561 let result = poly1 - poly2;
562 assert_eq!(
563 result.coeffs,
564 helper::padded(&[Zq::new(3), Zq::new(6), Zq::new(9), Zq::new(12)])
565 );
566
567 let poly3: Rq = vec![Zq::ONE, Zq::ONE, Zq::new(3), Zq::new(2)].into();
569 let poly4: Rq = vec![Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)].into();
570 let result2 = poly3 - poly4;
571 assert_eq!(
572 result2.coeffs,
573 helper::padded(&[
574 Zq::MAX,
575 Zq::new(u32::MAX - 2),
576 Zq::new(u32::MAX - 2),
577 Zq::new(u32::MAX - 5)
578 ])
579 );
580 let poly5: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
582 let poly6: Rq = vec![Zq::ZERO].into();
583 let result3 = poly6.clone() - poly5.clone();
584 let result4 = poly5 - poly6;
585 assert_eq!(
586 result3.coeffs,
587 helper::padded(&[
588 Zq::MAX,
589 Zq::new(u32::MAX - 1),
590 Zq::new(u32::MAX - 2),
591 Zq::new(u32::MAX - 3)
592 ])
593 );
594 assert_eq!(
595 result4.coeffs,
596 helper::padded(&[Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)])
597 );
598 }
599
600 #[test]
602 fn test_neg() {
603 let poly: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
604 let result = -poly;
605 assert_eq!(
606 result.coeffs,
607 helper::padded(&[
608 Zq::MAX,
609 Zq::new(u32::MAX - 1),
610 Zq::new(u32::MAX - 2),
611 Zq::new(u32::MAX - 3)
612 ])
613 );
614 }
615
616 #[test]
618 fn test_scalar_mul() {
619 let poly: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
620 let result = poly.scalar_mul(Zq::new(2));
621 assert_eq!(
622 result.coeffs,
623 helper::padded(&[Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)])
624 );
625 }
626
627 #[test]
629 fn test_eval() {
630 let poly: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
631 let result = poly.eval(Zq::new(2));
632 assert_eq!(result, Zq::new(49));
633 }
634
635 #[test]
637 fn test_is_equal() {
638 let poly1: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
639 let poly2: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
640 let poly3: Rq = vec![Zq::new(4), Zq::new(3), Zq::new(2), Zq::ONE].into();
641 assert!(poly1.is_equal(&poly2));
642 assert!(!poly1.is_equal(&poly3));
643 }
644
645 #[test]
647 fn test_is_zero_poly() {
648 let zero_poly: Rq = vec![Zq::ZERO; 4].into();
649 let non_zero_poly: Rq = vec![Zq::ONE, Zq::ZERO, Zq::ZERO, Zq::ZERO].into();
650 assert!(zero_poly.is_zero());
651 assert!(!non_zero_poly.is_zero());
652 }
653
654 #[test]
655 fn test_encode_message() {
656 let message = vec![true, false, true, false];
658 let encoded = Rq::encode_message(&message).unwrap();
659 assert_eq!(
660 encoded.coeffs,
661 helper::padded(&[Zq::ONE, Zq::ZERO, Zq::ONE, Zq::ZERO])
662 );
663
664 let short_message = vec![true, false];
666 let encoded_short = Rq::encode_message(&short_message).unwrap();
667 assert_eq!(
668 encoded_short.coeffs,
669 helper::padded(&[Zq::ONE, Zq::ZERO, Zq::ZERO, Zq::ZERO])
670 );
671
672 let long_message = vec![true; Rq::DEGREE + 2];
674 assert!(Rq::encode_message(&long_message).is_none());
675
676 let empty_message: Vec<bool> = vec![];
678 let encoded_empty = Rq::encode_message(&empty_message).unwrap();
679 assert!(encoded_empty.is_zero());
680 }
681
682 #[test]
684 fn test_get_coefficient() {
685 let poly: Rq = vec![Zq::ONE, Zq::ZERO, Zq::new(5), Zq::MAX].into();
686 let vec = helper::padded(&[Zq::ONE, Zq::ZERO, Zq::new(5), Zq::MAX]).to_vec();
687 assert!(poly.get_coefficients().to_vec() == vec);
688
689 let poly_zero: Rq = vec![Zq::ZERO, Zq::ZERO, Zq::ZERO, Zq::ZERO].into();
690 let vec_zero = helper::padded(&[Zq::ZERO, Zq::ZERO, Zq::ZERO, Zq::ZERO]).to_vec();
691 assert!(poly_zero.get_coefficients().to_vec() == vec_zero);
692 }
693
694 #[test]
695 fn test_base2_decomposition() {
696 let poly: Rq = vec![Zq::new(5), Zq::new(3), Zq::new(7), Zq::new(1)].into();
698 let parts = poly.decompose(Zq::TWO, 2);
699
700 assert_eq!(
702 parts[0].coeffs,
703 helper::padded(&[
704 Zq::ONE, Zq::ONE, Zq::ONE, Zq::ONE, ])
709 );
710
711 assert_eq!(
713 parts[1].coeffs,
714 helper::padded(&[
715 Zq::new(2), Zq::ONE, Zq::new(3), Zq::ZERO, ])
720 );
721
722 for i in 0..4 {
724 let expected = poly.coeffs[i];
725 let actual = parts[0].coeffs[i] + parts[1].coeffs[i] * Zq::TWO;
726 assert_eq!(actual, expected, "Base 2: Coefficient {} mismatch", i);
727 }
728 }
729
730 #[test]
731 fn test_base3_decomposition() {
732 let specific_poly: Rq = vec![Zq::new(8), Zq::new(11), Zq::new(4), Zq::new(15)].into();
734 let parts = specific_poly.decompose(Zq::new(3), 2);
735
736 assert_eq!(
738 parts[0].coeffs,
739 helper::padded(&[
740 Zq::MAX, Zq::MAX, Zq::ONE, Zq::ZERO, ])
745 );
746
747 assert_eq!(
749 parts[1].coeffs,
750 helper::padded(&[
751 Zq::new(3), Zq::new(4), Zq::ONE, Zq::new(5), ])
756 );
757
758 for i in 0..4 {
760 let expected = specific_poly.coeffs[i];
761 let p0 = parts[0].coeffs[i];
762 let p1 = parts[1].coeffs[i];
763 let actual = p0 + p1 * Zq::new(3);
764 assert_eq!(actual, expected, "Base 3: Coefficient {} mismatch", i);
765 }
766 }
767
768 #[test]
769 fn test_decomposition_edge_cases() {
770 let zero_poly: Rq = vec![Zq::ZERO; 4].into();
772 let parts = zero_poly.decompose(Zq::TWO, 2);
773 assert!(
774 parts.iter().all(|p| p.is_zero()),
775 "Zero polynomial decomposition failed"
776 );
777
778 let simple_poly: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
780 let parts = simple_poly.decompose(Zq::TWO, 1);
781 assert_eq!(
782 parts.get_elements().len(),
783 1,
784 "Single part decomposition length incorrect"
785 );
786 assert_eq!(
787 parts[0], simple_poly,
788 "Single part decomposition value incorrect"
789 );
790 }
791
792 #[test]
793 fn test_large_base_decomposition() {
794 let poly: Rq = vec![Zq::new(120), Zq::new(33), Zq::new(255), Zq::new(19)].into();
796
797 let parts_base8 = poly.decompose(Zq::new(8), 2);
799
800 assert_eq!(
802 parts_base8[0].coeffs,
803 helper::padded(&[
804 Zq::ZERO, Zq::ONE, Zq::MAX, Zq::new(3), ])
809 );
810
811 assert_eq!(
813 parts_base8[1].coeffs,
814 helper::padded(&[
815 Zq::new(15), Zq::new(4), Zq::new(32), Zq::new(2), ])
820 );
821
822 for i in 0..4 {
824 let expected = poly.coeffs[i];
825 let p0 = parts_base8[0].coeffs[i];
826 let p1 = parts_base8[1].coeffs[i];
827 let actual = p0 + p1 * Zq::new(8);
828 assert_eq!(actual, expected, "Base 8: Coefficient {} mismatch", i);
829 }
830
831 let parts_base16 = poly.decompose(Zq::new(16), 2);
833
834 for i in 0..4 {
836 let expected = poly.coeffs[i];
837 let p0 = parts_base16[0].coeffs[i];
838 let p1 = parts_base16[1].coeffs[i];
839 let actual = p0 + p1 * Zq::new(16);
840 assert_eq!(actual, expected, "Base 16: Coefficient {} mismatch", i);
841 }
842 }
843
844 #[test]
845 fn test_multi_part_decomposition() {
846 let poly: Rq = vec![Zq::new(123), Zq::new(456), Zq::new(789), Zq::new(101112)].into();
848
849 let parts = poly.decompose(Zq::new(4), 3);
851 assert_eq!(parts.get_elements().len(), 3, "Should have 3 parts");
852
853 let reconstructed =
855 parts[0].clone() + parts[1].scalar_mul(Zq::new(4)) + parts[2].scalar_mul(Zq::new(16)); for i in 0..4 {
859 assert_eq!(
860 reconstructed.coeffs[i], poly.coeffs[i],
861 "3-part base 4: Coefficient {} mismatch",
862 i
863 );
864 }
865 }
866
867 #[test]
868 fn test_centering_properties() {
869 let values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
872 let poly: Rq = values
873 .iter()
874 .map(|&v| Zq::new(v))
875 .collect::<Vec<Zq>>()
876 .into();
877
878 let parts = poly.decompose(Zq::new(5), 2);
879
880 let expected_centered = [
889 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, ];
901
902 for (i, &expected) in expected_centered.iter().enumerate() {
903 assert_eq!(
904 parts[0].coeffs[i], expected,
905 "Base 5 centering: Coefficient {} incorrectly centered",
906 i
907 );
908 }
909 }
910
911 #[test]
912 fn test_extreme_values() {
913 let poly: Rq = vec![Zq::ZERO, Zq::MAX, Zq::MAX - Zq::ONE].into();
915
916 let parts = poly.decompose(Zq::new(3), 2);
918
919 let reconstructed = parts[0].clone() + parts[1].scalar_mul(Zq::new(3));
921
922 for i in 0..3 {
923 assert_eq!(
924 reconstructed.coeffs[i], poly.coeffs[i],
925 "Extreme values: Coefficient {} mismatch",
926 i
927 );
928 }
929
930 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)); }
941
942 #[test]
943 fn test_decomposition_properties() {
944 let poly: Rq = vec![
946 Zq::new(100),
947 Zq::new(200),
948 Zq::new(300),
949 Zq::new(400),
950 Zq::new(500),
951 Zq::new(600),
952 Zq::new(700),
953 Zq::new(800),
954 ]
955 .into();
956
957 for base in [2, 3, 4, 5, 8, 10, 16].iter() {
958 let parts = poly.decompose(Zq::new(*base), 2);
959 let half_base = Zq::new(*base).scale_by(Zq::TWO);
960
961 for coeff in parts[0].coeffs.iter() {
963 let abs_coeff = if *coeff > Zq::new(u32::MAX / 2) {
965 Zq::ZERO - *coeff } else {
967 *coeff
968 };
969
970 assert!(
971 abs_coeff <= half_base,
972 "Base {}: First part coefficient {} exceeds half-base {}",
973 base,
974 coeff,
975 half_base
976 );
977 }
978
979 let reconstructed = parts[0].clone() + parts[1].scalar_mul(Zq::new(*base));
981 assert_eq!(reconstructed, poly, "Base {}: Reconstruction failed", base);
982 }
983 }
984
985 #[test]
986 fn test_conjugate_automorphism() {
987 let poly1 = helper::rq_from(&[Zq::ONE, Zq::TWO, Zq::new(3)]);
988 let poly2 = helper::rq_from(&[Zq::new(4), Zq::new(5), Zq::new(6)]);
989 let inner_12 = poly1.inner_product(&poly2);
990 let conjugated_1 = poly1.conjugate_automorphism();
991 let inner_conjugated_12 = conjugated_1 * poly2;
992
993 assert_eq!(inner_conjugated_12.coeffs.len(), Rq::DEGREE);
994 assert_eq!(inner_conjugated_12.get_coefficients()[0], Zq::from(32));
995 assert_eq!(inner_conjugated_12.get_coefficients()[1], Zq::from(17));
996 assert_eq!(inner_conjugated_12.get_coefficients()[2], Zq::new(6));
997
998 let ct_inner_conjugated_12 = inner_conjugated_12.get_coefficients()[0];
1000 assert_eq!(ct_inner_conjugated_12, inner_12);
1001 }
1002}