1use std::alloc::{Allocator, Global};
2use std::marker::PhantomData;
3
4use extension_impl::FreeAlgebraImplBase;
5use feanor_serde::newtype_struct::*;
6use serde::de::DeserializeSeed;
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use sparse::SparseMapVector;
9use zn_64::Zn;
10
11use crate::algorithms::convolution::fft::*;
12use crate::algorithms::convolution::*;
13use crate::algorithms::eea::signed_gcd;
14use crate::algorithms::int_factor::{factor, is_prime_power};
15use crate::algorithms::matmul::{ComputeInnerProduct, StrassenHint};
16use crate::algorithms::poly_factor::cantor_zassenhaus;
17use crate::algorithms::poly_gcd::PolyTFracGCDRing;
18use crate::algorithms::poly_gcd::finite::poly_squarefree_part_finite_field;
19use crate::algorithms::unity_root::*;
20use crate::computation::DontObserve;
21use crate::delegate::{DelegateRing, DelegateRingImplFiniteRing};
22use crate::divisibility::{DivisibilityRingStore, Domain};
23use crate::field::*;
24use crate::integer::*;
25use crate::pid::{EuclideanRing, PrincipalIdealRing};
26use crate::primitive_int::{StaticRing, StaticRingBase};
27use crate::ring::*;
28use crate::rings::extension::extension_impl::FreeAlgebraImpl;
29use crate::rings::extension::*;
30use crate::rings::field::{AsField, AsFieldBase};
31use crate::rings::finite::*;
32use crate::rings::local::{AsLocalPIR, AsLocalPIRBase};
33use crate::rings::poly::PolyRing;
34use crate::rings::poly::dense_poly::DensePolyRing;
35use crate::rings::zn::*;
36use crate::serialization::*;
37
38fn filter_irreducible<R, P>(poly_ring: P, mod_f_ring: R, degree: usize) -> Option<El<P>>
39where
40 P: RingStore,
41 P::Type: PolyRing + EuclideanRing,
42 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + FiniteRing + Field,
43 R: RingStore,
44 R::Type: FreeAlgebra,
45 <R::Type as RingExtension>::BaseRing: RingStore<Type = <<P::Type as RingExtension>::BaseRing as RingStore>::Type>,
46{
47 let f = mod_f_ring.generating_poly(&poly_ring, &poly_ring.base_ring().identity());
48 let squarefree_part = poly_squarefree_part_finite_field(&poly_ring, &f, DontObserve);
49 if poly_ring.degree(&squarefree_part) != Some(degree) {
50 return None;
51 }
52 if !cantor_zassenhaus::squarefree_is_irreducible_base(&poly_ring, &mod_f_ring) {
53 return None;
54 }
55 return Some(mod_f_ring.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
56}
57
58fn find_small_irreducible_poly_base<P, C>(
64 poly_ring: P,
65 degree: usize,
66 convolution: C,
67 rng: &mut oorandom::Rand64,
68) -> El<P>
69where
70 P: RingStore,
71 P::Type: PolyRing + EuclideanRing,
72 <P::Type as RingExtension>::BaseRing: Copy,
73 <<P::Type as RingExtension>::BaseRing as RingStore>::Type:
74 ZnRing + Field + SelfIso + CanHomFrom<StaticRingBase<i64>>,
75 C: ConvolutionAlgorithm<<<P::Type as RingExtension>::BaseRing as RingStore>::Type>,
76{
77 let Fp = *poly_ring.base_ring();
78 let create_mod_f_ring = |f: &El<P>| {
79 let mut f_body = SparseMapVector::new(degree, poly_ring.base_ring());
80 for (c, i) in poly_ring.terms(f) {
81 if i != degree {
82 *f_body.at_mut(i) = poly_ring.base_ring().negate(poly_ring.base_ring().clone_el(c));
83 }
84 }
85 return FreeAlgebraImpl::new_with_convolution(Fp, degree, f_body, "θ", Global, &convolution);
86 };
87
88 if degree > 3 {
89 for _ in 0..16 {
91 let i = (StaticRing::<i64>::RING
92 .get_uniformly_random(&(TryInto::<i64>::try_into(degree).unwrap() - 1), || rng.rand_u64())
93 + 1) as usize;
94 let a = Fp.random_element(|| rng.rand_u64());
95 let b = Fp.random_element(|| rng.rand_u64());
96 let f = poly_ring.from_terms([(a, 0), (b, i), (Fp.one(), degree)]);
97 if let Some(result) = filter_irreducible(&poly_ring, create_mod_f_ring(&f), degree) {
98 return result;
99 }
100 }
101
102 let ZZbig = BigIntRing::RING;
106 let ZZ = StaticRing::<i64>::RING;
107
108 let p = Fp.size(&ZZbig).unwrap();
109 let mut small_d = 1;
110 let Fq_star_order = ZZbig.sub(ZZbig.pow(ZZbig.clone_el(&p), small_d as usize), ZZbig.one());
111 let mut large_d = int_cast(
112 signed_gcd(
113 Fq_star_order,
114 int_cast(
115 TryInto::<i64>::try_into(degree).unwrap() / small_d,
116 ZZbig,
117 StaticRing::<i64>::RING,
118 ),
119 ZZbig,
120 ),
121 ZZ,
122 ZZbig,
123 );
124 let mut increased_small_d = true;
125 while increased_small_d {
126 increased_small_d = false;
127 for (k, _) in factor(&ZZ, TryInto::<i64>::try_into(degree).unwrap() / small_d) {
129 let new_small_d = small_d * k;
130 let Fq_star_order = ZZbig.sub(ZZbig.pow(ZZbig.clone_el(&p), new_small_d as usize), ZZbig.one());
131 let new_large_d = int_cast(
132 signed_gcd(
133 Fq_star_order,
134 int_cast(
135 TryInto::<i64>::try_into(degree).unwrap() / new_small_d,
136 ZZbig,
137 StaticRing::<i64>::RING,
138 ),
139 ZZbig,
140 ),
141 ZZ,
142 ZZbig,
143 );
144 if new_large_d > large_d {
145 small_d = new_small_d;
146 large_d = new_large_d;
147 increased_small_d = true;
148 break;
149 }
150 }
151 }
152 small_d = TryInto::<i64>::try_into(degree).unwrap() / large_d;
153 if large_d != 1 {
154 let Fq_star_order = ZZbig.sub(ZZbig.pow(ZZbig.clone_el(&p), small_d as usize), ZZbig.one());
155 let Fq = GaloisField::new_internal(Fp, small_d as usize, Global, &convolution);
157 let primitive_element = if is_prim_root_of_unity_gen(&Fq, &Fq.canonical_gen(), &Fq_star_order, ZZbig) {
161 Fq.canonical_gen()
162 } else {
163 get_prim_root_of_unity_gen(&Fq, &Fq_star_order, ZZbig, &Fq_star_order).unwrap()
164 };
165 let FqX = DensePolyRing::new(&Fq, "X");
169 let minpoly = FqX.prod((0..small_d).map(|i| {
170 FqX.from_terms([
171 (
172 Fq.negate(Fq.pow_gen(
173 Fq.clone_el(&primitive_element),
174 &ZZbig.pow(ZZbig.clone_el(&p), i as usize),
175 ZZbig,
176 )),
177 0,
178 ),
179 (Fq.one(), 1),
180 ])
181 }));
182 let minpoly_Fp = poly_ring.from_terms(FqX.terms(&minpoly).map(|(c, i)| {
183 let c_wrt_basis = Fq.wrt_canonical_basis(c);
184 assert!(c_wrt_basis.iter().skip(1).all(|x| Fp.is_zero(&x)));
185 return (c_wrt_basis.at(0), i);
186 }));
187 let f = poly_ring.evaluate(
188 &minpoly_Fp,
189 &poly_ring.from_terms([(Fp.one(), large_d as usize)]),
190 poly_ring.inclusion(),
191 );
192 return f;
193 }
194 }
195
196 loop {
198 let f = poly_ring.from_terms(
199 (0..degree)
200 .map(|i| (Fp.random_element(|| rng.rand_u64()), i))
201 .chain([(Fp.one(), degree)]),
202 );
203 if let Some(result) = filter_irreducible(&poly_ring, create_mod_f_ring(&f), degree) {
204 return result;
205 }
206 }
207}
208
209fn find_small_irreducible_poly<P>(poly_ring: P, degree: usize, rng: &mut oorandom::Rand64) -> El<P>
210where
211 P: RingStore,
212 P::Type: PolyRing + EuclideanRing,
213 <P::Type as RingExtension>::BaseRing: Copy,
214 <<P::Type as RingExtension>::BaseRing as RingStore>::Type:
215 ZnRing + Field + SelfIso + CanHomFrom<StaticRingBase<i64>>,
216{
217 static_assert_impls!(<<P::Type as RingExtension>::BaseRing as RingStore>::Type: PolyTFracGCDRing);
218
219 let log2_modulus = poly_ring
220 .base_ring()
221 .integer_ring()
222 .abs_log2_ceil(poly_ring.base_ring().modulus())
223 .unwrap();
224 let fft_convolution = FFTConvolution::new_with_alloc(Global);
225 if fft_convolution.has_sufficient_precision(
226 StaticRing::<i64>::RING
227 .abs_log2_ceil(°ree.try_into().unwrap())
228 .unwrap()
229 + 1,
230 log2_modulus,
231 ) {
232 find_small_irreducible_poly_base(
233 &poly_ring,
234 degree,
235 <FFTConvolutionZn as From<_>>::from(fft_convolution),
236 rng,
237 )
238 } else {
239 find_small_irreducible_poly_base(&poly_ring, degree, STANDARD_CONVOLUTION, rng)
240 }
241}
242
243#[repr(transparent)]
327#[derive(Debug)]
328pub struct GaloisFieldBase<Impl>
329where
330 Impl: RingStore,
331 Impl::Type: Field + FreeAlgebra + FiniteRing,
332 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
333{
334 base: Impl,
335}
336
337pub type DefaultGaloisFieldImpl =
340 AsField<FreeAlgebraImpl<AsField<Zn>, SparseMapVector<AsField<Zn>>, Global, KaratsubaAlgorithm>>;
341
342pub type GaloisField<Impl = DefaultGaloisFieldImpl> = RingValue<GaloisFieldBase<Impl>>;
347
348pub type GaloisFieldOver<R, A = Global, C = KaratsubaAlgorithm> = RingValue<GaloisFieldBaseOver<R, A, C>>;
351
352pub type GaloisFieldBaseOver<R, A = Global, C = KaratsubaAlgorithm> =
355 GaloisFieldBase<AsField<FreeAlgebraImpl<R, SparseMapVector<R>, A, C>>>;
356
357impl GaloisField {
358 pub fn new(p: i64, degree: usize) -> Self {
386 let field = match Zn::new(p as u64).as_field() {
387 Ok(field) => field,
388 Err(_) => panic!("characteristic {p} is not a prime"),
389 };
390
391 Self::new_with_convolution(field, degree, Global, STANDARD_CONVOLUTION)
392 }
393}
394
395impl<R, A, C> GaloisFieldOver<R, A, C>
396where
397 R: RingStore + Clone,
398 R::Type: ZnRing + Field + SelfIso + CanHomFrom<StaticRingBase<i64>>,
399 C: ConvolutionAlgorithm<R::Type>,
400 A: Allocator + Clone,
401{
402 #[stability::unstable(feature = "enable")]
439 pub fn new_with_convolution(base_field: R, degree: usize, allocator: A, convolution_algorithm: C) -> Self {
440 assert!(degree >= 1);
441 let poly_ring = DensePolyRing::new(&base_field, "X");
442 let mut rng = oorandom::Rand64::new(
443 poly_ring
444 .base_ring()
445 .integer_ring()
446 .default_hash(poly_ring.base_ring().modulus()) as u128,
447 );
448 let modulus = find_small_irreducible_poly(&poly_ring, degree, &mut rng);
449 let mut modulus_vec = SparseMapVector::new(degree, base_field.clone());
450 for (c, i) in poly_ring.terms(&modulus) {
451 if i != degree {
452 *modulus_vec.at_mut(i) = base_field.negate(base_field.clone_el(c));
453 }
454 }
455 return RingValue::from(GaloisFieldBase {
456 base: AsField::from(AsFieldBase::promise_is_perfect_field(
457 FreeAlgebraImpl::new_with_convolution(
458 base_field,
459 degree,
460 modulus_vec,
461 "θ",
462 allocator,
463 convolution_algorithm,
464 ),
465 )),
466 });
467 }
468
469 fn new_internal(base_ring: R, degree: usize, allocator: A, convolution_algorithm: C) -> Self
473 where
474 R: Copy,
475 {
476 assert!(degree >= 1);
477 let poly_ring = DensePolyRing::new(base_ring, "X");
478 let mut rng = oorandom::Rand64::new(
479 poly_ring
480 .base_ring()
481 .integer_ring()
482 .default_hash(poly_ring.base_ring().modulus()) as u128,
483 );
484 let modulus = find_small_irreducible_poly(&poly_ring, degree, &mut rng);
485 let mut modulus_vec = SparseMapVector::new(degree, base_ring);
486 for (c, i) in poly_ring.terms(&modulus) {
487 if i != degree {
488 *modulus_vec.at_mut(i) = base_ring.negate(base_ring.clone_el(c));
489 }
490 }
491 return RingValue::from(GaloisFieldBase {
492 base: AsField::from(AsFieldBase::promise_is_perfect_field(
493 FreeAlgebraImpl::new_with_convolution(
494 base_ring,
495 degree,
496 modulus_vec,
497 "θ",
498 allocator,
499 convolution_algorithm,
500 ),
501 )),
502 });
503 }
504}
505
506impl<A> GaloisFieldBase<AsField<FreeAlgebraImpl<AsField<Zn>, SparseMapVector<AsField<Zn>>, A, KaratsubaAlgorithm>>>
507where
508 A: Allocator + Clone,
509{
510 #[stability::unstable(feature = "enable")]
521 pub fn galois_ring(&self, e: usize) -> AsLocalPIR<FreeAlgebraImpl<Zn, SparseMapVector<Zn>, A, KaratsubaAlgorithm>> {
522 self.galois_ring_with(
523 Zn::new(StaticRing::<i64>::RING.pow(*self.base_ring().modulus(), e) as u64),
524 self.base.get_ring().get_delegate().allocator().clone(),
525 STANDARD_CONVOLUTION,
526 )
527 }
528}
529
530impl<Impl> GaloisFieldBase<Impl>
531where
532 Impl: RingStore,
533 Impl::Type: Field + FreeAlgebra + FiniteRing,
534 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
535{
536 #[stability::unstable(feature = "enable")]
547 pub fn galois_ring_with<S, A2, C2>(
548 &self,
549 new_base_ring: S,
550 allocator: A2,
551 convolution_algorithm: C2,
552 ) -> AsLocalPIR<FreeAlgebraImpl<S, SparseMapVector<S>, A2, C2>>
553 where
554 S: RingStore + Clone,
555 S::Type: ZnRing
556 + CanHomFrom<<<<Impl::Type as RingExtension>::BaseRing as RingStore>::Type as ZnRing>::IntegerRingBase>,
557 C2: ConvolutionAlgorithm<S::Type>,
558 A2: Allocator + Clone,
559 {
560 let (p, e) = is_prime_power(&BigIntRing::RING, &new_base_ring.size(&BigIntRing::RING).unwrap()).unwrap();
561 assert!(BigIntRing::RING.eq_el(&p, &self.base_ring().size(&BigIntRing::RING).unwrap()));
562 let mut modulus_vec = SparseMapVector::new(self.rank(), new_base_ring.clone());
563 let x_pow_deg = RingRef::new(self).pow(self.canonical_gen(), self.rank());
564 let x_pow_deg = self.wrt_canonical_basis(&x_pow_deg);
565 let hom = new_base_ring.can_hom(self.base_ring().integer_ring()).unwrap();
566 for i in 0..self.rank() {
567 if !self.base_ring().is_zero(&x_pow_deg.at(i)) {
568 *modulus_vec.at_mut(i) = hom.map(self.base_ring().smallest_lift(x_pow_deg.at(i)));
569 }
570 }
571 let result = FreeAlgebraImpl::new_with_convolution(
572 new_base_ring,
573 self.rank(),
574 modulus_vec,
575 "θ",
576 allocator,
577 convolution_algorithm,
578 );
579 let hom = result.base_ring().can_hom(self.base_ring().integer_ring()).unwrap();
580 let max_ideal_gen = result.inclusion().map(hom.map_ref(self.base_ring().modulus()));
581 return AsLocalPIR::from(AsLocalPIRBase::promise_is_local_pir(result, max_ideal_gen, Some(e)));
582 }
583
584 #[stability::unstable(feature = "enable")]
585 pub fn unwrap_self(self) -> Impl { self.base }
586}
587
588impl<Impl> GaloisField<Impl>
589where
590 Impl: RingStore,
591 Impl::Type: Field + FreeAlgebra + FiniteRing,
592 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
593{
594 #[stability::unstable(feature = "enable")]
600 pub fn create(base: Impl) -> Self { RingValue::from(GaloisFieldBase { base }) }
601}
602
603impl<Impl> Clone for GaloisFieldBase<Impl>
604where
605 Impl: RingStore + Clone,
606 Impl::Type: Field + FreeAlgebra + FiniteRing,
607 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
608{
609 fn clone(&self) -> Self {
610 Self {
611 base: self.base.clone(),
612 }
613 }
614}
615
616impl<Impl> Copy for GaloisFieldBase<Impl>
617where
618 Impl: RingStore + Copy,
619 Impl::Type: Field + FreeAlgebra + FiniteRing,
620 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
621 El<Impl>: Copy,
622{
623}
624
625impl<Impl> PartialEq for GaloisFieldBase<Impl>
626where
627 Impl: RingStore,
628 Impl::Type: Field + FreeAlgebra + FiniteRing,
629 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
630{
631 fn eq(&self, other: &Self) -> bool { self.base.get_ring() == other.base.get_ring() }
632}
633
634impl<Impl> DelegateRing for GaloisFieldBase<Impl>
635where
636 Impl: RingStore,
637 Impl::Type: Field + FreeAlgebra + FiniteRing,
638 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
639{
640 type Base = Impl::Type;
641 type Element = El<Impl>;
642
643 fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element { el }
644 fn delegate_mut<'a>(&self, el: &'a mut Self::Element) -> &'a mut <Self::Base as RingBase>::Element { el }
645 fn delegate_ref<'a>(&self, el: &'a Self::Element) -> &'a <Self::Base as RingBase>::Element { el }
646 fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element { el }
647
648 fn get_delegate(&self) -> &Self::Base { self.base.get_ring() }
649}
650
651impl<Impl> DelegateRingImplFiniteRing for GaloisFieldBase<Impl>
652where
653 Impl: RingStore,
654 Impl::Type: Field + FreeAlgebra + FiniteRing,
655 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
656{
657}
658
659impl<Impl> Domain for GaloisFieldBase<Impl>
660where
661 Impl: RingStore,
662 Impl::Type: Field + FreeAlgebra + FiniteRing,
663 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
664{
665}
666
667impl<Impl> Field for GaloisFieldBase<Impl>
668where
669 Impl: RingStore,
670 Impl::Type: Field + FreeAlgebra + FiniteRing,
671 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
672{
673}
674
675impl<Impl> PerfectField for GaloisFieldBase<Impl>
676where
677 Impl: RingStore,
678 Impl::Type: Field + FreeAlgebra + FiniteRing,
679 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
680{
681}
682
683impl<Impl> EuclideanRing for GaloisFieldBase<Impl>
700where
701 Impl: RingStore,
702 Impl::Type: Field + FreeAlgebra + FiniteRing,
703 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
704{
705 fn euclidean_div_rem(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element) {
706 assert!(!self.is_zero(rhs));
707 (self.base.checked_div(&lhs, rhs).unwrap(), self.zero())
708 }
709
710 fn euclidean_deg(&self, val: &Self::Element) -> Option<usize> { if self.is_zero(val) { Some(0) } else { Some(1) } }
711
712 fn euclidean_rem(&self, _: Self::Element, rhs: &Self::Element) -> Self::Element {
713 assert!(!self.is_zero(rhs));
714 self.zero()
715 }
716}
717
718impl<Impl> PrincipalIdealRing for GaloisFieldBase<Impl>
719where
720 Impl: RingStore,
721 Impl::Type: Field + FreeAlgebra + FiniteRing,
722 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
723{
724 fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
725 if self.is_zero(lhs) && self.is_zero(rhs) {
726 Some(self.one())
727 } else {
728 self.checked_left_div(lhs, rhs)
729 }
730 }
731
732 fn extended_ideal_gen(
733 &self,
734 lhs: &Self::Element,
735 rhs: &Self::Element,
736 ) -> (Self::Element, Self::Element, Self::Element) {
737 if self.is_zero(lhs) {
738 (self.zero(), self.one(), self.clone_el(rhs))
739 } else {
740 (self.one(), self.zero(), self.clone_el(lhs))
741 }
742 }
743}
744
745impl<Impl> Serialize for GaloisFieldBase<Impl>
746where
747 Impl: RingStore + Serialize,
748 Impl::Type: Field + FreeAlgebra + FiniteRing + SerializableElementRing,
749 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
750{
751 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
752 where
753 S: Serializer,
754 {
755 SerializableNewtypeStruct::new("GaloisField", &self.base).serialize(serializer)
756 }
757}
758
759impl<'de, Impl> Deserialize<'de> for GaloisFieldBase<Impl>
760where
761 Impl: RingStore + Deserialize<'de>,
762 Impl::Type: Field + FreeAlgebra + FiniteRing + SerializableElementRing,
763 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
764{
765 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
766 where
767 D: Deserializer<'de>,
768 {
769 DeserializeSeedNewtypeStruct::new("GaloisField", PhantomData::<Impl>)
770 .deserialize(deserializer)
771 .map(|res| GaloisField::create(res).into())
772 }
773}
774
775impl<Impl> KaratsubaHint for GaloisFieldBase<Impl>
776where
777 Impl: RingStore,
778 Impl::Type: Field + FreeAlgebra + FiniteRing,
779 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
780{
781 fn karatsuba_threshold(&self) -> usize { self.get_delegate().karatsuba_threshold() }
782}
783
784impl<Impl> ComputeInnerProduct for GaloisFieldBase<Impl>
785where
786 Impl: RingStore,
787 Impl::Type: Field + FreeAlgebra + FiniteRing,
788 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
789{
790 fn inner_product<I: Iterator<Item = (Self::Element, Self::Element)>>(&self, els: I) -> Self::Element {
791 self.rev_delegate(
792 self.get_delegate()
793 .inner_product(els.map(|(a, b)| (self.delegate(a), self.delegate(b)))),
794 )
795 }
796
797 fn inner_product_ref<'a, I: Iterator<Item = (&'a Self::Element, &'a Self::Element)>>(&self, els: I) -> Self::Element
798 where
799 Self::Element: 'a,
800 Self: 'a,
801 {
802 self.rev_delegate(
803 self.get_delegate()
804 .inner_product_ref(els.map(|(a, b)| (self.delegate_ref(a), self.delegate_ref(b)))),
805 )
806 }
807
808 fn inner_product_ref_fst<'a, I: Iterator<Item = (&'a Self::Element, Self::Element)>>(&self, els: I) -> Self::Element
809 where
810 Self::Element: 'a,
811 Self: 'a,
812 {
813 self.rev_delegate(
814 self.get_delegate()
815 .inner_product_ref_fst(els.map(|(a, b)| (self.delegate_ref(a), self.delegate(b)))),
816 )
817 }
818}
819
820impl<Impl> StrassenHint for GaloisFieldBase<Impl>
821where
822 Impl: RingStore,
823 Impl::Type: Field + FreeAlgebra + FiniteRing,
824 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
825{
826 fn strassen_threshold(&self) -> usize { self.get_delegate().strassen_threshold() }
827}
828
829impl<Impl, S> CanHomFrom<S> for GaloisFieldBase<Impl>
831where
832 Impl: RingStore,
833 Impl::Type: Field + FreeAlgebra + FiniteRing + CanHomFrom<S>,
834 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
835 S: ?Sized + RingBase,
836{
837 type Homomorphism = <Impl::Type as CanHomFrom<S>>::Homomorphism;
838
839 fn has_canonical_hom(&self, from: &S) -> Option<Self::Homomorphism> { self.base.get_ring().has_canonical_hom(from) }
840
841 fn map_in(&self, from: &S, el: <S as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
842 self.base.get_ring().map_in(from, el, hom)
843 }
844}
845
846impl<Impl, R, A, V, C> CanHomFrom<GaloisFieldBase<Impl>> for FreeAlgebraImplBase<R, V, A, C>
848where
849 Impl: RingStore,
850 Impl::Type: Field + FreeAlgebra + FiniteRing,
851 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
852 R: RingStore,
853 V: VectorView<El<R>>,
854 C: ConvolutionAlgorithm<R::Type>,
855 A: Allocator + Clone,
856 FreeAlgebraImplBase<R, V, A, C>: CanHomFrom<Impl::Type>,
857{
858 type Homomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanHomFrom<Impl::Type>>::Homomorphism;
859
860 fn has_canonical_hom(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Homomorphism> {
861 self.has_canonical_hom(from.base.get_ring())
862 }
863
864 fn map_in(
865 &self,
866 from: &GaloisFieldBase<Impl>,
867 el: <GaloisFieldBase<Impl> as RingBase>::Element,
868 hom: &Self::Homomorphism,
869 ) -> Self::Element {
870 self.map_in(from.base.get_ring(), el, hom)
871 }
872}
873
874impl<Impl, R, A, V, C> CanHomFrom<GaloisFieldBase<Impl>> for AsFieldBase<FreeAlgebraImpl<R, V, A, C>>
876where
877 Impl: RingStore,
878 Impl::Type: Field + FreeAlgebra + FiniteRing,
879 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
880 R: RingStore,
881 R::Type: LinSolveRing,
882 V: VectorView<El<R>>,
883 C: ConvolutionAlgorithm<R::Type>,
884 A: Allocator + Clone,
885 FreeAlgebraImplBase<R, V, A, C>: CanHomFrom<Impl::Type>,
886{
887 type Homomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanHomFrom<Impl::Type>>::Homomorphism;
888
889 fn has_canonical_hom(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Homomorphism> {
890 self.get_delegate().has_canonical_hom(from.base.get_ring())
891 }
892
893 fn map_in(
894 &self,
895 from: &GaloisFieldBase<Impl>,
896 el: <GaloisFieldBase<Impl> as RingBase>::Element,
897 hom: &Self::Homomorphism,
898 ) -> Self::Element {
899 self.rev_delegate(self.get_delegate().map_in(from.base.get_ring(), el, hom))
900 }
901}
902
903impl<Impl, S> CanIsoFromTo<S> for GaloisFieldBase<Impl>
905where
906 Impl: RingStore,
907 Impl::Type: Field + FreeAlgebra + FiniteRing + CanIsoFromTo<S>,
908 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
909 S: ?Sized + RingBase,
910{
911 type Isomorphism = <Impl::Type as CanIsoFromTo<S>>::Isomorphism;
912
913 fn has_canonical_iso(&self, from: &S) -> Option<Self::Isomorphism> { self.base.get_ring().has_canonical_iso(from) }
914
915 fn map_out(&self, from: &S, el: Self::Element, iso: &Self::Isomorphism) -> <S as RingBase>::Element {
916 self.base.get_ring().map_out(from, el, iso)
917 }
918}
919
920impl<Impl, R, A, V, C> CanIsoFromTo<GaloisFieldBase<Impl>> for FreeAlgebraImplBase<R, V, A, C>
922where
923 Impl: RingStore,
924 Impl::Type: Field + FreeAlgebra + FiniteRing,
925 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
926 R: RingStore,
927 V: VectorView<El<R>>,
928 C: ConvolutionAlgorithm<R::Type>,
929 A: Allocator + Clone,
930 FreeAlgebraImplBase<R, V, A, C>: CanIsoFromTo<Impl::Type>,
931{
932 type Isomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanIsoFromTo<Impl::Type>>::Isomorphism;
933
934 fn has_canonical_iso(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Isomorphism> {
935 self.has_canonical_iso(from.base.get_ring())
936 }
937
938 fn map_out(
939 &self,
940 from: &GaloisFieldBase<Impl>,
941 el: Self::Element,
942 iso: &Self::Isomorphism,
943 ) -> <GaloisFieldBase<Impl> as RingBase>::Element {
944 self.map_out(from.base.get_ring(), el, iso)
945 }
946}
947
948impl<Impl, R, A, V, C> CanIsoFromTo<GaloisFieldBase<Impl>> for AsFieldBase<FreeAlgebraImpl<R, V, A, C>>
950where
951 Impl: RingStore,
952 Impl::Type: Field + FreeAlgebra + FiniteRing,
953 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
954 R: RingStore,
955 R::Type: LinSolveRing,
956 V: VectorView<El<R>>,
957 C: ConvolutionAlgorithm<R::Type>,
958 A: Allocator + Clone,
959 FreeAlgebraImplBase<R, V, A, C>: CanIsoFromTo<Impl::Type>,
960{
961 type Isomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanIsoFromTo<Impl::Type>>::Isomorphism;
962
963 fn has_canonical_iso(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Isomorphism> {
964 self.get_delegate().has_canonical_iso(from.base.get_ring())
965 }
966
967 fn map_out(
968 &self,
969 from: &GaloisFieldBase<Impl>,
970 el: Self::Element,
971 iso: &Self::Isomorphism,
972 ) -> <GaloisFieldBase<Impl> as RingBase>::Element {
973 self.get_delegate()
974 .map_out(from.base.get_ring(), self.delegate(el), iso)
975 }
976}
977
978#[cfg(test)]
979use std::time::Instant;
980
981#[cfg(test)]
982use test::Bencher;
983
984#[test]
985fn test_can_hom_from() {
986 #[allow(unused)]
987 fn assert_impl_CanHomFrom<From, To>()
988 where
989 To: ?Sized + CanHomFrom<From>,
990 From: ?Sized + RingBase,
991 {
992 }
993
994 #[allow(unused)]
995 fn FreeAlgebraImpl_wrap_unwrap_homs<R, V, A, C>()
996 where
997 R: RingStore,
998 R::Type: SelfIso + LinSolveRing,
999 V: VectorView<El<R>>,
1000 A: Allocator + Clone,
1001 C: ConvolutionAlgorithm<R::Type>,
1002 {
1003 assert_impl_CanHomFrom::<FreeAlgebraImplBase<R, V, A, C>, AsFieldBase<FreeAlgebraImpl<R, V, A, C>>>();
1004 }
1005
1006 #[allow(unused)]
1007 fn FreeAlgebraImpl_from_GaloisField<R, V, A, C>()
1008 where
1009 R: RingStore,
1010 R::Type: SelfIso + LinSolveRing + FiniteRing + Field + ZnRing,
1011 V: VectorView<El<R>>,
1012 A: Allocator + Clone,
1013 C: ConvolutionAlgorithm<R::Type>,
1014 {
1015 assert_impl_CanHomFrom::<
1016 GaloisFieldBase<AsField<FreeAlgebraImpl<R, V, A, C>>>,
1017 AsFieldBase<FreeAlgebraImpl<R, V, A, C>>,
1018 >();
1019 }
1020
1021 #[allow(unused)]
1022 fn GaloisField_from_GaloisField<R, V, A, C>()
1023 where
1024 R: RingStore,
1025 R::Type: SelfIso + LinSolveRing + FiniteRing + Field + ZnRing,
1026 V: VectorView<El<R>>,
1027 A: Allocator + Clone,
1028 C: ConvolutionAlgorithm<R::Type>,
1029 {
1030 assert_impl_CanHomFrom::<
1031 GaloisFieldBase<AsField<FreeAlgebraImpl<R, V, A, C>>>,
1032 GaloisFieldBase<AsField<FreeAlgebraImpl<R, V, A, C>>>,
1033 >();
1034 }
1035}
1036
1037#[test]
1038fn test_galois_field() {
1039 let field = GaloisField::new(3, 1);
1040 assert_eq!(3, field.elements().count());
1041 crate::ring::generic_tests::test_ring_axioms(&field, field.elements());
1042 crate::ring::generic_tests::test_self_iso(&field, field.elements());
1043 crate::field::generic_tests::test_field_axioms(&field, field.elements());
1044
1045 let field = GaloisField::new(3, 2);
1046 assert_eq!(9, field.elements().count());
1047 crate::ring::generic_tests::test_ring_axioms(&field, field.elements());
1048 crate::ring::generic_tests::test_self_iso(&field, field.elements());
1049 crate::field::generic_tests::test_field_axioms(&field, field.elements());
1050}
1051
1052#[test]
1053fn test_principal_ideal_ring_axioms() {
1054 let field = GaloisField::new(3, 2);
1055 crate::pid::generic_tests::test_principal_ideal_ring_axioms(&field, field.elements());
1056 crate::pid::generic_tests::test_euclidean_ring_axioms(&field, field.elements());
1057}
1058
1059#[test]
1060fn test_galois_field_even() {
1061 for degree in 1..=9 {
1062 let field = GaloisField::new_with_convolution(
1063 Zn::new(2).as_field().ok().unwrap(),
1064 degree,
1065 Global,
1066 STANDARD_CONVOLUTION,
1067 );
1068 assert_eq!(degree, field.rank());
1069 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
1070 }
1071}
1072
1073#[test]
1074fn test_galois_field_odd() {
1075 for degree in 1..=9 {
1076 let field = GaloisField::new_with_convolution(
1077 Zn::new(3).as_field().ok().unwrap(),
1078 degree,
1079 Global,
1080 STANDARD_CONVOLUTION,
1081 );
1082 assert_eq!(degree, field.rank());
1083 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
1084 }
1085
1086 for degree in 1..=9 {
1087 let field = GaloisField::new_with_convolution(
1088 Zn::new(5).as_field().ok().unwrap(),
1089 degree,
1090 Global,
1091 STANDARD_CONVOLUTION,
1092 );
1093 assert_eq!(degree, field.rank());
1094 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
1095 }
1096}
1097
1098#[test]
1099fn test_galois_field_no_trinomial() {
1100 let field =
1101 GaloisField::new_with_convolution(Zn::new(2).as_field().ok().unwrap(), 24, Global, STANDARD_CONVOLUTION);
1102 assert_eq!(24, field.rank());
1103 let poly_ring = DensePolyRing::new(field.base_ring(), "X");
1104 poly_ring.println(&field.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
1105 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
1106
1107 let field =
1108 GaloisField::new_with_convolution(Zn::new(3).as_field().ok().unwrap(), 30, Global, STANDARD_CONVOLUTION);
1109 assert_eq!(30, field.rank());
1110 let poly_ring = DensePolyRing::new(field.base_ring(), "X");
1111 poly_ring.println(&field.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
1112 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
1113
1114 let field =
1115 GaloisField::new_with_convolution(Zn::new(17).as_field().ok().unwrap(), 32, Global, STANDARD_CONVOLUTION);
1116 assert_eq!(32, field.rank());
1117 let poly_ring = DensePolyRing::new(field.base_ring(), "X");
1118 poly_ring.println(&field.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
1119 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
1120}
1121
1122#[bench]
1123fn bench_create_galois_ring_2_14_96(bencher: &mut Bencher) {
1124 bencher.iter(|| {
1125 let field = GaloisField::new(2, 96);
1126 let ring = field.get_ring().galois_ring(14);
1127 assert_eq!(96, ring.rank());
1128 });
1129}
1130
1131#[test]
1132#[ignore]
1133fn test_galois_field_huge() {
1134 let start = Instant::now();
1135 let field = GaloisField::new(17, 2048);
1136 _ = std::hint::black_box(field);
1137 let end = Instant::now();
1138 println!("Created GF(17^2048) in {} ms", (end - start).as_millis());
1139}