1use std::alloc::{Allocator, Global};
2use std::marker::PhantomData;
3use std::ops::Deref;
4use std::sync::Arc;
5
6use feanor_math::algorithms::convolution::*;
7use feanor_math::iters::{multi_cartesian_product, MultiProduct};
8use feanor_math::primitive_int::*;
9use feanor_math::serialization::*;
10use feanor_math::specialization::{FiniteRingOperation, FiniteRingSpecializable};
11use feanor_math::ring::*;
12use feanor_math::homomorphism::*;
13use feanor_math::integer::*;
14use feanor_math::rings::extension::*;
15use feanor_math::rings::finite::{FiniteRing, FiniteRingStore};
16use feanor_math::rings::poly::dense_poly::DensePolyRing;
17use feanor_math::rings::zn::*;
18use feanor_math::rings::zn::zn_64::*;
19use feanor_math::seq::*;
20use feanor_math::matrix::*;
21
22use serde::Serialize;
23use serde::de::DeserializeSeed;
24use tracing::instrument;
25
26use crate::number_ring::HECyclotomicNumberRing;
27use crate::ciphertext_ring::poly_remainder::CyclotomicPolyReducer;
28use crate::cyclotomic::*;
29use crate::ciphertext_ring::double_rns_ring::{DoubleRNSRing, DoubleRNSRingBase};
30use crate::ntt::HERingConvolution;
31use crate::ntt::ntt_convolution::NTTConv;
32
33use super::serialization::{deserialize_rns_data, serialize_rns_data};
34use super::{BGFVCiphertextRing, PreparedMultiplicationRing};
35
36pub struct SingleRNSRingBase<NumberRing, A, C>
58 where NumberRing: HECyclotomicNumberRing,
59 A: Allocator + Clone,
60 C: PreparedConvolutionAlgorithm<ZnBase>
61{
62 base: DoubleRNSRing<NumberRing, A>,
66 convolutions: Vec<Arc<C>>,
68 poly_moduli: Vec<Arc<CyclotomicPolyReducer<Zn, Arc<C>>>>
70}
71
72pub type SingleRNSRing<NumberRing, A = Global, C = NTTConv<Zn, Global>> = RingValue<SingleRNSRingBase<NumberRing, A, C>>;
76
77pub struct SingleRNSRingEl<NumberRing, A, C>
81 where NumberRing: HECyclotomicNumberRing,
82 A: Allocator + Clone,
83 C: ConvolutionAlgorithm<ZnBase>
84{
85 coefficients: Vec<ZnEl, A>,
87 convolutions: PhantomData<C>,
88 number_ring: PhantomData<NumberRing>
89}
90
91pub struct SingleRNSRingPreparedMultiplicant<NumberRing, A, C>
92 where NumberRing: HECyclotomicNumberRing,
93 A: Allocator + Clone,
94 C: PreparedConvolutionAlgorithm<ZnBase>
95{
96 pub(super) element: PhantomData<SingleRNSRingEl<NumberRing, A, C>>,
97 pub(super) number_ring: PhantomData<NumberRing>,
98 pub(super) data: Vec<C::PreparedConvolutionOperand, A>
99}
100
101impl<NumberRing, C> SingleRNSRingBase<NumberRing, Global, C>
102 where NumberRing: HECyclotomicNumberRing,
103 C: HERingConvolution<Zn>
104{
105 #[instrument(skip_all)]
112 pub fn new(number_ring: NumberRing, rns_base: zn_rns::Zn<Zn, BigIntRing>) -> RingValue<Self> {
113 let max_log2_n = StaticRing::<i64>::RING.abs_log2_ceil(&(number_ring.n() as i64 * 2)).unwrap();
114 let convolutions = rns_base.as_iter().map(|Zp| Arc::new(C::new(Zp.clone(), max_log2_n))).collect();
115 Self::new_with(number_ring, rns_base, Global, convolutions)
116 }
117}
118
119impl<NumberRing, A, C> Clone for SingleRNSRingBase<NumberRing, A, C>
120 where NumberRing: HECyclotomicNumberRing + Clone,
121 A: Allocator + Clone,
122 C: HERingConvolution<Zn>
123{
124 fn clone(&self) -> Self {
125 Self {
126 base: self.base.clone(),
127 convolutions: self.convolutions.clone(),
128 poly_moduli: self.poly_moduli.clone()
129 }
130 }
131}
132
133impl<NumberRing, A, C> SingleRNSRingBase<NumberRing, A, C>
134 where NumberRing: HECyclotomicNumberRing,
135 A: Allocator + Clone,
136 C: PreparedConvolutionAlgorithm<ZnBase>
137{
138 #[instrument(skip_all)]
147 pub fn new_with(number_ring: NumberRing, rns_base: zn_rns::Zn<Zn, BigIntRing>, allocator: A, convolutions: Vec<Arc<C>>) -> RingValue<Self> {
148 assert!(rns_base.len() > 0);
149 assert_eq!(rns_base.len(), convolutions.len());
150 for i in 0..rns_base.len() {
151 assert!(convolutions[i].supports_ring(rns_base.at(i)));
152 }
153
154 let base = DoubleRNSRingBase::new_with(number_ring, rns_base, allocator);
155 let number_ring = base.get_ring().number_ring();
156 let rns_base = base.get_ring().rns_base();
157
158 RingValue::from(Self {
159 poly_moduli: rns_base.as_iter().zip(convolutions.iter()).map(|(Zp, conv)| CyclotomicPolyReducer::new(*Zp, number_ring.n() as i64, conv.clone())).map(Arc::new).collect::<Vec<_>>(),
160 base: base,
161 convolutions: convolutions,
162 })
163 }
164
165 #[instrument(skip_all)]
169 pub(super) fn reduce_modulus_partly(&self, k: usize, buffer: &mut [ZnEl], output: &mut [ZnEl]) {
170 assert_eq!(self.n(), output.len());
171 let Zp = self.rns_base().at(k);
172 for i in 0..self.n() {
173 output[i] = Zp.sum((i..buffer.len()).step_by(self.n()).map(|j| buffer[j]));
174 }
175 }
176
177 #[instrument(skip_all)]
181 pub(super) fn reduce_modulus_complete(&self, el: &mut SingleRNSRingEl<NumberRing, A, C>) {
182 let mut el_matrix = self.coefficients_as_matrix_mut(el);
183 for k in 0..self.rns_base().len() {
184 self.poly_moduli[k].remainder(el_matrix.row_mut_at(k));
185 }
186 }
187
188 pub fn rns_base(&self) -> &zn_rns::Zn<Zn, BigIntRing> {
189 self.base.get_ring().rns_base()
190 }
191
192 fn check_valid(&self, el: &SingleRNSRingEl<NumberRing, A, C>) {
193 assert_eq!(self.n() as usize * self.rns_base().len(), el.coefficients.len());
194 }
195
196 pub(super) fn coefficients_as_matrix<'a>(&self, element: &'a SingleRNSRingEl<NumberRing, A, C>) -> Submatrix<'a, AsFirstElement<ZnEl>, ZnEl> {
197 Submatrix::from_1d(&element.coefficients, self.rns_base().len(), self.n())
198 }
199
200 pub(super) fn coefficients_as_matrix_mut<'a>(&self, element: &'a mut SingleRNSRingEl<NumberRing, A, C>) -> SubmatrixMut<'a, AsFirstElement<ZnEl>, ZnEl> {
201 SubmatrixMut::from_1d(&mut element.coefficients, self.rns_base().len(), self.n())
202 }
203
204 pub fn to_matrix<'a>(&self, element: &'a mut SingleRNSRingEl<NumberRing, A, C>) -> Submatrix<'a, AsFirstElement<ZnEl>, ZnEl> {
205 self.reduce_modulus_complete(element);
206 return self.coefficients_as_matrix(element).restrict_cols(0..self.rank());
207 }
208
209 pub fn allocator(&self) -> &A {
210 self.base.get_ring().allocator()
211 }
212
213 pub fn convolutions<'a>(&'a self) -> impl VectorFn<&'a C> + use<'a, NumberRing, A, C> {
214 self.convolutions.as_fn().map_fn(|conv| &**conv)
215 }
216}
217
218impl<NumberRing, A, C> PreparedMultiplicationRing for SingleRNSRingBase<NumberRing, A, C>
219 where NumberRing: HECyclotomicNumberRing,
220 A: Allocator + Clone,
221 C: PreparedConvolutionAlgorithm<ZnBase>
222{
223 type PreparedMultiplicant = SingleRNSRingPreparedMultiplicant<NumberRing, A, C>;
224
225 #[instrument(skip_all)]
226 fn prepare_multiplicant(&self, el: &SingleRNSRingEl<NumberRing, A, C>) -> SingleRNSRingPreparedMultiplicant<NumberRing, A, C> {
227 let el_as_matrix = self.coefficients_as_matrix(el);
228 let mut result = Vec::new_in(self.allocator().clone());
229 result.extend(self.rns_base().as_iter().enumerate().map(|(i, Zp)| self.convolutions[i].prepare_convolution_operand(el_as_matrix.row_at(i), Zp)));
230 SingleRNSRingPreparedMultiplicant {
231 element: PhantomData,
232 data: result,
233 number_ring: PhantomData
234 }
235 }
236
237 #[instrument(skip_all)]
238 fn mul_prepared(&self, lhs: &SingleRNSRingPreparedMultiplicant<NumberRing, A, C>, rhs: &SingleRNSRingPreparedMultiplicant<NumberRing, A, C>) -> SingleRNSRingEl<NumberRing, A, C> {
239 let mut unreduced_result = Vec::with_capacity_in(2 * self.n(), self.allocator());
240 let mut result = self.zero();
241
242 for k in 0..self.rns_base().len() {
243 let Zp = self.rns_base().at(k);
244 unreduced_result.clear();
245 unreduced_result.resize_with(self.n() * 2, || Zp.zero());
246
247 self.convolutions[k].compute_convolution_prepared(
248 rhs.data.at(k),
249 lhs.data.at(k),
250 &mut unreduced_result,
251 Zp
252 );
253 self.reduce_modulus_partly(k, &mut unreduced_result, self.coefficients_as_matrix_mut(&mut result).row_mut_at(k));
254 }
255 return result;
256 }
257
258 #[instrument(skip_all)]
259 fn inner_product_prepared<'a, I>(&self, parts: I) -> Self::Element
260 where I: IntoIterator<Item = (&'a Self::PreparedMultiplicant, &'a Self::PreparedMultiplicant)>,
261 Self: 'a
262 {
263 let mut result = self.zero();
264 let mut unreduced_result = Vec::with_capacity_in(2 * self.n(), self.allocator());
265 let parts = parts.into_iter().collect::<Vec<_>>();
266 for k in 0..self.rns_base().len() {
267 let Zp = self.rns_base().at(k);
268 unreduced_result.clear();
269 unreduced_result.resize_with(self.n() * 2, || Zp.zero());
270 self.convolutions[k].compute_convolution_inner_product_prepared(parts.iter().copied().map(|(lhs, rhs)| (&lhs.data[k], &rhs.data[k])), &mut unreduced_result, Zp);
271 self.reduce_modulus_partly(k, &mut unreduced_result, self.coefficients_as_matrix_mut(&mut result).row_mut_at(k));
272 }
273 return result;
274 }
275}
276
277impl<NumberRing, A, C> BGFVCiphertextRing for SingleRNSRingBase<NumberRing, A, C>
278 where NumberRing: HECyclotomicNumberRing,
279 A: Allocator + Clone,
280 C: PreparedConvolutionAlgorithm<ZnBase>
281{
282 type NumberRing = NumberRing;
283
284 fn number_ring(&self) -> &NumberRing {
285 self.base.get_ring().number_ring()
286 }
287
288 #[instrument(skip_all)]
289 fn drop_rns_factor(&self, drop_rns_factors: &[usize]) -> Self {
290 Self {
291 base: self.base.get_ring().drop_rns_factor(drop_rns_factors),
292 convolutions: self.convolutions.iter().enumerate().filter(|(i, _)| !drop_rns_factors.contains(i)).map(|(_, conv)| conv.clone()).collect(),
293 poly_moduli: self.poly_moduli.iter().enumerate().filter(|(i, _)| !drop_rns_factors.contains(i)).map(|(_, modulus)| modulus.clone()).collect()
294 }
295 }
296
297 #[instrument(skip_all)]
298 fn drop_rns_factor_element(&self, from: &Self, drop_factors: &[usize], value: Self::Element) -> Self::Element {
299 assert_eq!(self.n(), from.n());
300 assert_eq!(self.base_ring().len() + drop_factors.len(), from.base_ring().len());
301 assert!(drop_factors.iter().all(|i| *i < from.base_ring().len()));
302
303 let mut result = self.zero();
304 let mut result_as_matrix = self.coefficients_as_matrix_mut(&mut result);
305 debug_assert_eq!(self.base_ring().len(), result_as_matrix.row_count());
306 debug_assert_eq!(self.n(), result_as_matrix.col_count());
307
308 let value_as_matrix = from.coefficients_as_matrix(&value);
309 debug_assert_eq!(from.base_ring().len(), value_as_matrix.row_count());
310 debug_assert_eq!(from.n(), value_as_matrix.col_count());
311
312 let mut i_self = 0;
313 for i_from in 0..from.base_ring().len() {
314 if drop_factors.contains(&i_from) {
315 continue;
316 }
317 assert!(self.base_ring().at(i_self).get_ring() == from.base_ring().at(i_from).get_ring());
318 for j in 0..result_as_matrix.col_count() {
319 *result_as_matrix.at_mut(i_self, j) = *value_as_matrix.at(i_from, j);
320 }
321 i_self += 1;
322 }
323
324 return result;
325 }
326
327 #[instrument(skip_all)]
328 fn drop_rns_factor_prepared(&self, from: &Self, drop_factors: &[usize], value: Self::PreparedMultiplicant) -> Self::PreparedMultiplicant {
329 assert_eq!(self.n(), from.n());
330 assert_eq!(self.base_ring().len() + drop_factors.len(), from.base_ring().len());
331 assert!(drop_factors.iter().all(|i| *i < from.base_ring().len()));
332 debug_assert_eq!(from.base_ring().len(), value.data.len());
333
334 let mut result = Vec::with_capacity_in(self.base_ring().len(), self.allocator().clone());
335 let mut i_self = 0;
336 for (i_from, operand) in value.data.into_iter().enumerate() {
337 if drop_factors.contains(&i_from) {
338 continue;
339 }
340 assert!(self.base_ring().at(i_self).get_ring() == from.base_ring().at(i_from).get_ring());
341 result.push(operand);
342 i_self += 1;
343 }
344
345 return SingleRNSRingPreparedMultiplicant {
346 data: result,
347 element: PhantomData,
348 number_ring: PhantomData
349 };
350 }
351
352 fn small_generating_set_len(&self) -> usize {
353 self.n()
354 }
355
356 fn as_representation_wrt_small_generating_set<V>(&self, x: &Self::Element, mut output: SubmatrixMut<V, ZnEl>)
357 where V: AsPointerToSlice<ZnEl>
358 {
359 let matrix = self.coefficients_as_matrix(x);
360 assert_eq!(output.row_count(), matrix.row_count());
361 assert_eq!(output.col_count(), matrix.col_count());
362 for i in 0..matrix.row_count() {
363 for j in 0..matrix.col_count() {
364 *output.at_mut(i, j) = *matrix.at(i, j);
365 }
366 }
367 }
368
369 fn partial_representation_wrt_small_generating_set<V>(&self, x: &Self::Element, row_indices: &[usize], mut output: SubmatrixMut<V, ZnEl>)
370 where V: AsPointerToSlice<ZnEl>
371 {
372 let matrix = self.coefficients_as_matrix(x);
373 assert_eq!(output.row_count(), row_indices.len());
374 assert_eq!(output.col_count(), matrix.col_count());
375 for (i_out, i_in) in row_indices.iter().enumerate() {
376 for j in 0..matrix.col_count() {
377 *output.at_mut(i_out, j) = *matrix.at(*i_in, j);
378 }
379 }
380 }
381
382 #[instrument(skip_all)]
383 fn from_representation_wrt_small_generating_set<V>(&self, data: Submatrix<V, ZnEl>) -> Self::Element
384 where V: AsPointerToSlice<ZnEl>
385 {
386 let mut result = self.zero();
387 let mut result_matrix = self.coefficients_as_matrix_mut(&mut result);
388 assert_eq!(result_matrix.row_count(), data.row_count());
389 assert_eq!(result_matrix.col_count(), data.col_count());
390 for i in 0..result_matrix.row_count() {
391 let Zp = self.rns_base().at(i);
392 for j in 0..result_matrix.col_count() {
393 *result_matrix.at_mut(i, j) = Zp.clone_el(data.at(i, j));
394 }
395 }
396 return result;
397 }
398
399 #[instrument(skip_all)]
400 fn two_by_two_convolution(&self, lhs: [&Self::Element; 2], rhs: [&Self::Element; 2]) -> [Self::Element; 3] {
401 enum OwnedOrBorrowed<'a, T> {
402 Owned(T),
403 Borrowed(&'a T)
404 }
405 impl<'a, T> Deref for OwnedOrBorrowed<'a, T> {
406 type Target = T;
407 fn deref(&self) -> &Self::Target {
408 match self {
409 Self::Owned(x) => x,
410 Self::Borrowed(x) => *x
411 }
412 }
413 }
414 let lhs0_prepared = self.prepare_multiplicant(lhs[0]);
415 let lhs1_prepared = if std::ptr::eq(lhs[0], lhs[1]) {
416 OwnedOrBorrowed::Borrowed(&lhs0_prepared)
417 } else {
418 OwnedOrBorrowed::Owned(self.prepare_multiplicant(lhs[1]))
419 };
420 let rhs0_prepared = if std::ptr::eq(lhs[0], rhs[0]) {
421 OwnedOrBorrowed::Borrowed(&lhs0_prepared)
422 } else if std::ptr::eq(lhs[1], rhs[0]) {
423 OwnedOrBorrowed::Borrowed(&*lhs1_prepared)
424 } else {
425 OwnedOrBorrowed::Owned(self.prepare_multiplicant(rhs[0]))
426 };
427 let rhs1_prepared = if std::ptr::eq(lhs[0], rhs[1]) {
428 OwnedOrBorrowed::Borrowed(&lhs0_prepared)
429 } else if std::ptr::eq(lhs[1], rhs[1]) {
430 OwnedOrBorrowed::Borrowed(&*lhs1_prepared)
431 } else if std::ptr::eq(rhs[0], rhs[1]) {
432 OwnedOrBorrowed::Borrowed(&*rhs0_prepared)
433 } else {
434 OwnedOrBorrowed::Owned(self.prepare_multiplicant(rhs[1]))
435 };
436 return [
437 self.mul_prepared(&lhs0_prepared, &*rhs0_prepared),
438 self.inner_product_prepared([(&lhs0_prepared, &*rhs1_prepared), (&*lhs1_prepared, &*rhs0_prepared)]),
439 self.mul_prepared(&*lhs1_prepared, &*rhs1_prepared)
440 ];
441 }
442}
443
444pub struct SingleRNSRingBaseElVectorRepresentation<'a, NumberRing, A, C>
445 where NumberRing: HECyclotomicNumberRing,
446 A: Allocator + Clone,
447 C: PreparedConvolutionAlgorithm<ZnBase>
448{
449 element: SingleRNSRingEl<NumberRing, A, C>,
450 ring: &'a SingleRNSRingBase<NumberRing, A, C>
451}
452
453impl<'a, NumberRing, A, C> VectorFn<El<zn_rns::Zn<Zn, BigIntRing>>> for SingleRNSRingBaseElVectorRepresentation<'a, NumberRing, A, C>
454 where NumberRing: HECyclotomicNumberRing,
455 A: Allocator + Clone,
456 C: PreparedConvolutionAlgorithm<ZnBase>
457{
458 fn len(&self) -> usize {
459 self.ring.rank()
460 }
461
462 fn at(&self, i: usize) -> El<zn_rns::Zn<Zn, BigIntRing>> {
463 assert!(i < self.len());
464 self.ring.rns_base().from_congruence(self.ring.coefficients_as_matrix(&self.element).col_at(i).as_iter().enumerate().map(|(i, x)| self.ring.rns_base().at(i).clone_el(x)))
465 }
466}
467
468impl<NumberRing, A, C> PartialEq for SingleRNSRingBase<NumberRing, A, C>
469 where NumberRing: HECyclotomicNumberRing,
470 A: Allocator + Clone,
471 C: PreparedConvolutionAlgorithm<ZnBase>
472{
473 fn eq(&self, other: &Self) -> bool {
474 self.number_ring() == other.number_ring() && self.rns_base().get_ring() == other.rns_base().get_ring()
475 }
476}
477
478impl<NumberRing, A, C> RingBase for SingleRNSRingBase<NumberRing, A, C>
479 where NumberRing: HECyclotomicNumberRing,
480 A: Allocator + Clone,
481 C: PreparedConvolutionAlgorithm<ZnBase>
482{
483 type Element = SingleRNSRingEl<NumberRing, A, C>;
484
485 fn clone_el(&self, val: &Self::Element) -> Self::Element {
486 self.check_valid(val);
487 let mut result = Vec::with_capacity_in(val.coefficients.len(), self.allocator().clone());
488 result.extend((0..val.coefficients.len()).map(|i| self.rns_base().at(i / self.n()).clone_el(&val.coefficients[i])));
489 SingleRNSRingEl {
490 coefficients: result,
491 number_ring: PhantomData,
492 convolutions: PhantomData
493 }
494 }
495
496 #[instrument(skip_all)]
497 fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
498 self.check_valid(lhs);
499 self.check_valid(rhs);
500 let mut lhs_matrix = self.coefficients_as_matrix_mut(lhs);
501 let rhs_matrix = self.coefficients_as_matrix(rhs);
502 for i in 0..self.rns_base().len() {
503 for j in 0..self.n() {
504 self.rns_base().at(i).add_assign_ref(lhs_matrix.at_mut(i, j), rhs_matrix.at(i, j));
505 }
506 }
507 }
508
509 fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
510 self.add_assign_ref(lhs, &rhs);
511 }
512
513 #[instrument(skip_all)]
514 fn sub_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
515 self.check_valid(lhs);
516 self.check_valid(rhs);
517 let mut lhs_matrix = self.coefficients_as_matrix_mut(lhs);
518 let rhs_matrix = self.coefficients_as_matrix(rhs);
519 for i in 0..self.rns_base().len() {
520 for j in 0..self.n() {
521 self.rns_base().at(i).sub_assign_ref(lhs_matrix.at_mut(i, j), rhs_matrix.at(i, j));
522 }
523 }
524 }
525
526 #[instrument(skip_all)]
527 fn negate_inplace(&self, lhs: &mut Self::Element) {
528 self.check_valid(lhs);
529 let mut lhs_matrix = self.coefficients_as_matrix_mut(lhs);
530 for i in 0..self.rns_base().len() {
531 for j in 0..self.n() {
532 self.rns_base().at(i).negate_inplace(lhs_matrix.at_mut(i, j));
533 }
534 }
535 }
536
537 fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
538 self.mul_assign_ref(lhs, &rhs);
539 }
540
541 #[instrument(skip_all)]
542 fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
543 self.check_valid(lhs);
544 self.check_valid(rhs);
545 let mut unreduced_result = Vec::with_capacity_in(2 * self.n(), self.allocator());
546
547 let rhs_matrix = self.coefficients_as_matrix(rhs);
548 let mut lhs_matrix = self.coefficients_as_matrix_mut(lhs);
549 for k in 0..self.rns_base().len() {
550 let Zp = self.rns_base().at(k);
551 unreduced_result.clear();
552 unreduced_result.resize_with(self.n() * 2, || Zp.zero());
553
554 self.convolutions[k].compute_convolution(
555 rhs_matrix.row_at(k),
556 lhs_matrix.row_at(k),
557 &mut unreduced_result,
558 Zp
559 );
560 self.reduce_modulus_partly(k, &mut unreduced_result, lhs_matrix.row_mut_at(k));
561 }
562 }
563
564 #[instrument(skip_all)]
565 fn from_int(&self, value: i32) -> Self::Element {
566 self.from(self.base_ring().get_ring().from_int(value))
567 }
568
569 #[instrument(skip_all)]
570 fn mul_assign_int(&self, lhs: &mut Self::Element, rhs: i32) {
571 self.check_valid(lhs);
572 let mut lhs_matrix = self.coefficients_as_matrix_mut(lhs);
573 for i in 0..self.rns_base().len() {
574 let rhs_mod_p = self.rns_base().at(i).get_ring().from_int(rhs);
575 for j in 0..self.n() {
576 self.rns_base().at(i).mul_assign_ref(lhs_matrix.at_mut(i, j), &rhs_mod_p);
577 }
578 }
579 }
580
581 fn zero(&self) -> Self::Element {
582 let mut result = Vec::with_capacity_in(self.n() * self.rns_base().len(), self.allocator().clone());
583 result.extend(self.rns_base().as_iter().flat_map(|Zp| (0..self.n()).map(|_| Zp.zero())));
584 return SingleRNSRingEl {
585 coefficients: result,
586 number_ring: PhantomData,
587 convolutions: PhantomData
588 };
589 }
590
591 #[instrument(skip_all)]
592 fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
593 let mut lhs = self.clone_el(lhs);
594 let lhs = self.to_matrix(&mut lhs);
595 let mut rhs = self.clone_el(rhs);
596 let rhs = self.to_matrix(&mut rhs);
597 for i in 0..self.rns_base().len() {
598 for j in 0..self.rank() {
599 if !self.rns_base().at(i).eq_el(lhs.at(i, j), rhs.at(i, j)) {
600 return false;
601 }
602 }
603 }
604 return true;
605 }
606
607 fn is_commutative(&self) -> bool { true }
608 fn is_noetherian(&self) -> bool { true }
609 fn is_approximate(&self) -> bool { false }
610
611 fn dbg_within<'a>(&self, value: &Self::Element, out: &mut std::fmt::Formatter<'a>, env: EnvBindingStrength) -> std::fmt::Result {
612 let poly_ring = DensePolyRing::new(self.base_ring(), "X");
613 poly_ring.get_ring().dbg_within(&RingRef::new(self).poly_repr(&poly_ring, value, self.base_ring().identity()), out, env)
614 }
615
616 fn characteristic<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
617 where I::Type: IntegerRing
618 {
619 self.base_ring().characteristic(ZZ)
620 }
621}
622
623impl<NumberRing, A, C> RingExtension for SingleRNSRingBase<NumberRing, A, C>
624 where NumberRing: HECyclotomicNumberRing,
625 A: Allocator + Clone,
626 C: PreparedConvolutionAlgorithm<ZnBase>
627{
628 type BaseRing = zn_rns::Zn<Zn, BigIntRing>;
629
630 fn base_ring<'a>(&'a self) -> &'a Self::BaseRing {
631 self.rns_base()
632 }
633
634 #[instrument(skip_all)]
635 fn from(&self, x: El<Self::BaseRing>) -> Self::Element {
636 let mut result = self.zero();
637 let mut result_matrix = self.coefficients_as_matrix_mut(&mut result);
638 let x_congruence = self.base_ring().get_congruence(&x);
639 for (i, Zp) in self.rns_base().as_iter().enumerate() {
640 *result_matrix.at_mut(i, 0) = Zp.clone_el(x_congruence.at(i));
641 }
642 return result;
643 }
644
645 #[instrument(skip_all)]
646 fn mul_assign_base(&self, lhs: &mut Self::Element, rhs: &El<Self::BaseRing>) {
647 let x_congruence = self.rns_base().get_congruence(rhs);
648 let mut lhs_matrix = self.coefficients_as_matrix_mut(lhs);
649 for (i, Zp) in self.rns_base().as_iter().enumerate() {
650 for j in 0..self.n() {
651 Zp.mul_assign_ref(lhs_matrix.at_mut(i, j), x_congruence.at(i));
652 }
653 }
654 }
655}
656
657impl<NumberRing, A, C> FreeAlgebra for SingleRNSRingBase<NumberRing, A, C>
658 where NumberRing: HECyclotomicNumberRing,
659 A: Allocator + Clone,
660 C: PreparedConvolutionAlgorithm<ZnBase>
661{
662 type VectorRepresentation<'a> = SingleRNSRingBaseElVectorRepresentation<'a, NumberRing, A, C>
663 where Self: 'a;
664
665 #[instrument(skip_all)]
666 fn canonical_gen(&self) -> SingleRNSRingEl<NumberRing, A, C> {
667 let mut result = self.zero();
668 let mut result_matrix = self.coefficients_as_matrix_mut(&mut result);
669 for (i, Zp) in self.rns_base().as_iter().enumerate() {
670 *result_matrix.at_mut(i, 1) = Zp.one();
671 }
672 return result;
673 }
674
675 fn rank(&self) -> usize {
676 self.number_ring().rank()
677 }
678
679 #[instrument(skip_all)]
680 fn wrt_canonical_basis<'a>(&'a self, el: &'a SingleRNSRingEl<NumberRing, A, C>) -> Self::VectorRepresentation<'a> {
681 let mut reduced_el = self.clone_el(el);
682 self.reduce_modulus_complete(&mut reduced_el);
683 SingleRNSRingBaseElVectorRepresentation {
684 ring: self,
685 element: reduced_el
686 }
687 }
688
689 #[instrument(skip_all)]
690 fn from_canonical_basis<V>(&self, vec: V) -> SingleRNSRingEl<NumberRing, A, C>
691 where V: IntoIterator<Item = El<Self::BaseRing>>
692 {
693 let mut result = self.zero();
694 let mut result_matrix = self.coefficients_as_matrix_mut(&mut result);
695 for (j, x) in vec.into_iter().enumerate() {
696 assert!(j < self.rank());
697 let congruence = self.base_ring().get_ring().get_congruence(&x);
698 for i in 0..self.rns_base().len() {
699 *result_matrix.at_mut(i, j) = self.rns_base().at(i).clone_el(congruence.at(i));
700 }
701 }
702 return result;
703 }
704}
705
706impl<NumberRing, A, C> CyclotomicRing for SingleRNSRingBase<NumberRing, A, C>
707 where NumberRing: HECyclotomicNumberRing,
708 A: Allocator + Clone,
709 C: PreparedConvolutionAlgorithm<ZnBase>
710{
711 fn n(&self) -> usize {
712 self.base.n()
713 }
714
715 fn galois_group(&self) -> CyclotomicGaloisGroup {
716 self.base.galois_group()
717 }
718
719 #[instrument(skip_all)]
720 fn apply_galois_action(&self, el: &Self::Element, s: CyclotomicGaloisGroupEl) -> Self::Element {
721 let self_ref = RingRef::new(self);
722 let iso = self.base.can_iso(&self_ref).unwrap();
723 let el_double_rns = iso.inv().map_ref(el);
724 let result_double_rns = self.base.apply_galois_action(&el_double_rns, s);
725 return iso.map(result_double_rns);
726 }
727
728 #[instrument(skip_all)]
729 fn apply_galois_action_many<'a>(&'a self, el: &Self::Element, gs: &'a [CyclotomicGaloisGroupEl]) -> Vec<Self::Element> {
730 let self_ref = RingRef::new(self);
731 let iso = (&self.base).into_can_iso(self_ref).ok().unwrap();
732 let el_double_rns = iso.inv().map_ref(el);
733 let result_double_rns = self.base.apply_galois_action_many(&el_double_rns, gs);
734 return result_double_rns.into_iter().map(|x| iso.map(x)).collect::<Vec<_>>();
735 }
736}
737
738pub struct WRTCanonicalBasisElementCreator<'a, NumberRing, A, C>
739 where NumberRing: HECyclotomicNumberRing,
740 A: Allocator + Clone,
741 C: PreparedConvolutionAlgorithm<ZnBase>
742{
743 ring: &'a SingleRNSRingBase<NumberRing, A, C>
744}
745
746impl<'a, 'b, NumberRing, A, C> Clone for WRTCanonicalBasisElementCreator<'a, NumberRing, A, C>
747 where NumberRing: HECyclotomicNumberRing,
748 A: Allocator + Clone,
749 C: PreparedConvolutionAlgorithm<ZnBase>
750{
751 fn clone(&self) -> Self {
752 Self { ring: self.ring }
753 }
754}
755
756impl<'a, 'b, NumberRing, A, C> Fn<(&'b [El<zn_rns::Zn<Zn, BigIntRing>>],)> for WRTCanonicalBasisElementCreator<'a, NumberRing, A, C>
757 where NumberRing: HECyclotomicNumberRing,
758 A: Allocator + Clone,
759 C: PreparedConvolutionAlgorithm<ZnBase>
760{
761 extern "rust-call" fn call(&self, args: (&'b [El<zn_rns::Zn<Zn, BigIntRing>>],)) -> Self::Output {
762 self.ring.from_canonical_basis(args.0.iter().map(|x| self.ring.base_ring().clone_el(x)))
763 }
764}
765
766impl<'a, 'b, NumberRing, A, C> FnMut<(&'b [El<zn_rns::Zn<Zn, BigIntRing>>],)> for WRTCanonicalBasisElementCreator<'a, NumberRing, A, C>
767 where NumberRing: HECyclotomicNumberRing,
768 A: Allocator + Clone,
769 C: PreparedConvolutionAlgorithm<ZnBase>
770{
771 extern "rust-call" fn call_mut(&mut self, args: (&'b [El<zn_rns::Zn<Zn, BigIntRing>>],)) -> Self::Output {
772 self.call(args)
773 }
774}
775
776impl<'a, 'b, NumberRing, A, C> FnOnce<(&'b [El<zn_rns::Zn<Zn, BigIntRing>>],)> for WRTCanonicalBasisElementCreator<'a, NumberRing, A, C>
777 where NumberRing: HECyclotomicNumberRing,
778 A: Allocator + Clone,
779 C: PreparedConvolutionAlgorithm<ZnBase>
780{
781 type Output = SingleRNSRingEl<NumberRing, A, C>;
782
783 extern "rust-call" fn call_once(self, args: (&'b [El<zn_rns::Zn<Zn, BigIntRing>>],)) -> Self::Output {
784 self.call(args)
785 }
786}
787
788impl<NumberRing, A, C> FiniteRingSpecializable for SingleRNSRingBase<NumberRing, A, C>
789 where NumberRing: HECyclotomicNumberRing,
790 A: Allocator + Clone,
791 C: PreparedConvolutionAlgorithm<ZnBase>
792{
793 fn specialize<O: FiniteRingOperation<Self>>(op: O) -> Result<O::Output, ()> {
794 Ok(op.execute())
795 }
796}
797
798impl<NumberRing, A, C> SerializableElementRing for SingleRNSRingBase<NumberRing, A, C>
799 where NumberRing: HECyclotomicNumberRing,
800 A: Allocator + Clone,
801 C: PreparedConvolutionAlgorithm<ZnBase>
802{
803 fn serialize<S>(&self, el: &Self::Element, serializer: S) -> Result<S::Ok, S::Error>
804 where S: serde::Serializer
805 {
806 if serializer.is_human_readable() {
807 return SerializableNewtype::new("RingEl", SerializableSeq::new(self.wrt_canonical_basis(el).map_fn(|c| SerializeOwnedWithRing::new(c, self.base_ring())))).serialize(serializer);
808 } else {
809 let mut reduced = self.clone_el(el);
810 self.reduce_modulus_complete(&mut reduced);
811 let reduced_as_matrix = self.coefficients_as_matrix(&reduced).restrict_cols(0..self.rank());
812 return SerializableNewtype::new("SingleRNSEl", &serialize_rns_data(self.base_ring(), reduced_as_matrix)).serialize(serializer);
813 }
814 }
815
816 fn deserialize<'de, D>(&self, deserializer: D) -> Result<Self::Element, D::Error>
817 where D: serde::Deserializer<'de>
818 {
819 if deserializer.is_human_readable() {
820 let data = DeserializeSeedNewtype::new("RingEl", DeserializeSeedSeq::new(
821 (0..self.rank()).map(|_| DeserializeWithRing::new(self.base_ring())),
822 Vec::with_capacity_in(self.rank(), self.allocator()),
823 |mut current, next| { current.push(next); current }
824 )).deserialize(deserializer)?;
825 if data.len() != self.rank() {
826 return Err(serde::de::Error::invalid_length(data.len(), &format!("expected a sequence of {} elements of Z/qZ", self.rank()).as_str()));
827 }
828 return Ok(self.from_canonical_basis(data.into_iter()));
829 } else {
830 let mut result = self.zero();
831 let result_as_matrix = self.coefficients_as_matrix_mut(&mut result).restrict_cols(0..self.rank());
832 DeserializeSeedNewtype::new("SingleRNSEl", deserialize_rns_data(self.base_ring(), result_as_matrix)).deserialize(deserializer)?;
833 return Ok(result);
834 }
835 }
836}
837
838impl<NumberRing, A, C> FiniteRing for SingleRNSRingBase<NumberRing, A, C>
839 where NumberRing: HECyclotomicNumberRing,
840 A: Allocator + Clone,
841 C: PreparedConvolutionAlgorithm<ZnBase>
842{
843 type ElementsIter<'a> = MultiProduct<
844 <zn_rns::ZnBase<Zn, BigIntRing> as FiniteRing>::ElementsIter<'a>,
845 WRTCanonicalBasisElementCreator<'a, NumberRing, A, C>,
846 CloneRingEl<&'a zn_rns::Zn<Zn, BigIntRing>>,
847 SingleRNSRingEl<NumberRing, A, C>
848 > where Self: 'a;
849
850 fn elements<'a>(&'a self) -> Self::ElementsIter<'a> {
851 multi_cartesian_product((0..self.rank()).map(|_| self.base_ring().elements()), WRTCanonicalBasisElementCreator { ring: self }, CloneRingEl(self.base_ring()))
852 }
853
854 fn size<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
855 where I::Type: IntegerRing
856 {
857 let modulus = self.base_ring().size(ZZ)?;
858 if ZZ.get_ring().representable_bits().is_none() || ZZ.get_ring().representable_bits().unwrap() >= self.rank() * ZZ.abs_log2_ceil(&modulus).unwrap() {
859 Some(ZZ.pow(modulus, self.rank()))
860 } else {
861 None
862 }
863 }
864
865 fn random_element<G: FnMut() -> u64>(&self, mut rng: G) -> <Self as RingBase>::Element {
866 let mut result = self.zero();
867 let mut result_matrix = self.coefficients_as_matrix_mut(&mut result);
868 for j in 0..self.rank() {
869 for i in 0..self.rns_base().len() {
870 *result_matrix.at_mut(i, j) = self.rns_base().at(i).random_element(&mut rng);
871 }
872 }
873 return result;
874 }
875}
876
877impl<NumberRing, A1, A2, C1, C2> CanHomFrom<SingleRNSRingBase<NumberRing, A2, C2>> for SingleRNSRingBase<NumberRing, A1, C1>
878 where NumberRing: HECyclotomicNumberRing,
879 A1: Allocator + Clone,
880 A2: Allocator + Clone,
881 C1: PreparedConvolutionAlgorithm<ZnBase>,
882 C2: PreparedConvolutionAlgorithm<ZnBase>
883{
884 type Homomorphism = Vec<<ZnBase as CanHomFrom<ZnBase>>::Homomorphism>;
885
886 fn has_canonical_hom(&self, from: &SingleRNSRingBase<NumberRing, A2, C2>) -> Option<Self::Homomorphism> {
887 if self.rns_base().len() == from.rns_base().len() && self.number_ring() == from.number_ring() {
888 (0..self.rns_base().len()).map(|i| self.rns_base().at(i).get_ring().has_canonical_hom(from.rns_base().at(i).get_ring()).ok_or(())).collect::<Result<Vec<_>, ()>>().ok()
889 } else {
890 None
891 }
892 }
893
894 fn map_in(&self, from: &SingleRNSRingBase<NumberRing, A2, C2>, el: <SingleRNSRingBase<NumberRing, A2, C2> as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
895 self.map_in_ref(from, &el, hom)
896 }
897
898 fn map_in_ref(&self, from: &SingleRNSRingBase<NumberRing, A2, C2>, el: &<SingleRNSRingBase<NumberRing, A2, C2> as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
899 let el_as_matrix = from.coefficients_as_matrix(&el);
900 let mut result = self.zero();
901 let mut result_matrix = self.coefficients_as_matrix_mut(&mut result);
902 for (i, Zp) in self.rns_base().as_iter().enumerate() {
903 for j in 0..self.n() {
904 *result_matrix.at_mut(i, j) = Zp.get_ring().map_in_ref(from.rns_base().at(i).get_ring(), el_as_matrix.at(i, j), &hom[i]);
905 }
906 }
907 return result;
908 }
909}
910
911impl<NumberRing, A1, A2, C1> CanHomFrom<DoubleRNSRingBase<NumberRing, A2>> for SingleRNSRingBase<NumberRing, A1, C1>
912 where NumberRing: HECyclotomicNumberRing,
913 A1: Allocator + Clone,
914 A2: Allocator + Clone,
915 C1: PreparedConvolutionAlgorithm<ZnBase>
916{
917 type Homomorphism = <DoubleRNSRingBase<NumberRing, A2> as CanIsoFromTo<Self>>::Isomorphism;
918
919 fn has_canonical_hom(&self, from: &DoubleRNSRingBase<NumberRing, A2>) -> Option<Self::Homomorphism> {
920 from.has_canonical_iso(self)
921 }
922
923 fn map_in(&self, from: &DoubleRNSRingBase<NumberRing, A2>, el: <DoubleRNSRingBase<NumberRing, A2> as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
924 from.map_out(self, el, hom)
925 }
926}
927
928impl<NumberRing, A1, A2, C1> CanIsoFromTo<DoubleRNSRingBase<NumberRing, A2>> for SingleRNSRingBase<NumberRing, A1, C1>
929 where NumberRing: HECyclotomicNumberRing,
930 A1: Allocator + Clone,
931 A2: Allocator + Clone,
932 C1: PreparedConvolutionAlgorithm<ZnBase>
933{
934 type Isomorphism = <DoubleRNSRingBase<NumberRing, A2> as CanHomFrom<Self>>::Homomorphism;
935
936 fn has_canonical_iso(&self, from: &DoubleRNSRingBase<NumberRing, A2>) -> Option<Self::Isomorphism> {
937 from.has_canonical_hom(self)
938 }
939
940 fn map_out(&self, from: &DoubleRNSRingBase<NumberRing, A2>, el: Self::Element, iso: &Self::Isomorphism) -> <DoubleRNSRingBase<NumberRing, A2> as RingBase>::Element {
941 from.map_in(self, el, iso)
942 }
943}
944
945impl<NumberRing, A1, A2, C1, C2> CanIsoFromTo<SingleRNSRingBase<NumberRing, A2, C2>> for SingleRNSRingBase<NumberRing, A1, C1>
946 where NumberRing: HECyclotomicNumberRing,
947 A1: Allocator + Clone,
948 A2: Allocator + Clone,
949 C1: PreparedConvolutionAlgorithm<ZnBase>,
950 C2: PreparedConvolutionAlgorithm<ZnBase>
951{
952 type Isomorphism = Vec<<ZnBase as CanIsoFromTo<ZnBase>>::Isomorphism>;
953
954 fn has_canonical_iso(&self, from: &SingleRNSRingBase<NumberRing, A2, C2>) -> Option<Self::Isomorphism> {
955 if self.rns_base().len() == from.rns_base().len() && self.number_ring() == from.number_ring() {
956 (0..self.rns_base().len()).map(|i| self.rns_base().at(i).get_ring().has_canonical_iso(from.rns_base().at(i).get_ring()).ok_or(())).collect::<Result<Vec<_>, ()>>().ok()
957 } else {
958 None
959 }
960 }
961
962 fn map_out(&self, from: &SingleRNSRingBase<NumberRing, A2, C2>, el: Self::Element, iso: &Self::Isomorphism) -> <SingleRNSRingBase<NumberRing, A2, C2> as RingBase>::Element {
963 let el_as_matrix = self.coefficients_as_matrix(&el);
964 let mut result = from.zero();
965 let mut result_matrix = from.coefficients_as_matrix_mut(&mut result);
966 for (i, Zp) in self.rns_base().as_iter().enumerate() {
967 for j in 0..self.n() {
968 *result_matrix.at_mut(i, j) = Zp.get_ring().map_out(from.rns_base().at(i).get_ring(), Zp.clone_el(el_as_matrix.at(i, j)), &iso[i]);
969 }
970 }
971 return result;
972 }
973}
974
975#[cfg(test)]
976use crate::number_ring::odd_cyclotomic::OddCyclotomicNumberRing;
977#[cfg(test)]
978use feanor_math::assert_el_eq;
979
980#[cfg(any(test, feature = "generic_tests"))]
981pub fn test_with_number_ring<NumberRing: Clone + HECyclotomicNumberRing>(number_ring: NumberRing) {
982 use feanor_math::algorithms::eea::signed_lcm;
983 use feanor_math::assert_el_eq;
984 use crate::number_ring::largest_prime_leq_congruent_to_one;
985
986 let required_root_of_unity = signed_lcm(
987 number_ring.mod_p_required_root_of_unity() as i64,
988 1 << StaticRing::<i64>::RING.abs_log2_ceil(&(number_ring.n() as i64)).unwrap() + 2,
989 StaticRing::<i64>::RING
990 );
991 let p1 = largest_prime_leq_congruent_to_one(20000, required_root_of_unity).unwrap();
992 let p2 = largest_prime_leq_congruent_to_one(p1 - 1, required_root_of_unity).unwrap();
993 assert!(p1 != p2);
994 let rank = number_ring.rank();
995 let base_ring = zn_rns::Zn::new(vec![Zn::new(p1 as u64), Zn::new(p2 as u64)], BigIntRing::RING);
996 let ring = SingleRNSRingBase::<_, _, NTTConv<_>>::new(number_ring.clone(), base_ring.clone());
997
998 let base_ring = ring.base_ring();
999 let elements = vec![
1000 ring.zero(),
1001 ring.one(),
1002 ring.neg_one(),
1003 ring.int_hom().map(p1 as i32),
1004 ring.int_hom().map(p2 as i32),
1005 ring.canonical_gen(),
1006 ring.pow(ring.canonical_gen(), rank - 1),
1007 ring.int_hom().mul_map(ring.canonical_gen(), p1 as i32),
1008 ring.int_hom().mul_map(ring.pow(ring.canonical_gen(), rank - 1), p1 as i32),
1009 ring.add(ring.canonical_gen(), ring.one())
1010 ];
1011
1012 feanor_math::ring::generic_tests::test_ring_axioms(&ring, elements.iter().map(|x| ring.clone_el(x)));
1013 feanor_math::ring::generic_tests::test_self_iso(&ring, elements.iter().map(|x| ring.clone_el(x)));
1014 feanor_math::rings::extension::generic_tests::test_free_algebra_axioms(&ring);
1015
1016 for a in &elements {
1017 for b in &elements {
1018 for c in &elements {
1019 let actual = ring.get_ring().two_by_two_convolution([a, b], [c, &ring.one()]);
1020 assert_el_eq!(&ring, ring.mul_ref(a, c), &actual[0]);
1021 assert_el_eq!(&ring, ring.add_ref_snd(ring.mul_ref(b, c), a), &actual[1]);
1022 assert_el_eq!(&ring, b, &actual[2]);
1023 }
1024 }
1025 }
1026
1027 let double_rns_ring = DoubleRNSRingBase::new(number_ring.clone(), base_ring.clone());
1028 feanor_math::ring::generic_tests::test_hom_axioms(&ring, &double_rns_ring, elements.iter().map(|x| ring.clone_el(x)));
1029
1030 let dropped_rns_factor_ring = SingleRNSRingBase::new(number_ring.clone(), zn_rns::Zn::new(vec![Zn::new(p2 as u64)], BigIntRing::RING));
1031
1032 for a in &elements {
1033 assert_el_eq!(
1034 &dropped_rns_factor_ring,
1035 dropped_rns_factor_ring.from_canonical_basis(ring.wrt_canonical_basis(a).iter().map(|c| dropped_rns_factor_ring.base_ring().from_congruence([*ring.base_ring().get_congruence(&c).at(1)].into_iter()))),
1036 dropped_rns_factor_ring.get_ring().drop_rns_factor_element(ring.get_ring(), &[0], ring.clone_el(a))
1037 );
1038 }
1039
1040 feanor_math::serialization::generic_tests::test_serialization(&ring, elements.iter().map(|x| ring.clone_el(x)));
1041}
1042
1043#[test]
1044fn test_multiple_representations() {
1045 let rns_base = zn_rns::Zn::new(vec![Zn::new(2113), Zn::new(2689)], BigIntRing::RING);
1046 let ring: SingleRNSRing<_> = SingleRNSRingBase::new(OddCyclotomicNumberRing::new(3), rns_base.clone());
1047
1048 let from_raw_representation = |data: [i32; 3]| SingleRNSRingEl {
1049 coefficients: ring.get_ring().rns_base().as_iter().flat_map(|Zp| data.iter().map(|x| Zp.int_hom().map(*x))).collect(),
1050 convolutions: PhantomData,
1051 number_ring: PhantomData
1052 };
1053 let from_reduced_representation = |data: [i32; 2]| ring.from_canonical_basis(data.iter().map(|x| ring.base_ring().int_hom().map(*x)));
1054
1055 let elements = [
1056 (from_reduced_representation([0, 0]), from_raw_representation([1, 1, 1])),
1057 (from_reduced_representation([1, 0]), from_raw_representation([0, -1, -1])),
1058 (from_reduced_representation([1, 1]), from_raw_representation([0, 0, -1])),
1059 (from_reduced_representation([0, 1]), from_raw_representation([-1, 0, -1])),
1060 (from_reduced_representation([2, 2]), from_raw_representation([1, 1, -1])),
1061 (from_reduced_representation([1, 2]), from_raw_representation([-1, 0, -2]))
1062 ];
1063
1064 for (red, unred) in &elements {
1065 assert_el_eq!(&ring, red, unred);
1066 assert_el_eq!(&ring, red, ring.from_canonical_basis(ring.wrt_canonical_basis(unred).iter()));
1067 assert_el_eq!(&ring, ring.negate(ring.clone_el(red)), ring.negate(ring.clone_el(unred)));
1068 }
1069 for (red1, unred1) in &elements {
1070 for (red2, unred2) in &elements {
1071 assert_el_eq!(&ring, ring.add_ref(red1, red2), ring.add_ref(red1, unred2));
1072 assert_el_eq!(&ring, ring.add_ref(red1, red2), ring.add_ref(unred1, red2));
1073 assert_el_eq!(&ring, ring.add_ref(red1, red2), ring.add_ref(unred1, unred2));
1074
1075 assert_el_eq!(&ring, ring.sub_ref(red1, red2), ring.sub_ref(red1, unred2));
1076 assert_el_eq!(&ring, ring.sub_ref(red1, red2), ring.sub_ref(unred1, red2));
1077 assert_el_eq!(&ring, ring.sub_ref(red1, red2), ring.sub_ref(unred1, unred2));
1078
1079 assert_el_eq!(&ring, ring.mul_ref(red1, red2), ring.mul_ref(red1, unred2));
1080 assert_el_eq!(&ring, ring.mul_ref(red1, red2), ring.mul_ref(unred1, red2));
1081 assert_el_eq!(&ring, ring.mul_ref(red1, red2), ring.mul_ref(unred1, unred2));
1082 }
1083 }
1084
1085 let doublerns_ring = DoubleRNSRingBase::new(OddCyclotomicNumberRing::new(3), rns_base);
1086 let iso = doublerns_ring.can_iso(&ring).unwrap();
1087 for (red, unred) in &elements {
1088 assert_el_eq!(&doublerns_ring, iso.inv().map_ref(red), iso.inv().map_ref(unred));
1089 assert_el_eq!(&ring, iso.map(iso.inv().map_ref(red)), iso.map(iso.inv().map_ref(unred)));
1090 }
1091}
1092
1093#[test]
1094fn test_two_by_two_convolution() {
1095 let rns_base = zn_rns::Zn::new(vec![Zn::new(2113), Zn::new(2689)], BigIntRing::RING);
1096 let ring: SingleRNSRing<_> = SingleRNSRingBase::new(OddCyclotomicNumberRing::new(3), rns_base.clone());
1097
1098 let a = ring.from_canonical_basis([1, 2].into_iter().map(|c| ring.base_ring().int_hom().map(c)));
1099 let b = ring.from_canonical_basis([3, 5].into_iter().map(|c| ring.base_ring().int_hom().map(c)));
1100 let c = ring.from_canonical_basis([7, 2].into_iter().map(|c| ring.base_ring().int_hom().map(c)));
1101 let d = ring.from_canonical_basis([9, 8].into_iter().map(|c| ring.base_ring().int_hom().map(c)));
1102
1103 for lhs0 in [&a, &b, &c, &d] {
1104 for lhs1 in [&a, &b, &c, &d] {
1105 for rhs0 in [&a, &b, &c, &d] {
1106 for rhs1 in [&a, &b, &c, &d] {
1107 let [res0, res1, res2] = ring.get_ring().two_by_two_convolution([lhs0, lhs1], [rhs0, rhs1]);
1108 assert_el_eq!(&ring, ring.mul_ref(lhs0, rhs0), res0);
1109 assert_el_eq!(&ring, ring.add(ring.mul_ref(lhs0, rhs1), ring.mul_ref(lhs1, rhs0)), res1);
1110 assert_el_eq!(&ring, ring.mul_ref(lhs1, rhs1), res2);
1111 }
1112 }
1113 }
1114 }
1115}