Skip to main content

feanor_math/rings/extension/
extension_impl.rs

1use std::alloc::{Allocator, Global};
2use std::fmt::{Debug, Formatter};
3use std::marker::PhantomData;
4use std::cell::OnceCell;
5use std::mem::replace;
6
7use feanor_serde::dependent_tuple::DeserializeSeedDependentTuple;
8use feanor_serde::newtype_struct::DeserializeSeedNewtypeStruct;
9use feanor_serde::seq::*;
10use feanor_serde::newtype_struct::*;
11use serde::de::DeserializeSeed;
12use serde::{Deserializer, Serialize, Serializer, Deserialize};
13
14use crate::algorithms::convolution::*;
15use crate::algorithms::extension_ops::create_multiplication_matrix;
16use crate::algorithms::linsolve::LinSolveRing;
17use crate::divisibility::*;
18use crate::{impl_localpir_wrap_unwrap_homs, impl_localpir_wrap_unwrap_isos, impl_field_wrap_unwrap_homs, impl_field_wrap_unwrap_isos};
19use crate::integer::*;
20use crate::iters::multi_cartesian_product;
21use crate::iters::MultiProduct;
22use crate::rings::poly::PolyRingStore;
23use crate::matrix::OwnedMatrix;
24use crate::primitive_int::StaticRing;
25use crate::rings::finite::*;
26use crate::specialization::*;
27use crate::rings::poly::dense_poly::DensePolyRing;
28use crate::seq::*;
29use crate::ring::*;
30use crate::integer::IntegerRingStore;
31use crate::delegate::DelegateRing;
32use crate::homomorphism::*;
33use crate::serialization::*;
34use crate::seq::sparse::SparseMapVector;
35
36use super::FreeAlgebra;
37use super::FreeAlgebraStore;
38
39///
40/// Implementation for rings that are generated by a single algebraic element over a base ring,
41/// equivalently a quotient of a univariate polynomial ring `R[X]/(f(X))`.
42/// 
43/// # Example
44/// ```rust
45/// # use feanor_math::ring::*;
46/// # use feanor_math::rings::extension::*;
47/// # use feanor_math::primitive_int::*;
48/// # use feanor_math::rings::extension::extension_impl::*;
49/// # use feanor_math::assert_el_eq;
50/// let base_ring = StaticRing::<i64>::RING;
51/// // ring of integers in the 6th cyclotomic number field
52/// let ring = FreeAlgebraImpl::new(base_ring, 2, [-1, 1]);
53/// let root_of_unity = ring.canonical_gen();
54/// assert_el_eq!(ring, ring.one(), ring.pow(root_of_unity, 6));
55/// ```
56/// 
57pub struct FreeAlgebraImplBase<R, V, A = Global, C = KaratsubaAlgorithm>
58    where R: RingStore, 
59        V: VectorView<El<R>>, 
60        A: Allocator + Clone,
61        C: ConvolutionAlgorithm<R::Type>
62{
63    base_ring: R,
64    x_pow_rank: V,
65    element_allocator: A,
66    log2_padded_len: usize,
67    rank: usize,
68    gen_name: &'static str,
69    convolution: C
70}
71
72/// 
73/// Implementation for rings that are generated by a single algebraic element over a base ring,
74/// equivalently a quotient of a univariate polynomial ring `R[X]/(f(X))`. For details, see
75/// [`FreeAlgebraImplBase`].
76/// 
77pub type FreeAlgebraImpl<R, V, A = Global, C = KaratsubaAlgorithm> = RingValue<FreeAlgebraImplBase<R, V, A, C>>;
78
79impl<R, V> FreeAlgebraImpl<R, V>
80    where R: RingStore, V: VectorView<El<R>>
81{
82    ///
83    /// Creates a new [`FreeAlgebraImpl`] ring as extension of the given base ring.
84    /// 
85    /// The created ring is `R[X]/(X^rank - sum_i x_pow_rank[i] X^i)`.
86    /// 
87    pub fn new(base_ring: R, rank: usize, x_pow_rank: V) -> Self {
88        Self::new_with_convolution(base_ring, rank, x_pow_rank, "θ", Global, STANDARD_CONVOLUTION)
89    }
90}
91
92impl<R, V, A, C> Clone for FreeAlgebraImplBase<R, V, A, C>
93    where R: RingStore + Clone, 
94        V: VectorView<El<R>> + Clone,
95        A: Allocator + Clone,
96        C: ConvolutionAlgorithm<R::Type> + Clone
97{
98    fn clone(&self) -> Self {
99        Self {
100            base_ring: self.base_ring.clone(),
101            x_pow_rank: self.x_pow_rank.clone(),
102            element_allocator: self.element_allocator.clone(),
103            log2_padded_len: self.log2_padded_len,
104            rank: self.rank,
105            convolution: self.convolution.clone(),
106            gen_name: self.gen_name
107        }
108    }
109}
110
111impl<R, V, A, C> Copy for FreeAlgebraImplBase<R, V, A, C>
112    where R: RingStore + Copy, 
113        V: VectorView<El<R>> + Copy,
114        A: Allocator + Copy,
115        C: ConvolutionAlgorithm<R::Type> + Copy,
116        El<R>: Copy
117{}
118
119///
120/// Type of an element of [`FreeAlgebraImpl`].
121/// 
122pub struct FreeAlgebraImplEl<R, A = Global>
123    where R: RingStore, A: Allocator
124{
125    values: Box<[El<R>], A>
126}
127
128impl<R, A> Debug for FreeAlgebraImplEl<R, A>
129    where R: RingStore, A: Allocator,
130        El<R>: Debug
131{
132    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
133        write!(f, "{:?}", &self.values)
134    }
135}
136
137impl<R, V, A, C> FreeAlgebraImpl<R, V, A, C>
138    where R: RingStore, 
139        V: VectorView<El<R>>,
140        A: Allocator + Clone,
141        C: ConvolutionAlgorithm<R::Type>
142{
143    #[stability::unstable(feature = "enable")]
144    pub fn new_with_convolution(base_ring: R, rank: usize, x_pow_rank: V, gen_name: &'static str, element_allocator: A, convolution: C) -> Self {
145        assert!(rank >= 1);
146        assert!(x_pow_rank.len() <= rank);
147        let log2_padded_len = StaticRing::<i64>::RING.abs_log2_ceil(&rank.try_into().unwrap()).unwrap();
148        RingValue::from(FreeAlgebraImplBase {
149            base_ring, gen_name, x_pow_rank, element_allocator, rank, log2_padded_len, convolution
150        })
151    }
152}
153
154impl<R, V, A, C> FreeAlgebraImplBase<R, V, A, C>
155    where R: RingStore, 
156        V: VectorView<El<R>>,
157        A: Allocator + Clone,
158        C: ConvolutionAlgorithm<R::Type>
159{
160    #[stability::unstable(feature = "enable")]
161    pub fn set_convolution<C_new: ConvolutionAlgorithm<R::Type>>(self, new_convolution: C_new) -> FreeAlgebraImpl<R, V, A, C_new> {
162        RingValue::from(FreeAlgebraImplBase {
163            base_ring: self.base_ring,
164            x_pow_rank: self.x_pow_rank,
165            element_allocator: self.element_allocator,
166            log2_padded_len: self.log2_padded_len,
167            rank: self.rank,
168            convolution: new_convolution,
169            gen_name: self.gen_name
170        })
171    }
172
173    #[stability::unstable(feature = "enable")]
174    pub fn allocator(&self) -> &A {
175        &self.element_allocator
176    }
177
178    #[stability::unstable(feature = "enable")]
179    pub fn gen_name(&self) -> &'static str {
180        self.gen_name
181    }
182
183    #[stability::unstable(feature = "enable")]
184    pub fn x_pow_rank(&self) -> &V {
185        &self.x_pow_rank
186    }
187
188    #[stability::unstable(feature = "enable")]
189    pub fn convolution(&self) -> &C {
190        &self.convolution
191    }
192
193    fn reduce_mod_poly(&self, data: &mut [El<R>]) {
194        struct ReduceModulo<'a, R>
195            where R: RingStore
196        {
197            rank: usize,
198            base_ring: &'a R,
199            data: &'a mut [El<R>]
200        }
201
202        impl<'a, R> SparseVectorViewOperation<El<R>> for ReduceModulo<'a, R>
203            where R: RingStore
204        {
205            type Output<'b> = ()
206                where Self: 'b;
207        
208            fn execute<'b, V: 'b + VectorViewSparse<El<R>>>(self, vector: V) -> () {
209                for i in (self.rank..(2 * self.rank)).rev() {
210                    for (j, c) in vector.nontrivial_entries() {
211                        let add = self.base_ring.mul_ref(c, &mut self.data[i]);
212                        self.base_ring.add_assign(&mut self.data[i - self.rank + j], add);
213                    }
214                }
215            }
216        }
217
218        let was_sparse = self.x_pow_rank.specialize_sparse(ReduceModulo {
219            rank: self.rank,
220            base_ring: self.base_ring(),
221            data: data
222        });
223        if was_sparse.is_err() {
224            for i in (self.rank()..(2 * self.rank())).rev() {
225                for j in 0..self.x_pow_rank.len() {
226                    let add = self.base_ring.mul_ref(self.x_pow_rank.at(j), &data[i]);
227                    self.base_ring.add_assign(&mut data[i - self.rank() + j], add);
228                }
229            }
230        }
231    }
232}
233
234impl<R, V, A, C> PartialEq for FreeAlgebraImplBase<R, V, A, C>
235    where R: RingStore, 
236        V: VectorView<El<R>>,
237        A: Allocator + Clone,
238        C: ConvolutionAlgorithm<R::Type>
239{
240    fn eq(&self, other: &Self) -> bool {
241        self.base_ring().get_ring() == other.base_ring().get_ring() && self.rank() == other.rank() &&
242            (0..self.rank()).all(|i| (i >= self.x_pow_rank.len() && i >= other.x_pow_rank.len()) ||
243                (i >= self.x_pow_rank.len() && self.base_ring().is_zero(other.x_pow_rank.at(i))) ||
244                (i >= other.x_pow_rank.len() && self.base_ring().is_zero(self.x_pow_rank.at(i))) ||
245                self.base_ring().eq_el(self.x_pow_rank.at(i), other.x_pow_rank.at(i)))
246    }
247}
248
249impl<R, V, A, C> RingBase for FreeAlgebraImplBase<R, V, A, C>
250    where R: RingStore, 
251        V: VectorView<El<R>>,
252        A: Allocator + Clone,
253        C: ConvolutionAlgorithm<R::Type>
254{
255    type Element = FreeAlgebraImplEl<R, A>;
256    
257    fn clone_el(&self, val: &Self::Element) -> Self::Element {
258        debug_assert_eq!(1 << self.log2_padded_len, val.values.len());
259        let mut result_values = Vec::with_capacity_in(val.values.len(), self.element_allocator.clone());
260        result_values.extend(val.values.iter().map(|x| self.base_ring().clone_el(x)));
261        return FreeAlgebraImplEl {
262            values: result_values.into_boxed_slice()
263        };
264    }
265
266    fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
267        debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
268        debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
269        for i in 0..self.rank() {
270            self.base_ring().add_assign_ref(&mut lhs.values[i], &rhs.values[i]);
271        }
272    }
273
274    fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
275        debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
276        debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
277        for (i, x) in (0..self.rank()).zip(rhs.values.iter()) {
278            self.base_ring().add_assign_ref(&mut lhs.values[i], x);
279        }
280    }
281
282    fn sub_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
283        debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
284        debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
285        for i in 0..self.rank() {
286            self.base_ring().sub_assign_ref(&mut lhs.values[i], &rhs.values[i]);
287        }
288    }
289
290    fn negate_inplace(&self, lhs: &mut Self::Element) {
291        debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
292        for i in 0..self.rank() {
293            self.base_ring().negate_inplace(&mut lhs.values[i]);
294        }
295    }
296
297    fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
298        debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
299        debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
300        self.mul_assign_ref(lhs, &rhs)
301    }
302
303    fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
304        *lhs = self.fma(lhs, rhs, self.zero());
305    }
306
307    fn fma(&self, lhs: &Self::Element, rhs: &Self::Element, summand: Self::Element) -> Self::Element {
308        debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
309        debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
310        debug_assert_eq!(1 << self.log2_padded_len, summand.values.len());
311        let mut tmp = summand.values.into_vec();
312        tmp.resize_with(2 << self.log2_padded_len, || self.base_ring.zero());
313        self.convolution.compute_convolution(&lhs.values[..], &rhs.values[..], &mut tmp[..], self.base_ring());
314        self.reduce_mod_poly(&mut tmp);
315        tmp.truncate(1 << self.log2_padded_len);
316        for i in self.rank()..(1 << self.log2_padded_len) {
317            tmp[i] = self.base_ring().zero();
318        }
319        return FreeAlgebraImplEl {
320            values: tmp.into_boxed_slice()
321        };
322    }
323    
324    fn from_int(&self, value: i32) -> Self::Element {
325        self.from(self.base_ring().int_hom().map(value))
326    }
327
328    fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
329        debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
330        debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
331        (0..self.rank()).all(|i| self.base_ring().eq_el(&lhs.values[i], &rhs.values[i]))
332    }
333
334    fn is_zero(&self, value: &Self::Element) -> bool {
335        debug_assert_eq!(1 << self.log2_padded_len, value.values.len());
336        (0..self.rank()).all(|i| self.base_ring.is_zero(&value.values[i]))
337    }
338
339    fn is_one(&self, value: &Self::Element) -> bool {
340        debug_assert_eq!(1 << self.log2_padded_len, value.values.len());
341        self.base_ring().is_one(&value.values[0]) && (1..self.rank()).all(|i| self.base_ring.is_zero(&value.values[i]))
342    }
343
344    fn is_neg_one(&self, value: &Self::Element) -> bool {
345        debug_assert_eq!(1 << self.log2_padded_len, value.values.len());
346        self.base_ring().is_neg_one(&value.values[0]) && (1..self.rank()).all(|i| self.base_ring.is_zero(&value.values[i]))
347    }
348
349    fn is_commutative(&self) -> bool {
350        self.base_ring().is_commutative()
351    }
352
353    fn is_noetherian(&self) -> bool {
354        self.base_ring().is_noetherian()
355    }
356
357    fn is_approximate(&self) -> bool {
358        self.base_ring().get_ring().is_approximate()
359    }
360
361    fn dbg<'a>(&self, value: &Self::Element, out: &mut std::fmt::Formatter<'a>) -> std::fmt::Result {
362        debug_assert_eq!(1 << self.log2_padded_len, value.values.len());
363        self.dbg_within(value, out, EnvBindingStrength::Weakest)
364    }
365
366    fn dbg_within<'a>(&self, value: &Self::Element, out: &mut std::fmt::Formatter<'a>, env: EnvBindingStrength) -> std::fmt::Result {
367        debug_assert_eq!(1 << self.log2_padded_len, value.values.len());
368        let poly_ring = DensePolyRing::new(self.base_ring(), self.gen_name);
369        poly_ring.get_ring().dbg_within(&RingRef::new(self).poly_repr(&poly_ring, value, &self.base_ring().identity()), out, env)
370    }
371
372    fn mul_assign_int(&self, lhs: &mut Self::Element, rhs: i32) {
373        debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
374        for i in 0..self.rank() {
375            self.base_ring().int_hom().mul_assign_map(&mut lhs.values[i], rhs);
376        }
377    }
378
379    fn fma_int(&self, lhs: &Self::Element, rhs: i32, summand: Self::Element) -> Self::Element {
380        debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
381        debug_assert_eq!(1 << self.log2_padded_len, summand.values.len());
382        let mut result = Vec::new_in(self.allocator().clone());
383        result.extend(summand.values.into_iter().enumerate().map(|(i, x)| self.base_ring().int_hom().fma_map(&lhs.values[i], &rhs, x)));
384        return FreeAlgebraImplEl {
385            values: result.into_boxed_slice()
386        };
387    }
388
389    fn characteristic<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
390        where I::Type: IntegerRing
391    {
392        self.base_ring().characteristic(ZZ)
393    }
394
395    fn square(&self, value: &mut Self::Element) {
396        let mut tmp = Vec::with_capacity_in(2 << self.log2_padded_len, self.element_allocator.clone());
397        tmp.resize_with(2 << self.log2_padded_len, || self.base_ring().zero());
398        let x_prepared = self.convolution.prepare_convolution_operand(&value.values[..], Some(2 * self.rank()), self.base_ring());
399        self.convolution.compute_convolution_prepared(&value.values[..], Some(&x_prepared), &value.values[..], Some(&x_prepared), &mut tmp, self.base_ring());
400        self.reduce_mod_poly(&mut tmp);
401        for i in 0..self.rank() {
402            value.values[i] = std::mem::replace(&mut tmp[i], self.base_ring().zero());
403        }
404    }
405}
406
407impl<R, V, A, C> RingExtension for FreeAlgebraImplBase<R, V, A, C>
408    where R: RingStore, 
409        V: VectorView<El<R>>,
410        A: Allocator + Clone,
411        C: ConvolutionAlgorithm<R::Type>
412{
413    type BaseRing = R;
414    
415    fn base_ring<'a>(&'a self) -> &'a Self::BaseRing {
416        &self.base_ring
417    }
418
419    fn from(&self, x: El<Self::BaseRing>) -> Self::Element {
420        let mut result_values = Vec::with_capacity_in(1 << self.log2_padded_len, self.element_allocator.clone());
421        result_values.push(x);
422        result_values.extend((0..((1 << self.log2_padded_len) - 1)).map(|_| self.base_ring().zero()));
423        return FreeAlgebraImplEl {
424            values: result_values.into_boxed_slice()
425        };
426    }
427
428    fn mul_assign_base(&self, lhs: &mut Self::Element, rhs: &El<Self::BaseRing>) {
429        for i in 0..self.rank() {
430            self.base_ring().mul_assign_ref(&mut lhs.values[i], rhs);
431        }
432    }
433
434    fn fma_base(&self, lhs: &Self::Element, rhs: &El<Self::BaseRing>, summand: Self::Element) -> Self::Element {
435        let mut new = Vec::with_capacity_in(1 << self.log2_padded_len, self.element_allocator.clone());
436        new.extend(summand.values.into_iter().take(self.rank()).enumerate().map(|(i, x)| self.base_ring.fma(&lhs.values[i], rhs, x)));
437        new.resize_with(1 << self.log2_padded_len, || self.base_ring().zero());
438        return FreeAlgebraImplEl {
439            values: new.into_boxed_slice()
440        };
441    }
442}
443
444///
445/// It seems like a good idea to also implement [`DivisibilityRing::prepare_divisor()`]
446/// and the prepared division functions here. However, currently the interface of
447/// [`LinSolveRing`] does not support this. I am not yet sure what is the best approach
448/// here, but it seems unlikely that this can be done properly without a breaking release
449///  - Either change [`LinSolveRing`] to support "factorizations" of matrices, or rather
450///    the equivalent to "prepared divisors" of matrices
451///  - Implement [`DivisibilityRing::prepare_divisor()`] only for `R::Type: [`PrincipalIdealRing`]`,
452///    which however requires specialization or a specialization workaround
453/// 
454/// TODO: Decide and implement on the next breaking release
455/// 
456impl<R, V, A, C> DivisibilityRing for FreeAlgebraImplBase<R, V, A, C>
457    where R: RingStore, 
458        R::Type: LinSolveRing,
459        V: VectorView<El<R>>,
460        A: Allocator + Clone,
461        C: ConvolutionAlgorithm<R::Type>
462{
463    fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
464        let mut mul_matrix: OwnedMatrix<_, _> = create_multiplication_matrix(RingRef::new(self), rhs, self.allocator().clone());
465        let mut lhs_matrix: OwnedMatrix<_, _> = OwnedMatrix::zero_in(self.rank(), 1, self.base_ring(), self.allocator().clone());
466        let data = self.wrt_canonical_basis(&lhs);
467        for j in 0..self.rank() {
468            *lhs_matrix.at_mut(j, 0) = data.at(j);
469        }
470        let mut solution: OwnedMatrix<_, _> = OwnedMatrix::zero_in(self.rank(), 1, self.base_ring(), self.allocator().clone());
471        let has_sol = self.base_ring().get_ring().solve_right(mul_matrix.data_mut(), lhs_matrix.data_mut(), solution.data_mut(), Global);
472        if has_sol.is_solved() {
473            return Some(self.from_canonical_basis((0..self.rank()).map(|i| self.base_ring().clone_el(solution.at(i, 0)))));
474        } else {
475            return None;
476        }
477    }
478
479    fn balance_factor<'a, I>(&self, elements: I) -> Option<Self::Element>
480        where I: Iterator<Item = &'a Self::Element>, 
481            Self: 'a
482    {
483        self.base_ring().get_ring().balance_factor(elements.flat_map(|x| x.values.iter())).map(|c| RingRef::new(self).inclusion().map(c))
484    }
485
486    default fn invert(&self, el: &Self::Element) -> Option<Self::Element> {
487        self.checked_left_div(&self.one(), el)
488    }
489}
490
491impl<R, V, A, C> Debug for FreeAlgebraImplBase<R, V, A, C>
492    where R: RingStore + Debug, 
493        V: VectorView<El<R>>, 
494        A: Allocator + Clone,
495        C: ConvolutionAlgorithm<R::Type>
496{
497    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
498        let poly_ring = DensePolyRing::new(self.base_ring(), self.gen_name);
499        f.debug_struct("FreeAlgebraImplBase")
500            .field("base_ring", &self.base_ring)
501            .field("modulus", &poly_ring.format(&RingRef::new(self).generating_poly(&poly_ring, self.base_ring.identity())))
502            .finish()
503    }
504}
505
506impl<R, V> Serialize for FreeAlgebraImplBase<R, V, Global, KaratsubaAlgorithm>
507    where R: RingStore + Serialize, 
508        R::Type: SerializableElementRing,
509        V: VectorView<El<R>>,
510{
511    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
512        where S: Serializer
513    {
514        let poly_ring = DensePolyRing::new(self.base_ring(), "X");
515        SerializableNewtypeStruct::new("FreeAlgebraImpl", (self.base_ring(), SerializeOwnedWithRing::new(RingRef::new(self).generating_poly(&poly_ring, self.base_ring().identity()), poly_ring))).serialize(serializer)
516    }
517}
518
519impl<'de, R> Deserialize<'de> for FreeAlgebraImplBase<R, SparseMapVector<R>, Global, KaratsubaAlgorithm>
520    where R: RingStore + Deserialize<'de> + Clone, 
521        R::Type: SerializableElementRing,
522{
523    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
524        where D: Deserializer<'de>
525    {
526        let ring_cell = OnceCell::new();
527        let poly = <_ as DeserializeSeed<'de>>::deserialize(DeserializeSeedNewtypeStruct::new("FreeAlgebraImpl", DeserializeSeedDependentTuple::new(PhantomData::<R>, |ring| {
528            let poly_ring = DensePolyRing::new(ring, "X");
529            ring_cell.set(poly_ring).ok().unwrap();
530            DeserializeWithRing::new(ring_cell.get().unwrap())
531        })), deserializer)?;
532        let poly_ring = ring_cell.into_inner().unwrap();
533        assert!(poly_ring.base_ring().is_one(poly_ring.lc(&poly).unwrap()));
534        let rank = poly_ring.degree(&poly).unwrap();
535        let mut x_pow_rank = SparseMapVector::new(rank, (*poly_ring.base_ring()).clone());
536        for (c, i) in poly_ring.terms(&poly) {
537            *x_pow_rank.at_mut(i) = poly_ring.base_ring().negate(poly_ring.base_ring().clone_el(c));
538        }
539        _ = x_pow_rank.at_mut(0);
540        return Ok(FreeAlgebraImpl::new_with_convolution(poly_ring.into().into_base_ring(), rank, x_pow_rank, "θ", Global, STANDARD_CONVOLUTION).into());
541    }
542}
543
544impl<'de, R> Deserialize<'de> for FreeAlgebraImplBase<R, Vec<El<R>>, Global, KaratsubaAlgorithm>
545    where R: RingStore + Deserialize<'de>, 
546        R::Type: SerializableElementRing
547{
548    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
549        where D: Deserializer<'de>
550    {
551        let ring_cell = OnceCell::new();
552        let poly = <_ as DeserializeSeed<'de>>::deserialize(DeserializeSeedNewtypeStruct::new("FreeAlgebraImpl", DeserializeSeedDependentTuple::new(PhantomData::<R>, |ring| {
553            let poly_ring = DensePolyRing::new(ring, "X");
554            ring_cell.set(poly_ring).ok().unwrap();
555            DeserializeWithRing::new(ring_cell.get().unwrap())
556        })), deserializer)?;
557        let poly_ring = ring_cell.into_inner().unwrap();
558        assert!(poly_ring.base_ring().is_one(poly_ring.lc(&poly).unwrap()));
559        let rank = poly_ring.degree(&poly).unwrap();
560        let x_pow_rank = (0..rank).map(|i| poly_ring.base_ring().negate(poly_ring.base_ring().clone_el(poly_ring.coefficient_at(&poly, i)))).collect::<Vec<_>>();
561        return Ok(FreeAlgebraImpl::new_with_convolution(poly_ring.into().into_base_ring(), rank, x_pow_rank, "θ", Global, STANDARD_CONVOLUTION).into());
562    }
563}
564
565impl<R, V, A, C> SerializableElementRing for FreeAlgebraImplBase<R, V, A, C>
566    where R: RingStore, 
567        V: VectorView<El<R>>,
568        A: Allocator + Clone,
569        C: ConvolutionAlgorithm<R::Type>,
570        R::Type: SerializableElementRing
571{
572    fn deserialize<'de, D>(&self, deserializer: D) -> Result<Self::Element, D::Error>
573        where D: Deserializer<'de>
574    {
575        // TODO: find better serialization name
576        DeserializeSeedNewtypeStruct::new("ExtensionRingEl", DeserializeSeedSeq::new(
577            std::iter::repeat(DeserializeWithRing::new(self.base_ring())).take(self.rank() + 1),
578            Vec::with_capacity_in(1 << self.log2_padded_len, self.element_allocator.clone()),
579            |mut current, next| { current.push(next); current }
580        )).deserialize(deserializer).map(|mut values| {
581            values.resize_with(1 << self.log2_padded_len, || self.base_ring().zero());
582            FreeAlgebraImplEl { values: values.into_boxed_slice() }
583        })
584    }
585
586    fn serialize<S>(&self, el: &Self::Element, serializer: S) -> Result<S::Ok, S::Error>
587        where S: Serializer
588    {
589        debug_assert_eq!(1 << self.log2_padded_len, el.values.len());
590        SerializableNewtypeStruct::new("ExtensionRingEl", SerializableSeq::new_with_len(
591            (0..self.rank()).map(|i| SerializeWithRing::new(&el.values[i], self.base_ring())), self.rank()
592        )).serialize(serializer)
593    }
594}
595
596impl<R, V, A, C> FreeAlgebra for FreeAlgebraImplBase<R, V, A, C>
597    where R: RingStore, 
598        V: VectorView<El<R>>,
599        A: Allocator + Clone,
600        C: ConvolutionAlgorithm<R::Type>
601{
602    type VectorRepresentation<'a> = CloneElFn<&'a [El<R>], El<R>, CloneRingEl<&'a R>>
603        where Self: 'a;
604
605    fn canonical_gen(&self) -> Self::Element {
606        if self.rank() == 1 {
607            if self.x_pow_rank.len() == 1 {
608                self.from_canonical_basis([self.base_ring.clone_el(self.x_pow_rank.at(0))])
609            } else {
610                assert!(self.x_pow_rank.len() == 0);
611                self.zero()
612            }
613        } else {
614            self.from_canonical_basis((0..self.rank()).map(|i| if i == 1 { self.base_ring.one() } else { self.base_ring.zero() }))
615        }
616    }
617
618    fn wrt_canonical_basis<'a>(&'a self, el: &'a Self::Element) -> Self::VectorRepresentation<'a> {
619        (&el.values[..self.rank]).clone_ring_els(self.base_ring())
620    }
621
622    fn from_canonical_basis<W>(&self, vec: W) -> Self::Element
623        where W: IntoIterator<Item = El<Self::BaseRing>>,
624            W::IntoIter: DoubleEndedIterator
625    {
626        let mut given_len = 0;
627        let mut vec_it = vec.into_iter().inspect(|_| given_len += 1);
628        let mut result = Vec::with_capacity_in(1 << self.log2_padded_len, self.element_allocator.clone());
629        result.extend(vec_it.by_ref());
630        result.extend((0..((1 << self.log2_padded_len) - self.rank())).map(|_| self.base_ring().zero()));
631        assert!(vec_it.next().is_none());
632        assert_eq!(given_len, self.rank());
633        FreeAlgebraImplEl { values: result.into_boxed_slice() }
634    }
635
636    fn rank(&self) -> usize {
637        self.rank
638    }
639
640    fn mul_assign_gen_power(&self, el: &mut Self::Element, power: usize) {
641        let mut gen_pow_d = Vec::new_in(self.allocator().clone());
642        gen_pow_d.extend(self.x_pow_rank.as_iter().map(|c| self.base_ring.clone_el(c)));
643        gen_pow_d.resize_with(1 << self.log2_padded_len, || self.base_ring.zero());
644
645        if power % self.rank() != 0 {
646            let mut gen_pow_rem = Vec::new_in(self.allocator().clone());
647            gen_pow_rem.extend((0..((power % self.rank()) - 1)).map(|_| self.base_ring.zero()));
648            gen_pow_rem.push(self.base_ring.one());
649            gen_pow_rem.resize_with(1 << self.log2_padded_len, || self.base_ring.zero());
650            *el = self.prod([
651                FreeAlgebraImplEl { values: replace(&mut el.values, Box::new_in([], self.allocator().clone())) },
652                RingValue::from_ref(self).pow(FreeAlgebraImplEl { values: gen_pow_d.into_boxed_slice() }, power / self.rank),
653                FreeAlgebraImplEl { values: gen_pow_rem.into_boxed_slice() },
654            ]);
655        } else {
656            self.mul_assign(
657                el,
658                RingValue::from_ref(self).pow(FreeAlgebraImplEl { values: gen_pow_d.into_boxed_slice() }, power / self.rank),
659            );
660        }
661    }
662}
663
664impl<R, V, A, C> HashableElRing for FreeAlgebraImplBase<R, V, A, C>
665    where R: RingStore, 
666        R::Type: HashableElRing,
667        V: VectorView<El<R>>,
668        A: Allocator + Clone,
669        C: ConvolutionAlgorithm<R::Type>
670{
671    fn hash<H: std::hash::Hasher>(&self, el: &Self::Element, h: &mut H) {
672        // this looks very simple, but I believe this is also the right implementation;
673        // in particular, we assume that elements whose hashes are compared (e.g. because
674        // they are in the same hashtable) belong to the same ring - after all, this is
675        // also how we implement equality and all other operations. Hence, it does not
676        // make sense to hash ring-specific info (like the rank). Additionally, hashes
677        // in Rust are assumed to have no prefix-collisions, thus we don't need to insert
678        // separators between elements, compare in particular [`Hash::hash_slice()`].
679        for x in &el.values[..self.rank()] {
680            self.base_ring().get_ring().hash(x, h)
681        }
682    }
683}
684
685///
686/// Function that creates a [`FreeAlgebraImplEl`] from its canonical basis
687/// representation.
688/// 
689/// Used by the implementation of [`FiniteRing::elements()`] for [`FreeAlgebraImplBase`].
690/// 
691pub struct WRTCanonicalBasisElementCreator<'a, R, V, A, C>
692    where R: RingStore, 
693        R::Type: FiniteRing, 
694        V: VectorView<El<R>>,
695        A: Allocator + Clone,
696        C: ConvolutionAlgorithm<R::Type>
697{
698    base_ring: &'a FreeAlgebraImplBase<R, V, A, C>
699}
700
701impl<'a, R, V, A, C> Clone for WRTCanonicalBasisElementCreator<'a, R, V, A, C>
702    where R: RingStore, 
703        R::Type: FiniteRing, 
704        V: VectorView<El<R>>,
705        A: Allocator + Clone,
706        C: ConvolutionAlgorithm<R::Type>
707{
708    fn clone(&self) -> Self { *self }
709}
710
711impl<'a, 'b, R, V, A, C> FnOnce<(&'b [El<R>], )> for WRTCanonicalBasisElementCreator<'a, R, V, A, C>
712    where R: RingStore, 
713        R::Type: FiniteRing, 
714        V: VectorView<El<R>>,
715        A: Allocator + Clone,
716        C: ConvolutionAlgorithm<R::Type>
717{
718    type Output = El<FreeAlgebraImpl<R, V, A, C>>;
719
720    extern "rust-call" fn call_once(mut self, args: (&'b [El<R>], )) -> Self::Output {
721        self.call_mut(args)
722    }
723}
724
725impl<'a, 'b, R, V, A, C> FnMut<(&'b [El<R>], )> for WRTCanonicalBasisElementCreator<'a, R, V, A, C>
726    where R: RingStore, 
727        R::Type: FiniteRing, 
728        V: VectorView<El<R>>,
729        A: Allocator + Clone,
730        C: ConvolutionAlgorithm<R::Type>
731{
732    extern "rust-call" fn call_mut(&mut self, args: (&'b [El<R>], )) -> Self::Output {
733        self.base_ring.from_canonical_basis(args.0.iter().map(|x| self.base_ring.base_ring().clone_el(x)))
734    }
735}
736
737impl<'a, R, V, A, C> Copy for WRTCanonicalBasisElementCreator<'a, R, V, A, C>
738    where R: RingStore, 
739        R::Type: FiniteRing, 
740        V: VectorView<El<R>>,
741        A: Allocator + Clone,
742        C: ConvolutionAlgorithm<R::Type>
743{}
744
745impl<R, V, A, C> FiniteRingSpecializable for FreeAlgebraImplBase<R, V, A, C>
746    where R: RingStore,
747        R::Type: FiniteRingSpecializable, 
748        V: VectorView<El<R>>,
749        A: Allocator + Clone,
750        C: ConvolutionAlgorithm<R::Type>
751{
752    fn specialize<O: FiniteRingOperation<Self>>(op: O) -> O::Output {
753        struct OpWrapper<R, V, A, C, O>
754            where R: RingStore,
755                R::Type: FiniteRingSpecializable, 
756                V: VectorView<El<R>>,
757                A: Allocator + Clone,
758                C: ConvolutionAlgorithm<R::Type>,
759                O: FiniteRingOperation<FreeAlgebraImplBase<R, V, A, C>>
760        {
761            operation: O,
762            ring: PhantomData<FreeAlgebraImpl<R, V, A, C>>
763        }
764
765        impl<R, V, A, C, O> FiniteRingOperation<R::Type> for OpWrapper<R, V, A, C, O>
766            where R: RingStore,
767                R::Type: FiniteRingSpecializable, 
768                V: VectorView<El<R>>,
769                A: Allocator + Clone,
770                C: ConvolutionAlgorithm<R::Type>,
771                O: FiniteRingOperation<FreeAlgebraImplBase<R, V, A, C>>
772        {
773            type Output = O::Output;
774            fn execute(self) -> Self::Output where R::Type:FiniteRing {
775                self.operation.execute()
776            }
777            fn fallback(self) -> Self::Output {
778                self.operation.fallback()
779            }
780        }
781
782        <R::Type as FiniteRingSpecializable>::specialize(OpWrapper { operation: op, ring: PhantomData })
783    }
784}
785
786impl<R, V, A, C> FiniteRing for FreeAlgebraImplBase<R, V, A, C>
787    where R: RingStore, 
788        R::Type: FiniteRing, 
789        V: VectorView<El<R>>,
790        A: Allocator + Clone,
791        C: ConvolutionAlgorithm<R::Type>
792{
793    type ElementsIter<'a> = MultiProduct<<R::Type as FiniteRing>::ElementsIter<'a>, WRTCanonicalBasisElementCreator<'a, R, V, A, C>, CloneRingEl<&'a R>, Self::Element>
794        where Self: 'a;
795
796    fn elements<'a>(&'a self) -> Self::ElementsIter<'a> {
797        multi_cartesian_product((0..self.rank()).map(|_| self.base_ring().elements()), WRTCanonicalBasisElementCreator { base_ring: self }, CloneRingEl(self.base_ring()))
798    }
799
800    fn size<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
801        where I::Type: IntegerRing
802    {
803        let base_ring_size = self.base_ring().size(ZZ)?;
804        if ZZ.get_ring().representable_bits().is_none() || ZZ.abs_log2_ceil(&base_ring_size).unwrap() * self.rank() < ZZ.get_ring().representable_bits().unwrap() {
805            Some(ZZ.pow(base_ring_size, self.rank()))
806        } else {
807            None
808        }
809    }
810
811    fn random_element<G: FnMut() -> u64>(&self, mut rng: G) -> <Self as RingBase>::Element {
812        self.from_canonical_basis((0..self.rank()).map(|_| self.base_ring().random_element(&mut rng)))
813    }
814}
815
816impl_field_wrap_unwrap_homs!{
817    <{R1, V1, A1, C1, R2, V2, A2, C2}> FreeAlgebraImplBase<R1, V1, A1, C1>, FreeAlgebraImplBase<R2, V2, A2, C2>
818        where R1: RingStore, R1::Type: LinSolveRing, V1: VectorView<El<R1>>, A1: Allocator + Clone, C1: ConvolutionAlgorithm<R1::Type>,
819            R2: RingStore, R2::Type: LinSolveRing, V2: VectorView<El<R2>>, A2: Allocator + Clone, C2: ConvolutionAlgorithm<R2::Type>,
820            R2::Type: CanHomFrom<R1::Type>
821}
822
823impl_field_wrap_unwrap_isos!{
824    <{R1, V1, A1, C1, R2, V2, A2, C2}> FreeAlgebraImplBase<R1, V1, A1, C1>, FreeAlgebraImplBase<R2, V2, A2, C2>
825        where R1: RingStore, R1::Type: LinSolveRing, V1: VectorView<El<R1>>, A1: Allocator + Clone, C1: ConvolutionAlgorithm<R1::Type>,
826            R2: RingStore, R2::Type: LinSolveRing, V2: VectorView<El<R2>>, A2: Allocator + Clone, C2: ConvolutionAlgorithm<R2::Type>,
827            R2::Type: CanIsoFromTo<R1::Type>
828}
829
830impl_localpir_wrap_unwrap_homs!{
831    <{R1, V1, A1, C1, R2, V2, A2, C2}> FreeAlgebraImplBase<R1, V1, A1, C1>, FreeAlgebraImplBase<R2, V2, A2, C2>
832        where R1: RingStore, R1::Type: LinSolveRing, V1: VectorView<El<R1>>, A1: Allocator + Clone, C1: ConvolutionAlgorithm<R1::Type>,
833            R2: RingStore, R2::Type: LinSolveRing, V2: VectorView<El<R2>>, A2: Allocator + Clone, C2: ConvolutionAlgorithm<R2::Type>,
834            R2::Type: CanHomFrom<R1::Type>
835}
836
837impl_localpir_wrap_unwrap_isos!{
838    <{R1, V1, A1, C1, R2, V2, A2, C2}> FreeAlgebraImplBase<R1, V1, A1, C1>, FreeAlgebraImplBase<R2, V2, A2, C2>
839        where R1: RingStore, R1::Type: LinSolveRing, V1: VectorView<El<R1>>, A1: Allocator + Clone, C1: ConvolutionAlgorithm<R1::Type>,
840            R2: RingStore, R2::Type: LinSolveRing, V2: VectorView<El<R2>>, A2: Allocator + Clone, C2: ConvolutionAlgorithm<R2::Type>,
841            R2::Type: CanIsoFromTo<R1::Type>
842}
843
844impl<R1, V1, A1, C1, R2, V2, A2, C2> CanHomFrom<FreeAlgebraImplBase<R1, V1, A1, C1>> for FreeAlgebraImplBase<R2, V2, A2, C2>
845    where R1: RingStore, V1: VectorView<El<R1>>, A1: Allocator + Clone, C1: ConvolutionAlgorithm<R1::Type>,
846        R2: RingStore, V2: VectorView<El<R2>>, A2: Allocator + Clone, C2: ConvolutionAlgorithm<R2::Type>,
847        R2::Type: CanHomFrom<R1::Type>
848{
849    type Homomorphism = <R2::Type as CanHomFrom<R1::Type>>::Homomorphism;
850
851    fn has_canonical_hom(&self, from: &FreeAlgebraImplBase<R1, V1, A1, C1>) -> Option<Self::Homomorphism> {
852        if self.rank() == from.rank() {
853            let hom = self.base_ring.get_ring().has_canonical_hom(from.base_ring.get_ring())?;
854            if (0..self.rank()).all(|i| (i >= self.x_pow_rank.len() && i >= from.x_pow_rank.len()) ||
855                (i >= self.x_pow_rank.len() && from.base_ring().is_zero(from.x_pow_rank.at(i))) ||
856                (i >= from.x_pow_rank.len() && self.base_ring().is_zero(self.x_pow_rank.at(i))) ||
857                self.base_ring.eq_el(self.x_pow_rank.at(i), &self.base_ring.get_ring().map_in_ref(from.base_ring.get_ring(), from.x_pow_rank.at(i), &hom))
858            ) {
859                Some(hom)
860            } else {
861                None
862            }
863        } else {
864            None
865        }
866    }
867
868    fn map_in(&self, from: &FreeAlgebraImplBase<R1, V1, A1, C1>, el: <FreeAlgebraImplBase<R1, V1, A1> as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
869        self.from_canonical_basis((0..self.rank()).map(|i| self.base_ring.get_ring().map_in_ref(from.base_ring.get_ring(), &el.values[i], hom)))
870    }
871}
872
873impl<R1, V1, A1, C1, R2, V2, A2, C2> CanIsoFromTo<FreeAlgebraImplBase<R1, V1, A1, C1>> for FreeAlgebraImplBase<R2, V2, A2, C2>
874    where R1: RingStore, V1: VectorView<El<R1>>, A1: Allocator + Clone, C1: ConvolutionAlgorithm<R1::Type>,
875        R2: RingStore, V2: VectorView<El<R2>>, A2: Allocator + Clone, C2: ConvolutionAlgorithm<R2::Type>,
876        R2::Type: CanIsoFromTo<R1::Type>
877{
878    type Isomorphism = <R2::Type as CanIsoFromTo<R1::Type>>::Isomorphism;
879
880    fn has_canonical_iso(&self, from: &FreeAlgebraImplBase<R1, V1, A1, C1>) -> Option<Self::Isomorphism> {
881        if self.rank() == from.rank() {
882            let iso = self.base_ring.get_ring().has_canonical_iso(from.base_ring.get_ring())?;
883            if (0..self.rank()).all(|i|(i >= self.x_pow_rank.len() && i >= from.x_pow_rank.len()) ||
884                (i >= self.x_pow_rank.len() && from.base_ring().is_zero(from.x_pow_rank.at(i))) ||
885                (i >= from.x_pow_rank.len() && self.base_ring().is_zero(self.x_pow_rank.at(i))) ||
886                from.base_ring.eq_el(&self.base_ring.get_ring().map_out(from.base_ring.get_ring(), self.base_ring.clone_el(self.x_pow_rank.at(i)), &iso), from.x_pow_rank.at(i))
887            ) {
888                Some(iso)
889            } else {
890                None
891            }
892        } else {
893            None
894        }
895    }
896
897    fn map_out(&self, from: &FreeAlgebraImplBase<R1, V1, A1, C1>, el: <FreeAlgebraImplBase<R2, V2, A2> as RingBase>::Element, iso: &Self::Isomorphism) -> <FreeAlgebraImplBase<R1, V1, A1, C1> as RingBase>::Element {
898        from.from_canonical_basis((0..self.rank()).map(|i| self.base_ring.get_ring().map_out(from.base_ring.get_ring(), self.base_ring.clone_el(&el.values[i]), iso)))
899    }
900}
901
902#[cfg(test)]
903use crate::rings::zn::zn_64::{Zn, ZnEl};
904#[cfg(test)]
905use crate::rings::zn::ZnRingStore;
906#[cfg(test)]
907use crate::rings::zn::zn_static;
908#[cfg(test)]
909use crate::algorithms::convolution::fft::FFTConvolution;
910
911#[cfg(test)]
912fn test_ring0_and_elements() -> (FreeAlgebraImpl<Zn, Vec<ZnEl>>, Vec<FreeAlgebraImplEl<Zn>>) {
913    let R = FreeAlgebraImpl::new(Zn::new(7), 1, vec![Zn::new(7).neg_one()]);
914    let elements = R.elements().collect();
915    return (R, elements);
916}
917
918#[cfg(test)]
919fn test_ring1_and_elements() -> (FreeAlgebraImpl<StaticRing::<i64>, [i64; 2]>, Vec<FreeAlgebraImplEl<StaticRing<i64>>>) {
920    let ZZ = StaticRing::<i64>::RING;
921    let R = FreeAlgebraImpl::new(ZZ, 2, [1, 1]);
922    let mut elements = Vec::new();
923    for a in -3..=3 {
924        for b in -3..=3 {
925            elements.push(R.from_canonical_basis([a, b]));
926        }
927    }
928    return (R, elements);
929}
930
931#[cfg(test)]
932fn test_ring2_and_elements() -> (FreeAlgebraImpl<StaticRing::<i64>, Vec<i64>>, Vec<FreeAlgebraImplEl<StaticRing<i64>>>) {
933    let ZZ = StaticRing::<i64>::RING;
934    let R = FreeAlgebraImpl::new(ZZ, 3, vec![1, -1, 1]);
935    let mut elements = Vec::new();
936    for a in [-2, 0, 1, 2] {
937        for b in [-2, 0, 2] {
938            for c in [-2, 0, 2] {
939                elements.push(R.from_canonical_basis([a, b, c]));
940            }
941        }
942    }
943    return (R, elements);
944}
945
946#[cfg(test)]
947fn test_ring3_and_elements() -> (FreeAlgebraImpl<StaticRing::<i64>, Vec<i64>>, Vec<FreeAlgebraImplEl<StaticRing<i64>>>) {
948    let ZZ = StaticRing::<i64>::RING;
949    let R = FreeAlgebraImpl::new(ZZ, 3, vec![1]);
950    let mut elements = Vec::new();
951    for a in [-2, 0, 1, 2] {
952        for b in [-2, 0, 2] {
953            for c in [-2, 0, 2] {
954                elements.push(R.from_canonical_basis([a, b, c]));
955            }
956        }
957    }
958    return (R, elements);
959}
960
961#[cfg(test)]
962fn test_ring4_and_elements() -> (FreeAlgebraImpl<StaticRing::<i64>, SparseMapVector<StaticRing<i64>>>, Vec<FreeAlgebraImplEl<StaticRing<i64>>>) {
963    let ZZ = StaticRing::<i64>::RING;
964    // ZZ[X]/(X^3 - X)
965    let mut modulus = SparseMapVector::new(3, StaticRing::<i64>::RING);
966    *modulus.at_mut(1) = 1;
967    let R = FreeAlgebraImpl::new(ZZ, 3, modulus);
968    let mut elements = Vec::new();
969    for a in [-2, 0, 1, 2] {
970        for b in [-2, 0, 2] {
971            for c in [-2, 0, 2] {
972                elements.push(R.from_canonical_basis([a, b, c]));
973            }
974        }
975    }
976    return (R, elements);
977}
978
979#[test]
980fn test_sparse() {
981    let (ring, _) = test_ring4_and_elements();
982    assert_el_eq!(ring, ring.canonical_gen(), ring.pow(ring.canonical_gen(), 3));
983}
984
985#[test]
986fn test_ring_axioms() {
987    // let (ring, els) = test_ring0_and_elements();
988    // crate::ring::generic_tests::test_ring_axioms(ring, els.into_iter());
989    // let (ring, els) = test_ring1_and_elements();
990    // crate::ring::generic_tests::test_ring_axioms(ring, els.into_iter());
991    let (ring, els) = test_ring2_and_elements();
992    crate::ring::generic_tests::test_ring_axioms(ring, els.into_iter());
993    let (ring, els) = test_ring3_and_elements();
994    crate::ring::generic_tests::test_ring_axioms(ring, els.into_iter());
995    let (ring, els) = test_ring4_and_elements();
996    crate::ring::generic_tests::test_ring_axioms(ring, els.into_iter());
997    
998    let (ring, els) = test_ring2_and_elements();
999    let ring = ring.into().set_convolution(FFTConvolution::new_with_alloc(Global));
1000    crate::ring::generic_tests::test_ring_axioms(ring, els.into_iter());
1001
1002    let base_ring = zn_static::Fp::<257>::RING;
1003    let x_pow_rank = vec![base_ring.neg_one(); 64];
1004    let ring = FreeAlgebraImpl::new(base_ring, 64, x_pow_rank);
1005    let mut rng = oorandom::Rand64::new(1);
1006    let els = (0..10).map(|_| ring.from_canonical_basis((0..64).map(|_| ring.base_ring().random_element(|| rng.rand_u64()))));
1007    crate::ring::generic_tests::test_ring_axioms(&ring, els);
1008}
1009
1010#[test]
1011fn test_rank_1_ring() {
1012    let base_ring = Zn::new(5).as_field().ok().unwrap();
1013    let ring = FreeAlgebraImpl::new(base_ring, 1, [base_ring.int_hom().map(1)]).as_field().ok().unwrap();
1014    crate::field::generic_tests::test_field_axioms(&ring, ring.elements());
1015
1016    assert_el_eq!(ring, ring.one(), ring.canonical_gen());
1017}
1018
1019#[test]
1020fn test_free_algebra_axioms() {
1021    let (ring, _) = test_ring0_and_elements();
1022    super::generic_tests::test_free_algebra_axioms(ring);
1023    let (ring, _) = test_ring1_and_elements();
1024    super::generic_tests::test_free_algebra_axioms(ring);
1025    let (ring, _) = test_ring2_and_elements();
1026    super::generic_tests::test_free_algebra_axioms(ring);
1027    let (ring, _) = test_ring3_and_elements();
1028    super::generic_tests::test_free_algebra_axioms(ring);
1029    let (ring, _) = test_ring4_and_elements();
1030    super::generic_tests::test_free_algebra_axioms(ring);
1031}
1032
1033#[test]
1034fn test_division() {
1035    let base_ring = Zn::new(4);
1036    let i = base_ring.int_hom();
1037    let ring = FreeAlgebraImpl::new(base_ring, 2, [i.map(-1), i.map(-1)]);
1038
1039    assert_el_eq!(ring, 
1040        &ring.from_canonical_basis([i.map(1), i.map(3)]), 
1041        &ring.checked_div(&ring.one(), &ring.from_canonical_basis([i.map(2), i.map(3)])).unwrap()
1042    );
1043
1044    let a = ring.from_canonical_basis([i.map(2), i.map(2)]);
1045    let b = ring.from_canonical_basis([i.map(0), i.map(2)]);
1046    assert_el_eq!(ring, a, ring.mul(ring.checked_div(&a, &b).unwrap(), b));
1047
1048    assert!(ring.checked_div(&ring.one(), &a).is_none());
1049}
1050
1051#[test]
1052fn test_division_ring_of_integers() {
1053    let base_ring = StaticRing::<i64>::RING;
1054    let ring = FreeAlgebraImpl::new(base_ring, 2, [11, 0]);
1055
1056    // the solution to Pell's equation is 10^2 - 3^2 * 11 = 1
1057    let u = ring.from_canonical_basis([10, 3]);
1058    let u_inv = ring.from_canonical_basis([10, -3]);
1059
1060    assert_el_eq!(ring, u_inv, ring.checked_div(&ring.one(), &u).unwrap());
1061    assert_el_eq!(ring, ring.pow(u_inv, 3), &ring.checked_div(&ring.one(), &ring.pow(u, 3)).unwrap());
1062
1063    assert!(ring.checked_div(&ring.from_canonical_basis([2, 0]), &ring.from_canonical_basis([3, 0])).is_none());
1064}
1065
1066#[test]
1067fn test_divisibility_axioms() {
1068    let (ring, els) = test_ring0_and_elements();
1069    crate::divisibility::generic_tests::test_divisibility_axioms(ring, els.into_iter());
1070    let (ring, els) = test_ring1_and_elements();
1071    crate::divisibility::generic_tests::test_divisibility_axioms(ring, els.into_iter());
1072    let (ring, els) = test_ring2_and_elements();
1073    crate::divisibility::generic_tests::test_divisibility_axioms(ring, els.into_iter());
1074    let (ring, els) = test_ring3_and_elements();
1075    crate::divisibility::generic_tests::test_divisibility_axioms(ring, els.into_iter());
1076    let (ring, els) = test_ring4_and_elements();
1077    crate::divisibility::generic_tests::test_divisibility_axioms(ring, els.into_iter());
1078}
1079
1080#[test]
1081fn test_cubic_mul() {
1082    let base_ring = zn_static::Zn::<256>::RING;
1083    let modulo = base_ring.int_hom();
1084    let ring = FreeAlgebraImpl::new(base_ring, 3, [modulo.map(-1), modulo.map(-1), modulo.map(-1)]);
1085    let a = ring.from_canonical_basis([modulo.map(0), modulo.map(-1), modulo.map(-1)]);
1086    let b = ring.from_canonical_basis([modulo.map(-1), modulo.map(-2), modulo.map(-1)]);
1087    assert_el_eq!(ring, b, ring.pow(a, 2));
1088}
1089
1090#[test]
1091fn test_as_field() {
1092    let base_ring = Zn::new(5).as_field().ok().unwrap();
1093    let ring = FreeAlgebraImpl::new(base_ring, 1, [base_ring.int_hom().map(1)]).as_field().ok().unwrap();
1094    crate::field::generic_tests::test_field_axioms(&ring, ring.elements());
1095
1096    let base_ring = Zn::new(3).as_field().ok().unwrap();
1097    let ring = FreeAlgebraImpl::new(base_ring, 2, [base_ring.int_hom().map(2)]).as_field().ok().unwrap();
1098    crate::field::generic_tests::test_field_axioms(&ring, ring.elements());
1099
1100    assert!(FreeAlgebraImpl::new(base_ring, 2, [base_ring.int_hom().map(1)]).as_field().is_err());
1101}
1102
1103#[test]
1104fn test_serialization() {
1105    let (ring, els) = test_ring0_and_elements();
1106    crate::serialization::generic_tests::test_serialization(ring, els.into_iter());
1107    let (ring, els) = test_ring1_and_elements();
1108    crate::serialization::generic_tests::test_serialization(ring, els.into_iter());
1109    let (ring, els) = test_ring2_and_elements();
1110    crate::serialization::generic_tests::test_serialization(ring, els.into_iter());
1111    let (ring, els) = test_ring3_and_elements();
1112    crate::serialization::generic_tests::test_serialization(ring, els.into_iter());
1113    let (ring, els) = test_ring4_and_elements();
1114    crate::serialization::generic_tests::test_serialization(ring, els.into_iter());
1115}
1116
1117#[test]
1118#[should_panic]
1119fn test_from_canonical_basis_enforce_len() {
1120    let (ring, _) = test_ring1_and_elements();
1121    _ = ring.from_canonical_basis([0, 1, 2]);
1122}
1123
1124#[test]
1125fn test_serialize_deserialize() {
1126    crate::serialization::generic_tests::test_serialize_deserialize(test_ring0_and_elements().0.into());
1127    crate::serialization::generic_tests::test_serialize_deserialize(test_ring2_and_elements().0.into());
1128    crate::serialization::generic_tests::test_serialize_deserialize(test_ring3_and_elements().0.into());
1129}