Skip to main content

feanor_math/rings/extension/
extension_impl.rs

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