Skip to main content

feanor_math/rings/
local.rs

1use super::field::{AsField, AsFieldBase};
2use crate::algorithms::convolution::KaratsubaHint;
3use crate::algorithms::int_factor::is_prime_power;
4use crate::algorithms::matmul::*;
5use crate::delegate::*;
6use crate::divisibility::{DivisibilityRing, DivisibilityRingStore, Domain};
7use crate::field::Field;
8use crate::homomorphism::*;
9use crate::integer::IntegerRing;
10use crate::local::{PrincipalLocalRing, PrincipalLocalRingStore};
11use crate::pid::*;
12use crate::reduce_lift::poly_eval::InterpolationBaseRing;
13use crate::ring::*;
14use crate::rings::zn::*;
15
16/// A wrapper around a ring that marks this ring to be a local principal ideal ring.
17///
18/// The design is analogous to [`crate::rings::field::AsFieldBase`].
19#[stability::unstable(feature = "enable")]
20pub struct AsLocalPIRBase<R: DivisibilityRingStore>
21where
22    R::Type: DivisibilityRing,
23{
24    base: R,
25    max_ideal_gen: LocalPIREl<R>,
26    nilpotent_power: Option<usize>,
27}
28
29impl<R> Clone for AsLocalPIRBase<R>
30where
31    R: DivisibilityRingStore + Clone,
32    R::Type: DivisibilityRing,
33{
34    fn clone(&self) -> Self {
35        Self {
36            base: self.base.clone(),
37            max_ideal_gen: self.clone_el(&self.max_ideal_gen),
38            nilpotent_power: self.nilpotent_power,
39        }
40    }
41}
42
43impl<R> Copy for AsLocalPIRBase<R>
44where
45    R: DivisibilityRingStore + Copy,
46    R::Type: DivisibilityRing,
47    El<R>: Copy,
48{
49}
50
51impl<R> PartialEq for AsLocalPIRBase<R>
52where
53    R: DivisibilityRingStore,
54    R::Type: DivisibilityRing,
55{
56    fn eq(&self, other: &Self) -> bool { self.base.get_ring() == other.base.get_ring() }
57}
58
59/// [`RingStore`] for [`AsLocalPIRBase`].
60#[stability::unstable(feature = "enable")]
61pub type AsLocalPIR<R> = RingValue<AsLocalPIRBase<R>>;
62
63#[stability::unstable(feature = "enable")]
64pub struct LocalPIREl<R: DivisibilityRingStore>(El<R>)
65where
66    R::Type: DivisibilityRing;
67
68impl<R: DivisibilityRingStore> Clone for LocalPIREl<R>
69where
70    El<R>: Clone,
71    R::Type: DivisibilityRing,
72{
73    fn clone(&self) -> Self { LocalPIREl(self.0.clone()) }
74}
75
76impl<R: DivisibilityRingStore> Copy for LocalPIREl<R>
77where
78    El<R>: Copy,
79    R::Type: DivisibilityRing,
80{
81}
82
83impl<R> AsLocalPIR<R>
84where
85    R: RingStore,
86    R::Type: ZnRing,
87{
88    #[stability::unstable(feature = "enable")]
89    pub fn from_zn(ring: R) -> Option<Self> {
90        let (p, e) = is_prime_power(ring.integer_ring(), ring.modulus())?;
91        let g = ring.can_hom(ring.integer_ring()).unwrap().map(p);
92        Some(Self::from(AsLocalPIRBase::promise_is_local_pir(ring, g, Some(e))))
93    }
94}
95
96impl<R> AsLocalPIR<R>
97where
98    R: RingStore,
99    R::Type: Field,
100{
101    #[stability::unstable(feature = "enable")]
102    pub fn from_field(ring: R) -> Self {
103        let zero = ring.zero();
104        Self::from(AsLocalPIRBase::promise_is_local_pir(ring, zero, Some(1)))
105    }
106}
107
108impl<R> AsLocalPIR<R>
109where
110    R: RingStore,
111    R::Type: PrincipalLocalRing,
112{
113    #[stability::unstable(feature = "enable")]
114    pub fn from_localpir(ring: R) -> Self {
115        let max_ideal_gen = ring.clone_el(ring.max_ideal_gen());
116        let nilpotent_power = ring.nilpotent_power();
117        Self::from(AsLocalPIRBase::promise_is_local_pir(
118            ring,
119            max_ideal_gen,
120            nilpotent_power,
121        ))
122    }
123}
124
125impl<R> AsLocalPIR<R>
126where
127    R: RingStore,
128    R::Type: DivisibilityRing,
129{
130    #[stability::unstable(feature = "enable")]
131    pub fn from_as_field(ring: AsField<R>) -> Self {
132        let ring = ring.into().unwrap_self();
133        let zero = ring.zero();
134        Self::from(AsLocalPIRBase::promise_is_local_pir(ring, zero, Some(1)))
135    }
136}
137
138impl<R: DivisibilityRingStore> AsLocalPIRBase<R>
139where
140    R::Type: DivisibilityRing,
141{
142    #[stability::unstable(feature = "enable")]
143    pub fn promise_is_local_pir(base: R, max_ideal_gen: El<R>, nilpotent_power: Option<usize>) -> Self {
144        assert!(base.is_commutative());
145        let max_ideal_gen = LocalPIREl(max_ideal_gen);
146        Self {
147            base,
148            max_ideal_gen,
149            nilpotent_power,
150        }
151    }
152
153    #[stability::unstable(feature = "enable")]
154    pub fn unwrap_element(&self, el: <Self as RingBase>::Element) -> El<R> { el.0 }
155
156    #[stability::unstable(feature = "enable")]
157    pub fn unwrap_self(self) -> R { self.base }
158}
159
160impl<R: DivisibilityRingStore> DelegateRing for AsLocalPIRBase<R>
161where
162    R::Type: DivisibilityRing,
163{
164    type Element = LocalPIREl<R>;
165    type Base = R::Type;
166
167    fn get_delegate(&self) -> &Self::Base { self.base.get_ring() }
168
169    fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element { el.0 }
170
171    fn delegate_mut<'a>(&self, el: &'a mut Self::Element) -> &'a mut <Self::Base as RingBase>::Element { &mut el.0 }
172
173    fn delegate_ref<'a>(&self, el: &'a Self::Element) -> &'a <Self::Base as RingBase>::Element { &el.0 }
174
175    fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element { LocalPIREl(el) }
176}
177
178impl<R: DivisibilityRingStore> DelegateRingImplFiniteRing for AsLocalPIRBase<R> where R::Type: DivisibilityRing {}
179
180impl<R: DivisibilityRingStore, S: DivisibilityRingStore> CanHomFrom<AsLocalPIRBase<S>> for AsLocalPIRBase<R>
181where
182    R::Type: DivisibilityRing + CanHomFrom<S::Type>,
183    S::Type: DivisibilityRing,
184{
185    type Homomorphism = <R::Type as CanHomFrom<S::Type>>::Homomorphism;
186
187    fn has_canonical_hom(&self, from: &AsLocalPIRBase<S>) -> Option<Self::Homomorphism> {
188        <R::Type as CanHomFrom<S::Type>>::has_canonical_hom(self.get_delegate(), from.get_delegate())
189    }
190
191    fn map_in(&self, from: &AsLocalPIRBase<S>, el: LocalPIREl<S>, hom: &Self::Homomorphism) -> Self::Element {
192        LocalPIREl(<R::Type as CanHomFrom<S::Type>>::map_in(
193            self.get_delegate(),
194            from.get_delegate(),
195            el.0,
196            hom,
197        ))
198    }
199}
200
201impl<R: DivisibilityRingStore, S: DivisibilityRingStore> CanIsoFromTo<AsLocalPIRBase<S>> for AsLocalPIRBase<R>
202where
203    R::Type: DivisibilityRing + CanIsoFromTo<S::Type>,
204    S::Type: DivisibilityRing,
205{
206    type Isomorphism = <R::Type as CanIsoFromTo<S::Type>>::Isomorphism;
207
208    fn has_canonical_iso(&self, from: &AsLocalPIRBase<S>) -> Option<Self::Isomorphism> {
209        <R::Type as CanIsoFromTo<S::Type>>::has_canonical_iso(self.get_delegate(), from.get_delegate())
210    }
211
212    fn map_out(&self, from: &AsLocalPIRBase<S>, el: Self::Element, iso: &Self::Isomorphism) -> LocalPIREl<S> {
213        LocalPIREl(<R::Type as CanIsoFromTo<S::Type>>::map_out(
214            self.get_delegate(),
215            from.get_delegate(),
216            el.0,
217            iso,
218        ))
219    }
220}
221
222/// Necessary to potentially implement [`crate::rings::zn::ZnRing`].
223impl<R: DivisibilityRingStore, S: IntegerRing + ?Sized> CanHomFrom<S> for AsLocalPIRBase<R>
224where
225    R::Type: DivisibilityRing + CanHomFrom<S>,
226{
227    type Homomorphism = <R::Type as CanHomFrom<S>>::Homomorphism;
228
229    fn has_canonical_hom(&self, from: &S) -> Option<Self::Homomorphism> { self.get_delegate().has_canonical_hom(from) }
230
231    fn map_in(&self, from: &S, el: S::Element, hom: &Self::Homomorphism) -> Self::Element {
232        LocalPIREl(<R::Type as CanHomFrom<S>>::map_in(self.get_delegate(), from, el, hom))
233    }
234}
235
236impl<R: DivisibilityRingStore> DivisibilityRing for AsLocalPIRBase<R>
237where
238    R::Type: DivisibilityRing,
239{
240    fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
241        self.get_delegate().checked_left_div(&lhs.0, &rhs.0).map(LocalPIREl)
242    }
243}
244
245impl<R: DivisibilityRingStore> PrincipalIdealRing for AsLocalPIRBase<R>
246where
247    R::Type: DivisibilityRing,
248{
249    fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
250        if let Some(e) = self.nilpotent_power {
251            if self.is_zero(lhs) && self.is_zero(rhs) {
252                return Some(self.one());
253            } else if self.is_zero(lhs) {
254                return Some(
255                    RingRef::new(self).pow(self.clone_el(self.max_ideal_gen()), e - self.valuation(rhs).unwrap()),
256                );
257            } else {
258                // the constraint `rhs * result = lhs` already fixes the evaluation of `result`
259                // uniquely
260                return self.checked_left_div(lhs, rhs);
261            }
262        } else {
263            // we are in a domain
264            self.checked_left_div(lhs, rhs)
265        }
266    }
267
268    fn extended_ideal_gen(
269        &self,
270        lhs: &Self::Element,
271        rhs: &Self::Element,
272    ) -> (Self::Element, Self::Element, Self::Element) {
273        if self.checked_left_div(lhs, rhs).is_some() {
274            (self.zero(), self.one(), self.clone_el(rhs))
275        } else {
276            (self.one(), self.zero(), self.clone_el(lhs))
277        }
278    }
279
280    fn create_elimination_matrix(&self, a: &Self::Element, b: &Self::Element) -> ([Self::Element; 4], Self::Element) {
281        if let Some(quo) = self.checked_left_div(b, a) {
282            (
283                [self.one(), self.zero(), self.negate(quo), self.one()],
284                self.clone_el(a),
285            )
286        } else {
287            let quo = self.checked_left_div(a, b).unwrap();
288            (
289                [self.zero(), self.one(), self.one(), self.negate(quo)],
290                self.clone_el(b),
291            )
292        }
293    }
294}
295
296impl<R: DivisibilityRingStore> KaratsubaHint for AsLocalPIRBase<R>
297where
298    R::Type: DivisibilityRing,
299{
300    fn karatsuba_threshold(&self) -> usize { self.get_delegate().karatsuba_threshold() }
301}
302
303impl<R: DivisibilityRingStore> StrassenHint for AsLocalPIRBase<R>
304where
305    R::Type: DivisibilityRing,
306{
307    fn strassen_threshold(&self) -> usize { self.get_delegate().strassen_threshold() }
308}
309
310impl<R: DivisibilityRingStore> ComputeInnerProduct for AsLocalPIRBase<R>
311where
312    R::Type: DivisibilityRing,
313{
314    fn inner_product<I: Iterator<Item = (Self::Element, Self::Element)>>(&self, els: I) -> Self::Element {
315        self.rev_delegate(
316            self.get_delegate()
317                .inner_product(els.map(|(a, b)| (self.delegate(a), self.delegate(b)))),
318        )
319    }
320
321    fn inner_product_ref<'a, I: Iterator<Item = (&'a Self::Element, &'a Self::Element)>>(&self, els: I) -> Self::Element
322    where
323        Self::Element: 'a,
324        Self: 'a,
325    {
326        self.rev_delegate(
327            self.get_delegate()
328                .inner_product_ref(els.map(|(a, b)| (self.delegate_ref(a), self.delegate_ref(b)))),
329        )
330    }
331
332    fn inner_product_ref_fst<'a, I: Iterator<Item = (&'a Self::Element, Self::Element)>>(&self, els: I) -> Self::Element
333    where
334        Self::Element: 'a,
335        Self: 'a,
336    {
337        self.rev_delegate(
338            self.get_delegate()
339                .inner_product_ref_fst(els.map(|(a, b)| (self.delegate_ref(a), self.delegate(b)))),
340        )
341    }
342}
343
344impl<R: DivisibilityRingStore> EuclideanRing for AsLocalPIRBase<R>
345where
346    R::Type: DivisibilityRing,
347{
348    fn euclidean_div_rem(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element) {
349        if let Some(quo) = self.checked_left_div(&lhs, rhs) {
350            (quo, self.zero())
351        } else {
352            (self.zero(), lhs)
353        }
354    }
355
356    fn euclidean_deg(&self, val: &Self::Element) -> Option<usize> { self.valuation(val) }
357}
358
359impl<R: DivisibilityRingStore> PrincipalLocalRing for AsLocalPIRBase<R>
360where
361    R::Type: DivisibilityRing,
362{
363    fn max_ideal_gen(&self) -> &Self::Element { &self.max_ideal_gen }
364
365    fn nilpotent_power(&self) -> Option<usize> { self.nilpotent_power }
366}
367
368impl<R: DivisibilityRingStore> Domain for AsLocalPIRBase<R> where R::Type: DivisibilityRing + Domain {}
369
370impl<R> FromModulusCreateableZnRing for AsLocalPIRBase<RingValue<R>>
371where
372    R: DivisibilityRing + ZnRing + FromModulusCreateableZnRing,
373{
374    fn from_modulus<F, E>(create_modulus: F) -> Result<Self, E>
375    where
376        F: FnOnce(&Self::IntegerRingBase) -> Result<El<Self::IntegerRing>, E>,
377    {
378        <R as FromModulusCreateableZnRing>::from_modulus(create_modulus)
379            .map(|ring| AsLocalPIR::from_zn(RingValue::from(ring)).unwrap().into())
380    }
381}
382
383impl<R: DivisibilityRingStore> Field for AsLocalPIRBase<R> where R::Type: DivisibilityRing + Field {}
384
385impl<R> InterpolationBaseRing for AsLocalPIRBase<R>
386where
387    R: RingStore,
388    R::Type: InterpolationBaseRing,
389{
390    type ExtendedRingBase<'a>
391        = <R::Type as InterpolationBaseRing>::ExtendedRingBase<'a>
392    where
393        Self: 'a;
394
395    type ExtendedRing<'a>
396        = <R::Type as InterpolationBaseRing>::ExtendedRing<'a>
397    where
398        Self: 'a;
399
400    fn in_base<'a, S>(&self, ext_ring: S, el: El<S>) -> Option<Self::Element>
401    where
402        Self: 'a,
403        S: RingStore<Type = Self::ExtendedRingBase<'a>>,
404    {
405        self.get_delegate().in_base(ext_ring, el).map(|x| self.rev_delegate(x))
406    }
407
408    fn in_extension<'a, S>(&self, ext_ring: S, el: Self::Element) -> El<S>
409    where
410        Self: 'a,
411        S: RingStore<Type = Self::ExtendedRingBase<'a>>,
412    {
413        self.get_delegate().in_extension(ext_ring, self.delegate(el))
414    }
415
416    fn interpolation_points<'a>(&'a self, count: usize) -> (Self::ExtendedRing<'a>, Vec<El<Self::ExtendedRing<'a>>>) {
417        self.get_delegate().interpolation_points(count)
418    }
419}
420
421impl<R1, R2> CanHomFrom<AsFieldBase<R1>> for AsLocalPIRBase<R2>
422where
423    R1: RingStore,
424    R2: RingStore,
425    R2::Type: CanHomFrom<R1::Type>,
426    R1::Type: DivisibilityRing,
427    R2::Type: DivisibilityRing,
428{
429    type Homomorphism = <R2::Type as CanHomFrom<R1::Type>>::Homomorphism;
430
431    fn has_canonical_hom(&self, from: &AsFieldBase<R1>) -> Option<Self::Homomorphism> {
432        self.get_delegate().has_canonical_hom(from.get_delegate())
433    }
434
435    fn map_in(
436        &self,
437        from: &AsFieldBase<R1>,
438        el: <AsFieldBase<R1> as RingBase>::Element,
439        hom: &Self::Homomorphism,
440    ) -> Self::Element {
441        self.rev_delegate(self.get_delegate().map_in(from.get_delegate(), from.delegate(el), hom))
442    }
443}
444
445impl<R1, R2> CanIsoFromTo<AsFieldBase<R1>> for AsLocalPIRBase<R2>
446where
447    R1: RingStore,
448    R2: RingStore,
449    R2::Type: CanIsoFromTo<R1::Type>,
450    R1::Type: DivisibilityRing,
451    R2::Type: DivisibilityRing,
452{
453    type Isomorphism = <R2::Type as CanIsoFromTo<R1::Type>>::Isomorphism;
454
455    fn has_canonical_iso(&self, from: &AsFieldBase<R1>) -> Option<Self::Isomorphism> {
456        self.get_delegate().has_canonical_iso(from.get_delegate())
457    }
458
459    fn map_out(
460        &self,
461        from: &AsFieldBase<R1>,
462        el: Self::Element,
463        iso: &Self::Isomorphism,
464    ) -> <AsFieldBase<R1> as RingBase>::Element {
465        from.rev_delegate(self.get_delegate().map_out(from.get_delegate(), self.delegate(el), iso))
466    }
467}
468
469/// Implements the isomorphisms `S: CanHomFrom<AsFieldBase<RingStore<Type = R>>>` and
470/// `AsFieldBase<RingStore<Type = S>>: CanHomFrom<R>`.
471///
472/// For details, see [`crate::impl_field_wrap_unwrap_homs!`]
473#[macro_export]
474macro_rules! impl_localpir_wrap_unwrap_homs {
475    (<{$($gen_args:tt)*}> $self_type_from:ty, $self_type_to:ty where $($constraints:tt)*) => {
476
477        impl<AsLocalPIRStore, $($gen_args)*> CanHomFrom<$self_type_from> for $crate::rings::local::AsLocalPIRBase<AsLocalPIRStore>
478            where AsLocalPIRStore: RingStore<Type = $self_type_to>, $($constraints)*
479        {
480            type Homomorphism = <$self_type_to as CanHomFrom<$self_type_from>>::Homomorphism;
481
482            fn has_canonical_hom(&self, from: &$self_type_from) -> Option<Self::Homomorphism> {
483                self.get_delegate().has_canonical_hom(from)
484            }
485
486            fn map_in(&self, from: &$self_type_from, el: <$self_type_from as $crate::ring::RingBase>::Element, hom: &Self::Homomorphism) -> <Self as $crate::ring::RingBase>::Element {
487                self.rev_delegate(self.get_delegate().map_in(from, el, hom))
488            }
489        }
490
491        impl<AsLocalPIRStore, $($gen_args)*> CanHomFrom<$crate::rings::local::AsLocalPIRBase<AsLocalPIRStore>> for $self_type_to
492            where AsLocalPIRStore: RingStore<Type = $self_type_from>, $($constraints)*
493        {
494            type Homomorphism = <$self_type_to as CanHomFrom<$self_type_from>>::Homomorphism;
495
496            fn has_canonical_hom(&self, from: &$crate::rings::local::AsLocalPIRBase<AsLocalPIRStore>) -> Option<Self::Homomorphism> {
497                self.has_canonical_hom(from.get_delegate())
498            }
499
500            fn map_in(&self, from: &$crate::rings::local::AsLocalPIRBase<AsLocalPIRStore>, el: $crate::rings::local::LocalPIREl<AsLocalPIRStore>, hom: &Self::Homomorphism) -> <Self as $crate::ring::RingBase>::Element {
501                self.map_in(from.get_delegate(), from.delegate(el), hom)
502            }
503        }
504    };
505    ($self_type_from:ty, $self_type_to:ty) => {
506        impl_localpir_wrap_unwrap_homs!{ <{}> $self_type_from, $self_type_to where }
507    };
508}
509
510/// Implements the isomorphisms `S: CanIsoFromTo<AsLocalPIRBase<RingStore<Type = R>>>` and
511/// `AsLocalPIRBase<RingStore<Type = S>>: CanIsoFromTo<R>`.
512///
513/// For details, see [`crate::impl_field_wrap_unwrap_isos!`]
514#[macro_export]
515macro_rules! impl_localpir_wrap_unwrap_isos {
516    (<{$($gen_args:tt)*}> $self_type_from:ty, $self_type_to:ty where $($constraints:tt)*) => {
517
518        impl<AsLocalPIRStore, $($gen_args)*> CanIsoFromTo<$self_type_from> for $crate::rings::local::AsLocalPIRBase<AsLocalPIRStore>
519            where AsLocalPIRStore: RingStore<Type = $self_type_to>, $($constraints)*
520        {
521            type Isomorphism = <$self_type_to as CanIsoFromTo<$self_type_from>>::Isomorphism;
522
523            fn has_canonical_iso(&self, from: &$self_type_from) -> Option<Self::Isomorphism> {
524                self.get_delegate().has_canonical_iso(from)
525            }
526
527            fn map_out(&self, from: &$self_type_from, el: <Self as RingBase>::Element, iso: &Self::Isomorphism) -> <$self_type_from as RingBase>::Element {
528                self.get_delegate().map_out(from, self.delegate(el), iso)
529            }
530        }
531
532        impl<AsLocalPIRStore, $($gen_args)*> CanIsoFromTo<$crate::rings::local::AsLocalPIRBase<AsLocalPIRStore>> for $self_type_to
533            where AsLocalPIRStore: RingStore<Type = $self_type_from>, $($constraints)*
534        {
535            type Isomorphism = <$self_type_to as CanIsoFromTo<$self_type_from>>::Isomorphism;
536
537            fn has_canonical_iso(&self, from: &$crate::rings::local::AsLocalPIRBase<AsLocalPIRStore>) -> Option<Self::Isomorphism> {
538                self.has_canonical_iso(from.get_delegate())
539            }
540
541            fn map_out(&self, from: &$crate::rings::local::AsLocalPIRBase<AsLocalPIRStore>, el: <Self as RingBase>::Element, hom: &Self::Isomorphism) -> $crate::rings::local::LocalPIREl<AsLocalPIRStore> {
542                from.rev_delegate(self.map_out(from.get_delegate(), el, hom))
543            }
544        }
545    };
546    ($self_type_from:ty, $self_type_to:ty) => {
547        impl_localpir_wrap_unwrap_isos!{ <{}> $self_type_from, $self_type_to where }
548    };
549}
550
551#[cfg(test)]
552use std::alloc::Global;
553#[cfg(test)]
554use std::time::Instant;
555
556#[cfg(test)]
557use super::extension::galois_field::GaloisField;
558#[cfg(test)]
559use crate::algorithms::linsolve::LinSolveRing;
560#[cfg(test)]
561use crate::assert_matrix_eq;
562#[cfg(test)]
563use crate::matrix::{OwnedMatrix, TransposableSubmatrix, TransposableSubmatrixMut};
564#[cfg(test)]
565use crate::primitive_int::*;
566#[cfg(test)]
567use crate::rings::finite::FiniteRingStore;
568#[cfg(test)]
569use crate::rings::zn::zn_big::Zn;
570
571#[test]
572fn test_canonical_hom_axioms_static_int() {
573    let R = AsLocalPIR::from_zn(Zn::new(StaticRing::<i64>::RING, 8)).unwrap();
574    crate::ring::generic_tests::test_hom_axioms(StaticRing::<i64>::RING, &R, 0..8);
575}
576
577#[test]
578fn test_divisibility_axioms() {
579    let R = AsLocalPIR::from_zn(Zn::new(StaticRing::<i64>::RING, 8)).unwrap();
580    crate::divisibility::generic_tests::test_divisibility_axioms(&R, R.elements());
581}
582
583#[test]
584fn test_principal_ideal_ring_axioms() {
585    let R = AsLocalPIR::from_zn(Zn::new(StaticRing::<i64>::RING, 8)).unwrap();
586    crate::pid::generic_tests::test_principal_ideal_ring_axioms(&R, R.elements());
587    let R = AsLocalPIR::from_zn(Zn::new(StaticRing::<i64>::RING, 9)).unwrap();
588    crate::pid::generic_tests::test_principal_ideal_ring_axioms(&R, R.elements());
589    let R = AsLocalPIR::from_zn(Zn::new(StaticRing::<i64>::RING, 17)).unwrap();
590    crate::pid::generic_tests::test_principal_ideal_ring_axioms(&R, R.elements());
591}
592
593#[test]
594fn test_canonical_hom_axioms_wrap_unwrap() {
595    let R = AsLocalPIR::from_zn(Zn::new(StaticRing::<i64>::RING, 8)).unwrap();
596    crate::ring::generic_tests::test_hom_axioms(
597        RingRef::new(R.get_ring().get_delegate()),
598        &R,
599        RingRef::new(R.get_ring().get_delegate()).elements(),
600    );
601    crate::ring::generic_tests::test_iso_axioms(
602        RingRef::new(R.get_ring().get_delegate()),
603        &R,
604        RingRef::new(R.get_ring().get_delegate()).elements(),
605    );
606}
607
608#[test]
609fn test_checked_div_min() {
610    let ring = AsLocalPIR::from_zn(Zn::new(StaticRing::<i64>::RING, 27)).unwrap();
611    assert_el_eq!(
612        &ring,
613        ring.zero(),
614        ring.checked_div_min(&ring.zero(), &ring.one()).unwrap()
615    );
616    assert_el_eq!(
617        &ring,
618        ring.int_hom().map(9),
619        ring.checked_div_min(&ring.zero(), &ring.int_hom().map(3)).unwrap()
620    );
621    assert_el_eq!(
622        &ring,
623        ring.int_hom().map(3),
624        ring.checked_div_min(&ring.zero(), &ring.int_hom().map(9)).unwrap()
625    );
626    assert_el_eq!(
627        &ring,
628        ring.one(),
629        ring.checked_div_min(&ring.zero(), &ring.zero()).unwrap()
630    );
631}
632
633#[test]
634#[ignore]
635fn test_solve_large_galois_ring() {
636    let ring: AsLocalPIR<_> = GaloisField::new(17, 2048).get_ring().galois_ring(5);
637    let mut matrix: OwnedMatrix<_> = OwnedMatrix::zero(2, 2, &ring);
638    let mut rng = oorandom::Rand64::new(1);
639
640    *matrix.at_mut(0, 0) = ring.random_element(|| rng.rand_u64());
641    *matrix.at_mut(0, 1) = ring.random_element(|| rng.rand_u64());
642    *matrix.at_mut(1, 1) = ring.random_element(|| rng.rand_u64());
643    assert!(
644        ring.is_unit(&ring.sub_ref(matrix.at(0, 1), matrix.at(1, 1))),
645        "matrix generation failed, pick another seed"
646    );
647    *matrix.at_mut(1, 0) = ring.clone_el(matrix.at(0, 0));
648
649    let mut rhs: OwnedMatrix<_> = OwnedMatrix::zero(2, 1, &ring);
650    *rhs.at_mut(0, 0) = ring.random_element(|| rng.rand_u64());
651    *rhs.at_mut(0, 1) = ring.random_element(|| rng.rand_u64());
652
653    let rhs = rhs;
654    let matrix = matrix;
655    let mut result: OwnedMatrix<_> = OwnedMatrix::zero(2, 1, &ring);
656
657    let start = Instant::now();
658    ring.get_ring()
659        .solve_right(
660            matrix.clone_matrix(&ring).data_mut(),
661            rhs.clone_matrix(&ring).data_mut(),
662            result.data_mut(),
663            Global,
664        )
665        .assert_solved();
666    let end = Instant::now();
667    println!("Solved over GR(17, 5, 2048) in {} ms", (end - start).as_millis());
668
669    let mut product: OwnedMatrix<_> = OwnedMatrix::zero(2, 1, &ring);
670    STANDARD_MATMUL.matmul(
671        TransposableSubmatrix::from(matrix.data()),
672        TransposableSubmatrix::from(result.data()),
673        TransposableSubmatrixMut::from(product.data_mut()),
674        &ring,
675    );
676
677    assert_matrix_eq!(ring, rhs, product);
678}