1use std::alloc::{Allocator, Global};
2use std::marker::PhantomData;
3
4use extension_impl::FreeAlgebraImplBase;
5use sparse::SparseMapVector;
6use zn_64::Zn;
7
8use crate::algorithms::convolution::*;
9use crate::algorithms::eea::signed_gcd;
10use crate::algorithms::poly_gcd::finite::poly_squarefree_part_finite_field;
11use crate::algorithms::int_factor::factor;
12use crate::algorithms::int_factor::is_prime_power;
13use crate::algorithms::matmul::{ComputeInnerProduct, StrassenHint};
14use crate::algorithms::poly_gcd::PolyTFracGCDRing;
15use crate::algorithms::unity_root::*;
16use crate::computation::DontObserve;
17use crate::delegate::{DelegateRing, DelegateRingImplFiniteRing};
18use crate::divisibility::{DivisibilityRingStore, Domain};
19use crate::field::*;
20use crate::pid::PrincipalIdealRing;
21use crate::rings::extension::extension_impl::FreeAlgebraImpl;
22use crate::rings::finite::*;
23use crate::algorithms::convolution::fft::*;
24use crate::algorithms::poly_factor::cantor_zassenhaus;
25use crate::pid::EuclideanRing;
26use crate::primitive_int::{StaticRing, StaticRingBase};
27use crate::rings::field::{AsField, AsFieldBase};
28use crate::rings::local::{AsLocalPIR, AsLocalPIRBase};
29use crate::rings::poly::dense_poly::DensePolyRing;
30use crate::rings::poly::PolyRing;
31use crate::rings::zn::*;
32use crate::ring::*;
33use crate::rings::extension::*;
34use crate::integer::*;
35use crate::serialization::*;
36
37use feanor_serde::newtype_struct::*;
38use serde::{Serialize, Deserialize, Deserializer, Serializer};
39use serde::de::DeserializeSeed;
40
41fn filter_irreducible<R, P>(poly_ring: P, mod_f_ring: R, degree: usize) -> Option<El<P>>
42 where P: RingStore,
43 P::Type: PolyRing + EuclideanRing,
44 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + FiniteRing + Field,
45 R: RingStore,
46 R::Type: FreeAlgebra,
47 <R::Type as RingExtension>::BaseRing: RingStore<Type = <<P::Type as RingExtension>::BaseRing as RingStore>::Type>
48{
49 let f = mod_f_ring.generating_poly(&poly_ring, &poly_ring.base_ring().identity());
50 let squarefree_part = poly_squarefree_part_finite_field(&poly_ring, &f, DontObserve);
51 if poly_ring.degree(&squarefree_part) != Some(degree) {
52 return None;
53 }
54 if !cantor_zassenhaus::squarefree_is_irreducible_base(&poly_ring, &mod_f_ring) {
55 return None;
56 }
57 return Some(mod_f_ring.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
58}
59
60fn find_small_irreducible_poly_base<P, C>(poly_ring: P, degree: usize, convolution: C, rng: &mut oorandom::Rand64) -> El<P>
68 where P: RingStore,
69 P::Type: PolyRing + EuclideanRing,
70 <P::Type as RingExtension>::BaseRing: Copy,
71 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field + SelfIso + CanHomFrom<StaticRingBase<i64>>,
72 C: ConvolutionAlgorithm<<<P::Type as RingExtension>::BaseRing as RingStore>::Type>
73{
74 let Fp = *poly_ring.base_ring();
75 let create_mod_f_ring = |f: &El<P>| {
76 let mut f_body = SparseMapVector::new(degree, poly_ring.base_ring());
77 for (c, i) in poly_ring.terms(f) {
78 if i != degree {
79 *f_body.at_mut(i) = poly_ring.base_ring().negate(poly_ring.base_ring().clone_el(c));
80 }
81 }
82 return FreeAlgebraImpl::new_with_convolution(Fp, degree, f_body, "θ", Global, &convolution);
83 };
84
85 if degree > 3 {
86
87 for _ in 0..16 {
89 let i = (StaticRing::<i64>::RING.get_uniformly_random(&(TryInto::<i64>::try_into(degree).unwrap() - 1), || rng.rand_u64()) + 1) as usize;
90 let a = Fp.random_element(|| rng.rand_u64());
91 let b = Fp.random_element(|| rng.rand_u64());
92 let f = poly_ring.from_terms([(a, 0), (b, i), (Fp.one(), degree)].into_iter());
93 if let Some(result) = filter_irreducible(&poly_ring, create_mod_f_ring(&f), degree) {
94 return result;
95 }
96 }
97
98 let ZZbig = BigIntRing::RING;
101 let ZZ = StaticRing::<i64>::RING;
102
103 let p = Fp.size(&ZZbig).unwrap();
104 let mut small_d = 1;
105 let Fq_star_order = ZZbig.sub(ZZbig.pow(ZZbig.clone_el(&p), small_d as usize), ZZbig.one());
106 let mut large_d = int_cast(signed_gcd(Fq_star_order, int_cast(TryInto::<i64>::try_into(degree).unwrap() / small_d, ZZbig, StaticRing::<i64>::RING), ZZbig), ZZ, ZZbig);
107 let mut increased_small_d = true;
108 while increased_small_d {
109 increased_small_d = false;
110 for (k, _) in factor(&ZZ, TryInto::<i64>::try_into(degree).unwrap() / small_d) {
112 let new_small_d = small_d * k;
113 let Fq_star_order = ZZbig.sub(ZZbig.pow(ZZbig.clone_el(&p), new_small_d as usize), ZZbig.one());
114 let new_large_d = int_cast(signed_gcd(Fq_star_order, int_cast(TryInto::<i64>::try_into(degree).unwrap() / new_small_d, ZZbig, StaticRing::<i64>::RING), ZZbig), ZZ, ZZbig);
115 if new_large_d > large_d {
116 small_d = new_small_d;
117 large_d = new_large_d;
118 increased_small_d = true;
119 break;
120 }
121 }
122 }
123 small_d = TryInto::<i64>::try_into(degree).unwrap() / large_d;
124 if large_d != 1 {
125 let Fq_star_order = ZZbig.sub(ZZbig.pow(ZZbig.clone_el(&p), small_d as usize), ZZbig.one());
126 let Fq = GaloisField::new_internal(Fp, small_d as usize, Global, &convolution);
128 let primitive_element = if is_prim_root_of_unity_gen(&Fq, &Fq.canonical_gen(), &Fq_star_order, ZZbig) {
131 Fq.canonical_gen()
132 } else {
133 get_prim_root_of_unity_gen(&Fq, &Fq_star_order, ZZbig, &Fq_star_order).unwrap()
134 };
135 let FqX = DensePolyRing::new(&Fq, "X");
138 let minpoly = FqX.prod((0..small_d).map(|i| FqX.from_terms([(Fq.negate(Fq.pow_gen(Fq.clone_el(&primitive_element), &ZZbig.pow(ZZbig.clone_el(&p), i as usize), ZZbig)), 0), (Fq.one(), 1)].into_iter())));
139 let minpoly_Fp = poly_ring.from_terms(FqX.terms(&minpoly).map(|(c, i)| {
140 let c_wrt_basis = Fq.wrt_canonical_basis(c);
141 assert!(c_wrt_basis.iter().skip(1).all(|x| Fp.is_zero(&x)));
142 return (c_wrt_basis.at(0), i);
143 }));
144 let f = poly_ring.evaluate(&minpoly_Fp, &poly_ring.from_terms([(Fp.one(), large_d as usize)].into_iter()), &poly_ring.inclusion());
145 return f;
146 }
147 }
148
149 loop {
151 let f = poly_ring.from_terms((0..degree).map(|i| (Fp.random_element(|| rng.rand_u64()), i)).chain([(Fp.one(), degree)].into_iter()));
152 if let Some(result) = filter_irreducible(&poly_ring, create_mod_f_ring(&f), degree) {
153 return result;
154 }
155 }
156}
157
158fn find_small_irreducible_poly<P>(poly_ring: P, degree: usize, rng: &mut oorandom::Rand64) -> El<P>
159 where P: RingStore,
160 P::Type: PolyRing + EuclideanRing,
161 <P::Type as RingExtension>::BaseRing: Copy,
162 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field + SelfIso + CanHomFrom<StaticRingBase<i64>>
163{
164 static_assert_impls!(<<P::Type as RingExtension>::BaseRing as RingStore>::Type: PolyTFracGCDRing);
165
166 let log2_modulus = poly_ring.base_ring().integer_ring().abs_log2_ceil(poly_ring.base_ring().modulus()).unwrap();
167 let fft_convolution = FFTConvolution::new_with_alloc(Global);
168 if fft_convolution.has_sufficient_precision(StaticRing::<i64>::RING.abs_log2_ceil(°ree.try_into().unwrap()).unwrap() + 1, log2_modulus) {
169 find_small_irreducible_poly_base(
170 &poly_ring,
171 degree,
172 <FFTConvolutionZn as From<_>>::from(fft_convolution),
173 rng
174 )
175 } else {
176 find_small_irreducible_poly_base(
177 &poly_ring,
178 degree,
179 STANDARD_CONVOLUTION,
180 rng
181 )
182 }
183}
184
185#[repr(transparent)]
264#[derive(Debug)]
265pub struct GaloisFieldBase<Impl>
266 where Impl: RingStore,
267 Impl::Type: Field + FreeAlgebra + FiniteRing,
268 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
269{
270 base: Impl
271}
272
273pub type DefaultGaloisFieldImpl = AsField<FreeAlgebraImpl<AsField<Zn>, SparseMapVector<AsField<Zn>>, Global, KaratsubaAlgorithm>>;
278
279pub type GaloisField<Impl = DefaultGaloisFieldImpl> = RingValue<GaloisFieldBase<Impl>>;
285
286pub type GaloisFieldOver<R, A = Global, C = KaratsubaAlgorithm> = RingValue<GaloisFieldBaseOver<R, A, C>>;
291
292pub type GaloisFieldBaseOver<R, A = Global, C = KaratsubaAlgorithm> = GaloisFieldBase<AsField<FreeAlgebraImpl<R, SparseMapVector<R>, A, C>>>;
297
298impl GaloisField {
299
300 pub fn new(p: i64, degree: usize) -> Self {
328 let field = match Zn::new(p as u64).as_field() {
329 Ok(field) => field,
330 Err(_) => panic!("characteristic {p} is not a prime"),
331 };
332
333 Self::new_with_convolution(field, degree, Global, STANDARD_CONVOLUTION)
334 }
335}
336
337impl<R, A, C> GaloisFieldOver<R, A, C>
338 where R: RingStore + Clone,
339 R::Type: ZnRing + Field + SelfIso + CanHomFrom<StaticRingBase<i64>>,
340 C: ConvolutionAlgorithm<R::Type>,
341 A: Allocator + Clone
342{
343 #[stability::unstable(feature = "enable")]
375 pub fn new_with_convolution(base_field: R, degree: usize, allocator: A, convolution_algorithm: C) -> Self {
376 assert!(degree >= 1);
377 let poly_ring = DensePolyRing::new(&base_field, "X");
378 let mut rng = oorandom::Rand64::new(poly_ring.base_ring().integer_ring().default_hash(poly_ring.base_ring().modulus()) as u128);
379 let modulus = find_small_irreducible_poly(&poly_ring, degree, &mut rng);
380 let mut modulus_vec = SparseMapVector::new(degree, base_field.clone());
381 for (c, i) in poly_ring.terms(&modulus) {
382 if i != degree {
383 *modulus_vec.at_mut(i) = base_field.negate(base_field.clone_el(c));
384 }
385 }
386 return RingValue::from(GaloisFieldBase {
387 base: AsField::from(AsFieldBase::promise_is_perfect_field(FreeAlgebraImpl::new_with_convolution(base_field, degree, modulus_vec, "θ", allocator, convolution_algorithm)))
388 });
389 }
390
391 fn new_internal(base_ring: R, degree: usize, allocator: A, convolution_algorithm: C) -> Self
397 where R: Copy
398 {
399 assert!(degree >= 1);
400 let poly_ring = DensePolyRing::new(base_ring.clone(), "X");
401 let mut rng = oorandom::Rand64::new(poly_ring.base_ring().integer_ring().default_hash(poly_ring.base_ring().modulus()) as u128);
402 let modulus = find_small_irreducible_poly(&poly_ring, degree, &mut rng);
403 let mut modulus_vec = SparseMapVector::new(degree, base_ring.clone());
404 for (c, i) in poly_ring.terms(&modulus) {
405 if i != degree {
406 *modulus_vec.at_mut(i) = base_ring.negate(base_ring.clone_el(c));
407 }
408 }
409 return RingValue::from(GaloisFieldBase {
410 base: AsField::from(AsFieldBase::promise_is_perfect_field(FreeAlgebraImpl::new_with_convolution(base_ring, degree, modulus_vec, "θ", allocator, convolution_algorithm)))
411 });
412 }
413}
414
415impl<A> GaloisFieldBase<AsField<FreeAlgebraImpl<AsField<Zn>, SparseMapVector<AsField<Zn>>, A, KaratsubaAlgorithm>>>
416 where A: Allocator + Clone
417{
418 #[stability::unstable(feature = "enable")]
430 pub fn galois_ring(&self, e: usize) -> AsLocalPIR<FreeAlgebraImpl<Zn, SparseMapVector<Zn>, A, KaratsubaAlgorithm>> {
431 self.galois_ring_with(Zn::new(StaticRing::<i64>::RING.pow(*self.base_ring().modulus(), e) as u64), self.base.get_ring().get_delegate().allocator().clone(), STANDARD_CONVOLUTION)
432 }
433}
434
435impl<Impl> GaloisFieldBase<Impl>
436 where Impl: RingStore,
437 Impl::Type: Field + FreeAlgebra + FiniteRing,
438 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
439{
440 #[stability::unstable(feature = "enable")]
452 pub fn galois_ring_with<S, A2, C2>(&self, new_base_ring: S, allocator: A2, convolution_algorithm: C2) -> AsLocalPIR<FreeAlgebraImpl<S, SparseMapVector<S>, A2, C2>>
453 where S: RingStore + Clone,
454 S::Type: ZnRing + CanHomFrom<<<<Impl::Type as RingExtension>::BaseRing as RingStore>::Type as ZnRing>::IntegerRingBase>,
455 C2: ConvolutionAlgorithm<S::Type>,
456 A2: Allocator + Clone
457 {
458 let (p, e) = is_prime_power(&BigIntRing::RING, &new_base_ring.size(&BigIntRing::RING).unwrap()).unwrap();
459 assert!(BigIntRing::RING.eq_el(&p, &self.base_ring().size(&BigIntRing::RING).unwrap()));
460 let mut modulus_vec = SparseMapVector::new(self.rank(), new_base_ring.clone());
461 let x_pow_deg = RingRef::new(self).pow(self.canonical_gen(), self.rank());
462 let x_pow_deg = self.wrt_canonical_basis(&x_pow_deg);
463 let hom = new_base_ring.can_hom(self.base_ring().integer_ring()).unwrap();
464 for i in 0..self.rank() {
465 if !self.base_ring().is_zero(&x_pow_deg.at(i)) {
466 *modulus_vec.at_mut(i) = hom.map(self.base_ring().smallest_lift(x_pow_deg.at(i)));
467 }
468 }
469 let result = FreeAlgebraImpl::new_with_convolution(
470 new_base_ring,
471 self.rank(),
472 modulus_vec,
473 "θ",
474 allocator,
475 convolution_algorithm
476 );
477 let hom = result.base_ring().can_hom(self.base_ring().integer_ring()).unwrap();
478 let max_ideal_gen = result.inclusion().map(hom.map_ref(self.base_ring().modulus()));
479 return AsLocalPIR::from(AsLocalPIRBase::promise_is_local_pir(result, max_ideal_gen, Some(e)));
480 }
481
482 #[stability::unstable(feature = "enable")]
483 pub fn unwrap_self(self) -> Impl {
484 self.base
485 }
486}
487
488impl<Impl> GaloisField<Impl>
489 where Impl: RingStore,
490 Impl::Type: Field + FreeAlgebra + FiniteRing,
491 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
492{
493 #[stability::unstable(feature = "enable")]
501 pub fn create(base: Impl) -> Self {
502 RingValue::from(GaloisFieldBase {
503 base: base
504 })
505 }
506}
507
508impl<Impl> Clone for GaloisFieldBase<Impl>
509 where Impl: RingStore + Clone,
510 Impl::Type: Field + FreeAlgebra + FiniteRing,
511 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
512{
513 fn clone(&self) -> Self {
514 Self {
515 base: self.base.clone()
516 }
517 }
518}
519
520impl<Impl> Copy for GaloisFieldBase<Impl>
521 where Impl: RingStore + Copy,
522 Impl::Type: Field + FreeAlgebra + FiniteRing,
523 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
524 El<Impl>: Copy
525{}
526
527impl<Impl> PartialEq for GaloisFieldBase<Impl>
528 where Impl: RingStore,
529 Impl::Type: Field + FreeAlgebra + FiniteRing,
530 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
531{
532 fn eq(&self, other: &Self) -> bool {
533 self.base.get_ring() == other.base.get_ring()
534 }
535}
536
537impl<Impl> DelegateRing for GaloisFieldBase<Impl>
538 where Impl: RingStore,
539 Impl::Type: Field + FreeAlgebra + FiniteRing,
540 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
541{
542 type Base = Impl::Type;
543 type Element = El<Impl>;
544
545 fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element { el }
546 fn delegate_mut<'a>(&self, el: &'a mut Self::Element) -> &'a mut <Self::Base as RingBase>::Element { el }
547 fn delegate_ref<'a>(&self, el: &'a Self::Element) -> &'a <Self::Base as RingBase>::Element { el }
548 fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element { el }
549
550 fn get_delegate(&self) -> &Self::Base {
551 self.base.get_ring()
552 }
553}
554
555impl<Impl> DelegateRingImplFiniteRing for GaloisFieldBase<Impl>
556 where Impl: RingStore,
557 Impl::Type: Field + FreeAlgebra + FiniteRing,
558 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
559{}
560
561impl<Impl> Domain for GaloisFieldBase<Impl>
562 where Impl: RingStore,
563 Impl::Type: Field + FreeAlgebra + FiniteRing,
564 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
565{}
566
567impl<Impl> Field for GaloisFieldBase<Impl>
568 where Impl: RingStore,
569 Impl::Type: Field + FreeAlgebra + FiniteRing,
570 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
571{}
572
573impl<Impl> PerfectField for GaloisFieldBase<Impl>
574 where Impl: RingStore,
575 Impl::Type: Field + FreeAlgebra + FiniteRing,
576 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
577{}
578
579impl<Impl> EuclideanRing for GaloisFieldBase<Impl>
595 where Impl: RingStore,
596 Impl::Type: Field + FreeAlgebra + FiniteRing,
597 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
598{
599 fn euclidean_div_rem(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element) {
600 assert!(!self.is_zero(rhs));
601 (self.base.checked_div(&lhs, rhs).unwrap(), self.zero())
602 }
603
604 fn euclidean_deg(&self, val: &Self::Element) -> Option<usize> {
605 if self.is_zero(val) {
606 Some(0)
607 } else {
608 Some(1)
609 }
610 }
611
612 fn euclidean_rem(&self, _: Self::Element, rhs: &Self::Element) -> Self::Element {
613 assert!(!self.is_zero(rhs));
614 self.zero()
615 }
616}
617
618impl<Impl> PrincipalIdealRing for GaloisFieldBase<Impl>
619 where Impl: RingStore,
620 Impl::Type: Field + FreeAlgebra + FiniteRing,
621 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
622{
623 fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
624 if self.is_zero(lhs) && self.is_zero(rhs) {
625 Some(self.one())
626 } else {
627 self.checked_left_div(lhs, rhs)
628 }
629 }
630
631 fn extended_ideal_gen(&self, lhs: &Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element, Self::Element) {
632 if self.is_zero(lhs) {
633 (self.zero(), self.one(), self.clone_el(rhs))
634 } else {
635 (self.one(), self.zero(), self.clone_el(lhs))
636 }
637 }
638}
639
640impl<Impl> Serialize for GaloisFieldBase<Impl>
641 where Impl: RingStore + Serialize,
642 Impl::Type: Field + FreeAlgebra + FiniteRing + SerializableElementRing,
643 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
644{
645 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
646 where S: Serializer
647 {
648 SerializableNewtypeStruct::new("GaloisField", &self.base).serialize(serializer)
649 }
650}
651
652impl<'de, Impl> Deserialize<'de> for GaloisFieldBase<Impl>
653 where Impl: RingStore + Deserialize<'de>,
654 Impl::Type: Field + FreeAlgebra + FiniteRing + SerializableElementRing,
655 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
656{
657 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
658 where D: Deserializer<'de>
659 {
660 DeserializeSeedNewtypeStruct::new("GaloisField", PhantomData::<Impl>).deserialize(deserializer).map(|res| GaloisField::create(res).into())
661 }
662}
663
664impl<Impl> KaratsubaHint for GaloisFieldBase<Impl>
665 where Impl: RingStore,
666 Impl::Type: Field + FreeAlgebra + FiniteRing,
667 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
668{
669 fn karatsuba_threshold(&self) -> usize {
670 self.get_delegate().karatsuba_threshold()
671 }
672}
673
674impl<Impl> ComputeInnerProduct for GaloisFieldBase<Impl>
675 where Impl: RingStore,
676 Impl::Type: Field + FreeAlgebra + FiniteRing,
677 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
678{
679 fn inner_product<I: Iterator<Item = (Self::Element, Self::Element)>>(&self, els: I) -> Self::Element {
680 self.rev_delegate(self.get_delegate().inner_product(els.map(|(a, b)| (self.delegate(a), self.delegate(b)))))
681 }
682
683 fn inner_product_ref<'a, I: Iterator<Item = (&'a Self::Element, &'a Self::Element)>>(&self, els: I) -> Self::Element
684 where Self::Element: 'a,
685 Self: 'a
686 {
687 self.rev_delegate(self.get_delegate().inner_product_ref(els.map(|(a, b)| (self.delegate_ref(a), self.delegate_ref(b)))))
688 }
689
690 fn inner_product_ref_fst<'a, I: Iterator<Item = (&'a Self::Element, Self::Element)>>(&self, els: I) -> Self::Element
691 where Self::Element: 'a,
692 Self: 'a
693 {
694 self.rev_delegate(self.get_delegate().inner_product_ref_fst(els.map(|(a, b)| (self.delegate_ref(a), self.delegate(b)))))
695 }
696}
697
698impl<Impl> StrassenHint for GaloisFieldBase<Impl>
699 where Impl: RingStore,
700 Impl::Type: Field + FreeAlgebra + FiniteRing,
701 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
702{
703 fn strassen_threshold(&self) -> usize {
704 self.get_delegate().strassen_threshold()
705 }
706}
707
708impl<Impl, S> CanHomFrom<S> for GaloisFieldBase<Impl>
712 where Impl: RingStore,
713 Impl::Type: Field + FreeAlgebra + FiniteRing + CanHomFrom<S>,
714 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
715 S: ?Sized + RingBase
716{
717 type Homomorphism = <Impl::Type as CanHomFrom<S>>::Homomorphism;
718
719 fn has_canonical_hom(&self, from: &S) -> Option<Self::Homomorphism> {
720 self.base.get_ring().has_canonical_hom(from)
721 }
722
723 fn map_in(&self, from: &S, el: <S as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
724 self.base.get_ring().map_in(from, el, hom)
725 }
726}
727
728impl<Impl, R, A, V, C> CanHomFrom<GaloisFieldBase<Impl>> for FreeAlgebraImplBase<R, V, A, C>
732 where Impl: RingStore,
733 Impl::Type: Field + FreeAlgebra + FiniteRing,
734 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
735 R: RingStore,
736 V: VectorView<El<R>>,
737 C: ConvolutionAlgorithm<R::Type>,
738 A: Allocator + Clone,
739 FreeAlgebraImplBase<R, V, A, C>: CanHomFrom<Impl::Type>
740{
741 type Homomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanHomFrom<Impl::Type>>::Homomorphism;
742
743 fn has_canonical_hom(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Homomorphism> {
744 self.has_canonical_hom(from.base.get_ring())
745 }
746
747 fn map_in(&self, from: &GaloisFieldBase<Impl>, el: <GaloisFieldBase<Impl> as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
748 self.map_in(from.base.get_ring(), el, hom)
749 }
750}
751
752impl<Impl, R, A, V, C> CanHomFrom<GaloisFieldBase<Impl>> for AsFieldBase<FreeAlgebraImpl<R, V, A, C>>
756 where Impl: RingStore,
757 Impl::Type: Field + FreeAlgebra + FiniteRing,
758 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
759 R: RingStore,
760 R::Type: LinSolveRing,
761 V: VectorView<El<R>>,
762 C: ConvolutionAlgorithm<R::Type>,
763 A: Allocator + Clone,
764 FreeAlgebraImplBase<R, V, A, C>: CanHomFrom<Impl::Type>
765{
766 type Homomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanHomFrom<Impl::Type>>::Homomorphism;
767
768 fn has_canonical_hom(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Homomorphism> {
769 self.get_delegate().has_canonical_hom(from.base.get_ring())
770 }
771
772 fn map_in(&self, from: &GaloisFieldBase<Impl>, el: <GaloisFieldBase<Impl> as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
773 self.rev_delegate(self.get_delegate().map_in(from.base.get_ring(), el, hom))
774 }
775}
776
777impl<Impl, S> CanIsoFromTo<S> for GaloisFieldBase<Impl>
781 where Impl: RingStore,
782 Impl::Type: Field + FreeAlgebra + FiniteRing + CanIsoFromTo<S>,
783 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
784 S: ?Sized + RingBase
785{
786 type Isomorphism = <Impl::Type as CanIsoFromTo<S>>::Isomorphism;
787
788 fn has_canonical_iso(&self, from: &S) -> Option<Self::Isomorphism> {
789 self.base.get_ring().has_canonical_iso(from)
790 }
791
792 fn map_out(&self, from: &S, el: Self::Element, iso: &Self::Isomorphism) -> <S as RingBase>::Element {
793 self.base.get_ring().map_out(from, el, iso)
794 }
795}
796
797impl<Impl, R, A, V, C> CanIsoFromTo<GaloisFieldBase<Impl>> for FreeAlgebraImplBase<R, V, A, C>
801 where Impl: RingStore,
802 Impl::Type: Field + FreeAlgebra + FiniteRing,
803 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
804 R: RingStore,
805 V: VectorView<El<R>>,
806 C: ConvolutionAlgorithm<R::Type>,
807 A: Allocator + Clone,
808 FreeAlgebraImplBase<R, V, A, C>: CanIsoFromTo<Impl::Type>
809{
810 type Isomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanIsoFromTo<Impl::Type>>::Isomorphism;
811
812 fn has_canonical_iso(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Isomorphism> {
813 self.has_canonical_iso(from.base.get_ring())
814 }
815
816 fn map_out(&self, from: &GaloisFieldBase<Impl>, el: Self::Element, iso: &Self::Isomorphism) -> <GaloisFieldBase<Impl> as RingBase>::Element {
817 self.map_out(from.base.get_ring(), el, iso)
818 }
819}
820
821impl<Impl, R, A, V, C> CanIsoFromTo<GaloisFieldBase<Impl>> for AsFieldBase<FreeAlgebraImpl<R, V, A, C>>
825 where Impl: RingStore,
826 Impl::Type: Field + FreeAlgebra + FiniteRing,
827 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
828 R: RingStore,
829 R::Type: LinSolveRing,
830 V: VectorView<El<R>>,
831 C: ConvolutionAlgorithm<R::Type>,
832 A: Allocator + Clone,
833 FreeAlgebraImplBase<R, V, A, C>: CanIsoFromTo<Impl::Type>
834{
835 type Isomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanIsoFromTo<Impl::Type>>::Isomorphism;
836
837 fn has_canonical_iso(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Isomorphism> {
838 self.get_delegate().has_canonical_iso(from.base.get_ring())
839 }
840
841 fn map_out(&self, from: &GaloisFieldBase<Impl>, el: Self::Element, iso: &Self::Isomorphism) -> <GaloisFieldBase<Impl> as RingBase>::Element {
842 self.get_delegate().map_out(from.base.get_ring(), self.delegate(el), iso)
843 }
844}
845
846#[cfg(test)]
847use test::Bencher;
848#[cfg(test)]
849use std::time::Instant;
850
851#[test]
852fn test_can_hom_from() {
853 #[allow(unused)]
854 fn assert_impl_CanHomFrom<From, To>()
855 where To: ?Sized + CanHomFrom<From>, From: ?Sized + RingBase
856 {}
857
858 #[allow(unused)]
859 fn FreeAlgebraImpl_wrap_unwrap_homs<R, V, A, C>()
860 where R: RingStore,
861 R::Type: SelfIso + LinSolveRing,
862 V: VectorView<El<R>>,
863 A: Allocator + Clone,
864 C: ConvolutionAlgorithm<R::Type>
865 {
866 assert_impl_CanHomFrom::<FreeAlgebraImplBase<R, V, A, C>, AsFieldBase<FreeAlgebraImpl<R, V, A, C>>>();
867 }
868
869 #[allow(unused)]
870 fn FreeAlgebraImpl_from_GaloisField<R, V, A, C>()
871 where R: RingStore,
872 R::Type: SelfIso + LinSolveRing + FiniteRing + Field + ZnRing,
873 V: VectorView<El<R>>,
874 A: Allocator + Clone,
875 C: ConvolutionAlgorithm<R::Type>
876 {
877 assert_impl_CanHomFrom::<GaloisFieldBase<AsField<FreeAlgebraImpl<R, V, A, C>>>, AsFieldBase<FreeAlgebraImpl<R, V, A, C>>>();
878 }
879
880 #[allow(unused)]
881 fn GaloisField_from_GaloisField<R, V, A, C>()
882 where R: RingStore,
883 R::Type: SelfIso + LinSolveRing + FiniteRing + Field + ZnRing,
884 V: VectorView<El<R>>,
885 A: Allocator + Clone,
886 C: ConvolutionAlgorithm<R::Type>
887 {
888 assert_impl_CanHomFrom::<GaloisFieldBase<AsField<FreeAlgebraImpl<R, V, A, C>>>, GaloisFieldBase<AsField<FreeAlgebraImpl<R, V, A, C>>>>();
889 }
890}
891
892#[test]
893fn test_galois_field() {
894 let field = GaloisField::new(3, 1);
895 assert_eq!(3, field.elements().count());
896 crate::ring::generic_tests::test_ring_axioms(&field, field.elements());
897 crate::ring::generic_tests::test_self_iso(&field, field.elements());
898 crate::field::generic_tests::test_field_axioms(&field, field.elements());
899
900 let field = GaloisField::new(3, 2);
901 assert_eq!(9, field.elements().count());
902 crate::ring::generic_tests::test_ring_axioms(&field, field.elements());
903 crate::ring::generic_tests::test_self_iso(&field, field.elements());
904 crate::field::generic_tests::test_field_axioms(&field, field.elements());
905}
906
907#[test]
908fn test_principal_ideal_ring_axioms() {
909 let field = GaloisField::new(3, 2);
910 crate::pid::generic_tests::test_principal_ideal_ring_axioms(&field, field.elements());
911 crate::pid::generic_tests::test_euclidean_ring_axioms(&field, field.elements());
912}
913
914#[test]
915fn test_galois_field_even() {
916 for degree in 1..=9 {
917 let field = GaloisField::new_with_convolution(Zn::new(2).as_field().ok().unwrap(), degree, Global, STANDARD_CONVOLUTION);
918 assert_eq!(degree, field.rank());
919 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
920 }
921}
922
923#[test]
924fn test_galois_field_odd() {
925 for degree in 1..=9 {
926 let field = GaloisField::new_with_convolution(Zn::new(3).as_field().ok().unwrap(), degree, Global, STANDARD_CONVOLUTION);
927 assert_eq!(degree, field.rank());
928 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
929 }
930
931 for degree in 1..=9 {
932 let field = GaloisField::new_with_convolution(Zn::new(5).as_field().ok().unwrap(), degree, Global, STANDARD_CONVOLUTION);
933 assert_eq!(degree, field.rank());
934 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
935 }
936}
937
938#[test]
939fn test_galois_field_no_trinomial() {
940 let field = GaloisField::new_with_convolution(Zn::new(2).as_field().ok().unwrap(), 24, Global, STANDARD_CONVOLUTION);
941 assert_eq!(24, field.rank());
942 let poly_ring = DensePolyRing::new(field.base_ring(), "X");
943 poly_ring.println(&field.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
944 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
945
946 let field = GaloisField::new_with_convolution(Zn::new(3).as_field().ok().unwrap(), 30, Global, STANDARD_CONVOLUTION);
947 assert_eq!(30, field.rank());
948 let poly_ring = DensePolyRing::new(field.base_ring(), "X");
949 poly_ring.println(&field.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
950 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
951
952 let field = GaloisField::new_with_convolution(Zn::new(17).as_field().ok().unwrap(), 32, Global, STANDARD_CONVOLUTION);
953 assert_eq!(32, field.rank());
954 let poly_ring = DensePolyRing::new(field.base_ring(), "X");
955 poly_ring.println(&field.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
956 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
957}
958
959#[bench]
960fn bench_create_galois_ring_2_14_96(bencher: &mut Bencher) {
961 bencher.iter(|| {
962 let field = GaloisField::new(2, 96);
963 let ring = field.get_ring().galois_ring(14);
964 assert_eq!(96, ring.rank());
965 });
966}
967
968#[test]
969#[ignore]
970fn test_galois_field_huge() {
971 let start = Instant::now();
972 let field = GaloisField::new(17, 2048);
973 _ = std::hint::black_box(field);
974 let end = Instant::now();
975 println!("Created GF(17^2048) in {} ms", (end - start).as_millis());
976}