he_ring/ciphertext_ring/
single_rns_ring.rs

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
36///
37/// Implementation of the ring `Z[𝝵_n]/(q)`, where `q = p1 ... pr` is a product of "RNS factors".
38/// Elements are stored in single-RNS-representation, using NTTs for multiplication.
39/// 
40/// As opposed to [`DoubleRNSRing`], this means multiplications are more expensive, but non-arithmetic
41/// operations like [`FreeAlgebra::wrt_canonical_basis()`] or [`BGFVCiphertextRing::as_representation_wrt_small_generating_set()`]
42/// are faster. Note that repeated multiplications with a fixed element will get significantly faster when using
43/// [`PreparedMultiplicationRing::prepare_multiplicant()`] and [`PreparedMultiplicationRing::mul_prepared()`].
44///  
45/// # Mathematical details
46/// 
47/// Elements are stored as polynomials, with coefficients represented w.r.t. this RNS base.
48/// In other words, the coefficients are stored by their cosets modulo each `pi`. Multiplication
49/// is done by computing the convolution of coefficients with the configured convolution algorithm,
50/// followed by a reduction modulo `Phi_n` (or rather `X^n - 1`, see below).
51/// 
52/// Furthermore, we currently store polynomials of degree `< n` (instead of degree `< phi(n) = deg(Phi_n)`) 
53/// to avoid expensive polynomial division by `Phi_n` (polynomial division by `X^n - 1` is very cheap).
54/// The reduction modulo `Phi_n` is only done when necessary, e.g. in [`RingBase::eq_el()`] or
55/// in [`SingleRNSRingBase::to_matrix()`].
56/// 
57pub struct SingleRNSRingBase<NumberRing, A, C> 
58    where NumberRing: HECyclotomicNumberRing,
59        A: Allocator + Clone,
60        C: PreparedConvolutionAlgorithm<ZnBase>
61{
62    // we have to store the double-RNS ring as well, since we need double-RNS representation
63    // for computing Galois operations. This is because I don't think there is any good way
64    // of efficiently computing the galois action image using only coefficient representation
65    base: DoubleRNSRing<NumberRing, A>,
66    /// Convolution algorithms to use to compute convolutions over each `Fp` in the RNS base
67    convolutions: Vec<Arc<C>>,
68    /// Used to compute the polynomial division by `Phi_n` when necessary
69    poly_moduli: Vec<Arc<CyclotomicPolyReducer<Zn, Arc<C>>>>
70}
71
72///
73/// [`RingStore`] for [`SingleRNSRingBase`]
74/// 
75pub type SingleRNSRing<NumberRing, A = Global, C = NTTConv<Zn, Global>> = RingValue<SingleRNSRingBase<NumberRing, A, C>>;
76
77///
78/// Type of elements of [`SingleRNSRingBase`]
79/// 
80pub struct SingleRNSRingEl<NumberRing, A, C>
81    where NumberRing: HECyclotomicNumberRing,
82        A: Allocator + Clone,
83        C: ConvolutionAlgorithm<ZnBase>
84{
85    /// we allow coefficients up to `n` (and not `phi(n)`) to avoid intermediate reductions modulo `Phi_n`
86    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    ///
106    /// Creates a new [`SingleRNSRing`].
107    /// 
108    /// It must be possible to create a convolution of type `C` for each RNS factors 
109    /// `Z/(pi)` in `rns_base`.
110    /// 
111    #[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    ///
139    /// Creates a new [`SingleRNSRing`].
140    /// 
141    /// The list of convolutions `convolutions` must contain one convolution for each RNS factor
142    /// `Z/(pi)` in `rns_base`. If there is one convolution object that supports computing convolutions
143    /// over every `Z/(pi)` (like [`STANDARD_CONVOLUTION`]), `convolutions` should contain multiple `Arc`s 
144    /// all pointing to this one convolution object.
145    /// 
146    #[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    ///
166    /// Performs reduction modulo `X^n - 1`.
167    /// 
168    #[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    ///
178    /// Performs reduction modulo `Phi_n`.
179    /// 
180    #[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}