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