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).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 {
324 Self::new_with_convolution(Zn::new(p as u64).as_field().ok().unwrap(), degree, Global, STANDARD_CONVOLUTION)
325 }
326}
327
328impl<R, A, C> GaloisFieldOver<R, A, C>
329 where R: RingStore + Clone,
330 R::Type: ZnRing + Field + SelfIso + CanHomFrom<StaticRingBase<i64>>,
331 C: ConvolutionAlgorithm<R::Type>,
332 A: Allocator + Clone
333{
334 #[stability::unstable(feature = "enable")]
366 pub fn new_with_convolution(base_field: R, degree: usize, allocator: A, convolution_algorithm: C) -> Self {
367 assert!(degree >= 1);
368 let poly_ring = DensePolyRing::new(&base_field, "X");
369 let mut rng = oorandom::Rand64::new(poly_ring.base_ring().integer_ring().default_hash(poly_ring.base_ring().modulus()) as u128);
370 let modulus = find_small_irreducible_poly(&poly_ring, degree, &mut rng);
371 let mut modulus_vec = SparseMapVector::new(degree, base_field.clone());
372 for (c, i) in poly_ring.terms(&modulus) {
373 if i != degree {
374 *modulus_vec.at_mut(i) = base_field.negate(base_field.clone_el(c));
375 }
376 }
377 return RingValue::from(GaloisFieldBase {
378 base: AsField::from(AsFieldBase::promise_is_perfect_field(FreeAlgebraImpl::new_with_convolution(base_field, degree, modulus_vec, "θ", allocator, convolution_algorithm)))
379 });
380 }
381
382 fn new_internal(base_ring: R, degree: usize, allocator: A, convolution_algorithm: C) -> Self
388 where R: Copy
389 {
390 assert!(degree >= 1);
391 let poly_ring = DensePolyRing::new(base_ring.clone(), "X");
392 let mut rng = oorandom::Rand64::new(poly_ring.base_ring().integer_ring().default_hash(poly_ring.base_ring().modulus()) as u128);
393 let modulus = find_small_irreducible_poly(&poly_ring, degree, &mut rng);
394 let mut modulus_vec = SparseMapVector::new(degree, base_ring.clone());
395 for (c, i) in poly_ring.terms(&modulus) {
396 if i != degree {
397 *modulus_vec.at_mut(i) = base_ring.negate(base_ring.clone_el(c));
398 }
399 }
400 return RingValue::from(GaloisFieldBase {
401 base: AsField::from(AsFieldBase::promise_is_perfect_field(FreeAlgebraImpl::new_with_convolution(base_ring, degree, modulus_vec, "θ", allocator, convolution_algorithm)))
402 });
403 }
404}
405
406impl<A> GaloisFieldBase<AsField<FreeAlgebraImpl<AsField<Zn>, SparseMapVector<AsField<Zn>>, A, KaratsubaAlgorithm>>>
407 where A: Allocator + Clone
408{
409 #[stability::unstable(feature = "enable")]
421 pub fn galois_ring(&self, e: usize) -> AsLocalPIR<FreeAlgebraImpl<Zn, SparseMapVector<Zn>, A, KaratsubaAlgorithm>> {
422 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)
423 }
424}
425
426impl<Impl> GaloisFieldBase<Impl>
427 where Impl: RingStore,
428 Impl::Type: Field + FreeAlgebra + FiniteRing,
429 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
430{
431 #[stability::unstable(feature = "enable")]
443 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>>
444 where S: RingStore + Clone,
445 S::Type: ZnRing + CanHomFrom<<<<Impl::Type as RingExtension>::BaseRing as RingStore>::Type as ZnRing>::IntegerRingBase>,
446 C2: ConvolutionAlgorithm<S::Type>,
447 A2: Allocator + Clone
448 {
449 let (p, e) = is_prime_power(&BigIntRing::RING, &new_base_ring.size(&BigIntRing::RING).unwrap()).unwrap();
450 assert!(BigIntRing::RING.eq_el(&p, &self.base_ring().size(&BigIntRing::RING).unwrap()));
451 let mut modulus_vec = SparseMapVector::new(self.rank(), new_base_ring.clone());
452 let x_pow_deg = RingRef::new(self).pow(self.canonical_gen(), self.rank());
453 let x_pow_deg = self.wrt_canonical_basis(&x_pow_deg);
454 let hom = new_base_ring.can_hom(self.base_ring().integer_ring()).unwrap();
455 for i in 0..self.rank() {
456 if !self.base_ring().is_zero(&x_pow_deg.at(i)) {
457 *modulus_vec.at_mut(i) = hom.map(self.base_ring().smallest_lift(x_pow_deg.at(i)));
458 }
459 }
460 let result = FreeAlgebraImpl::new_with_convolution(
461 new_base_ring,
462 self.rank(),
463 modulus_vec,
464 "θ",
465 allocator,
466 convolution_algorithm
467 );
468 let hom = result.base_ring().can_hom(self.base_ring().integer_ring()).unwrap();
469 let max_ideal_gen = result.inclusion().map(hom.map_ref(self.base_ring().modulus()));
470 return AsLocalPIR::from(AsLocalPIRBase::promise_is_local_pir(result, max_ideal_gen, Some(e)));
471 }
472
473 #[stability::unstable(feature = "enable")]
474 pub fn unwrap_self(self) -> Impl {
475 self.base
476 }
477}
478
479impl<Impl> GaloisField<Impl>
480 where Impl: RingStore,
481 Impl::Type: Field + FreeAlgebra + FiniteRing,
482 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
483{
484 #[stability::unstable(feature = "enable")]
492 pub fn create(base: Impl) -> Self {
493 RingValue::from(GaloisFieldBase {
494 base: base
495 })
496 }
497}
498
499impl<Impl> Clone for GaloisFieldBase<Impl>
500 where Impl: RingStore + Clone,
501 Impl::Type: Field + FreeAlgebra + FiniteRing,
502 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
503{
504 fn clone(&self) -> Self {
505 Self {
506 base: self.base.clone()
507 }
508 }
509}
510
511impl<Impl> Copy for GaloisFieldBase<Impl>
512 where Impl: RingStore + Copy,
513 Impl::Type: Field + FreeAlgebra + FiniteRing,
514 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
515 El<Impl>: Copy
516{}
517
518impl<Impl> PartialEq for GaloisFieldBase<Impl>
519 where Impl: RingStore,
520 Impl::Type: Field + FreeAlgebra + FiniteRing,
521 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
522{
523 fn eq(&self, other: &Self) -> bool {
524 self.base.get_ring() == other.base.get_ring()
525 }
526}
527
528impl<Impl> DelegateRing for GaloisFieldBase<Impl>
529 where Impl: RingStore,
530 Impl::Type: Field + FreeAlgebra + FiniteRing,
531 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
532{
533 type Base = Impl::Type;
534 type Element = El<Impl>;
535
536 fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element { el }
537 fn delegate_mut<'a>(&self, el: &'a mut Self::Element) -> &'a mut <Self::Base as RingBase>::Element { el }
538 fn delegate_ref<'a>(&self, el: &'a Self::Element) -> &'a <Self::Base as RingBase>::Element { el }
539 fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element { el }
540
541 fn get_delegate(&self) -> &Self::Base {
542 self.base.get_ring()
543 }
544}
545
546impl<Impl> DelegateRingImplFiniteRing for GaloisFieldBase<Impl>
547 where Impl: RingStore,
548 Impl::Type: Field + FreeAlgebra + FiniteRing,
549 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
550{}
551
552impl<Impl> Domain for GaloisFieldBase<Impl>
553 where Impl: RingStore,
554 Impl::Type: Field + FreeAlgebra + FiniteRing,
555 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
556{}
557
558impl<Impl> Field for GaloisFieldBase<Impl>
559 where Impl: RingStore,
560 Impl::Type: Field + FreeAlgebra + FiniteRing,
561 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
562{}
563
564impl<Impl> PerfectField for GaloisFieldBase<Impl>
565 where Impl: RingStore,
566 Impl::Type: Field + FreeAlgebra + FiniteRing,
567 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
568{}
569
570impl<Impl> EuclideanRing for GaloisFieldBase<Impl>
586 where Impl: RingStore,
587 Impl::Type: Field + FreeAlgebra + FiniteRing,
588 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
589{
590 fn euclidean_div_rem(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element) {
591 assert!(!self.is_zero(rhs));
592 (self.base.checked_div(&lhs, rhs).unwrap(), self.zero())
593 }
594
595 fn euclidean_deg(&self, val: &Self::Element) -> Option<usize> {
596 if self.is_zero(val) {
597 Some(0)
598 } else {
599 Some(1)
600 }
601 }
602
603 fn euclidean_rem(&self, _: Self::Element, rhs: &Self::Element) -> Self::Element {
604 assert!(!self.is_zero(rhs));
605 self.zero()
606 }
607}
608
609impl<Impl> PrincipalIdealRing for GaloisFieldBase<Impl>
610 where Impl: RingStore,
611 Impl::Type: Field + FreeAlgebra + FiniteRing,
612 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
613{
614 fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
615 if self.is_zero(lhs) && self.is_zero(rhs) {
616 Some(self.one())
617 } else {
618 self.checked_left_div(lhs, rhs)
619 }
620 }
621
622 fn extended_ideal_gen(&self, lhs: &Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element, Self::Element) {
623 if self.is_zero(lhs) {
624 (self.zero(), self.one(), self.clone_el(rhs))
625 } else {
626 (self.one(), self.zero(), self.clone_el(lhs))
627 }
628 }
629}
630
631impl<Impl> Serialize for GaloisFieldBase<Impl>
632 where Impl: RingStore + Serialize,
633 Impl::Type: Field + FreeAlgebra + FiniteRing + SerializableElementRing,
634 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
635{
636 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
637 where S: Serializer
638 {
639 SerializableNewtypeStruct::new("GaloisField", &self.base).serialize(serializer)
640 }
641}
642
643impl<'de, Impl> Deserialize<'de> for GaloisFieldBase<Impl>
644 where Impl: RingStore + Deserialize<'de>,
645 Impl::Type: Field + FreeAlgebra + FiniteRing + SerializableElementRing,
646 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
647{
648 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
649 where D: Deserializer<'de>
650 {
651 DeserializeSeedNewtypeStruct::new("GaloisField", PhantomData::<Impl>).deserialize(deserializer).map(|res| GaloisField::create(res).into())
652 }
653}
654
655impl<Impl> KaratsubaHint for GaloisFieldBase<Impl>
656 where Impl: RingStore,
657 Impl::Type: Field + FreeAlgebra + FiniteRing,
658 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
659{
660 fn karatsuba_threshold(&self) -> usize {
661 self.get_delegate().karatsuba_threshold()
662 }
663}
664
665impl<Impl> ComputeInnerProduct for GaloisFieldBase<Impl>
666 where Impl: RingStore,
667 Impl::Type: Field + FreeAlgebra + FiniteRing,
668 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
669{
670 fn inner_product<I: Iterator<Item = (Self::Element, Self::Element)>>(&self, els: I) -> Self::Element {
671 self.rev_delegate(self.get_delegate().inner_product(els.map(|(a, b)| (self.delegate(a), self.delegate(b)))))
672 }
673
674 fn inner_product_ref<'a, I: Iterator<Item = (&'a Self::Element, &'a Self::Element)>>(&self, els: I) -> Self::Element
675 where Self::Element: 'a,
676 Self: 'a
677 {
678 self.rev_delegate(self.get_delegate().inner_product_ref(els.map(|(a, b)| (self.delegate_ref(a), self.delegate_ref(b)))))
679 }
680
681 fn inner_product_ref_fst<'a, I: Iterator<Item = (&'a Self::Element, Self::Element)>>(&self, els: I) -> Self::Element
682 where Self::Element: 'a,
683 Self: 'a
684 {
685 self.rev_delegate(self.get_delegate().inner_product_ref_fst(els.map(|(a, b)| (self.delegate_ref(a), self.delegate(b)))))
686 }
687}
688
689impl<Impl> StrassenHint for GaloisFieldBase<Impl>
690 where Impl: RingStore,
691 Impl::Type: Field + FreeAlgebra + FiniteRing,
692 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field
693{
694 fn strassen_threshold(&self) -> usize {
695 self.get_delegate().strassen_threshold()
696 }
697}
698
699impl<Impl, S> CanHomFrom<S> for GaloisFieldBase<Impl>
703 where Impl: RingStore,
704 Impl::Type: Field + FreeAlgebra + FiniteRing + CanHomFrom<S>,
705 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
706 S: ?Sized + RingBase
707{
708 type Homomorphism = <Impl::Type as CanHomFrom<S>>::Homomorphism;
709
710 fn has_canonical_hom(&self, from: &S) -> Option<Self::Homomorphism> {
711 self.base.get_ring().has_canonical_hom(from)
712 }
713
714 fn map_in(&self, from: &S, el: <S as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
715 self.base.get_ring().map_in(from, el, hom)
716 }
717}
718
719impl<Impl, R, A, V, C> CanHomFrom<GaloisFieldBase<Impl>> for FreeAlgebraImplBase<R, V, A, C>
723 where Impl: RingStore,
724 Impl::Type: Field + FreeAlgebra + FiniteRing,
725 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
726 R: RingStore,
727 V: VectorView<El<R>>,
728 C: ConvolutionAlgorithm<R::Type>,
729 A: Allocator + Clone,
730 FreeAlgebraImplBase<R, V, A, C>: CanHomFrom<Impl::Type>
731{
732 type Homomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanHomFrom<Impl::Type>>::Homomorphism;
733
734 fn has_canonical_hom(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Homomorphism> {
735 self.has_canonical_hom(from.base.get_ring())
736 }
737
738 fn map_in(&self, from: &GaloisFieldBase<Impl>, el: <GaloisFieldBase<Impl> as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
739 self.map_in(from.base.get_ring(), el, hom)
740 }
741}
742
743impl<Impl, R, A, V, C> CanHomFrom<GaloisFieldBase<Impl>> for AsFieldBase<FreeAlgebraImpl<R, V, A, C>>
747 where Impl: RingStore,
748 Impl::Type: Field + FreeAlgebra + FiniteRing,
749 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
750 R: RingStore,
751 R::Type: LinSolveRing,
752 V: VectorView<El<R>>,
753 C: ConvolutionAlgorithm<R::Type>,
754 A: Allocator + Clone,
755 FreeAlgebraImplBase<R, V, A, C>: CanHomFrom<Impl::Type>
756{
757 type Homomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanHomFrom<Impl::Type>>::Homomorphism;
758
759 fn has_canonical_hom(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Homomorphism> {
760 self.get_delegate().has_canonical_hom(from.base.get_ring())
761 }
762
763 fn map_in(&self, from: &GaloisFieldBase<Impl>, el: <GaloisFieldBase<Impl> as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
764 self.rev_delegate(self.get_delegate().map_in(from.base.get_ring(), el, hom))
765 }
766}
767
768impl<Impl, S> CanIsoFromTo<S> for GaloisFieldBase<Impl>
772 where Impl: RingStore,
773 Impl::Type: Field + FreeAlgebra + FiniteRing + CanIsoFromTo<S>,
774 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
775 S: ?Sized + RingBase
776{
777 type Isomorphism = <Impl::Type as CanIsoFromTo<S>>::Isomorphism;
778
779 fn has_canonical_iso(&self, from: &S) -> Option<Self::Isomorphism> {
780 self.base.get_ring().has_canonical_iso(from)
781 }
782
783 fn map_out(&self, from: &S, el: Self::Element, iso: &Self::Isomorphism) -> <S as RingBase>::Element {
784 self.base.get_ring().map_out(from, el, iso)
785 }
786}
787
788impl<Impl, R, A, V, C> CanIsoFromTo<GaloisFieldBase<Impl>> for FreeAlgebraImplBase<R, V, A, C>
792 where Impl: RingStore,
793 Impl::Type: Field + FreeAlgebra + FiniteRing,
794 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
795 R: RingStore,
796 V: VectorView<El<R>>,
797 C: ConvolutionAlgorithm<R::Type>,
798 A: Allocator + Clone,
799 FreeAlgebraImplBase<R, V, A, C>: CanIsoFromTo<Impl::Type>
800{
801 type Isomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanIsoFromTo<Impl::Type>>::Isomorphism;
802
803 fn has_canonical_iso(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Isomorphism> {
804 self.has_canonical_iso(from.base.get_ring())
805 }
806
807 fn map_out(&self, from: &GaloisFieldBase<Impl>, el: Self::Element, iso: &Self::Isomorphism) -> <GaloisFieldBase<Impl> as RingBase>::Element {
808 self.map_out(from.base.get_ring(), el, iso)
809 }
810}
811
812impl<Impl, R, A, V, C> CanIsoFromTo<GaloisFieldBase<Impl>> for AsFieldBase<FreeAlgebraImpl<R, V, A, C>>
816 where Impl: RingStore,
817 Impl::Type: Field + FreeAlgebra + FiniteRing,
818 <<Impl::Type as RingExtension>::BaseRing as RingStore>::Type: ZnRing + Field,
819 R: RingStore,
820 R::Type: LinSolveRing,
821 V: VectorView<El<R>>,
822 C: ConvolutionAlgorithm<R::Type>,
823 A: Allocator + Clone,
824 FreeAlgebraImplBase<R, V, A, C>: CanIsoFromTo<Impl::Type>
825{
826 type Isomorphism = <FreeAlgebraImplBase<R, V, A, C> as CanIsoFromTo<Impl::Type>>::Isomorphism;
827
828 fn has_canonical_iso(&self, from: &GaloisFieldBase<Impl>) -> Option<Self::Isomorphism> {
829 self.get_delegate().has_canonical_iso(from.base.get_ring())
830 }
831
832 fn map_out(&self, from: &GaloisFieldBase<Impl>, el: Self::Element, iso: &Self::Isomorphism) -> <GaloisFieldBase<Impl> as RingBase>::Element {
833 self.get_delegate().map_out(from.base.get_ring(), self.delegate(el), iso)
834 }
835}
836
837#[cfg(test)]
838use test::Bencher;
839#[cfg(test)]
840use std::time::Instant;
841
842#[test]
843fn test_can_hom_from() {
844 #[allow(unused)]
845 fn assert_impl_CanHomFrom<From, To>()
846 where To: ?Sized + CanHomFrom<From>, From: ?Sized + RingBase
847 {}
848
849 #[allow(unused)]
850 fn FreeAlgebraImpl_wrap_unwrap_homs<R, V, A, C>()
851 where R: RingStore,
852 R::Type: SelfIso + LinSolveRing,
853 V: VectorView<El<R>>,
854 A: Allocator + Clone,
855 C: ConvolutionAlgorithm<R::Type>
856 {
857 assert_impl_CanHomFrom::<FreeAlgebraImplBase<R, V, A, C>, AsFieldBase<FreeAlgebraImpl<R, V, A, C>>>();
858 }
859
860 #[allow(unused)]
861 fn FreeAlgebraImpl_from_GaloisField<R, V, A, C>()
862 where R: RingStore,
863 R::Type: SelfIso + LinSolveRing + FiniteRing + Field + ZnRing,
864 V: VectorView<El<R>>,
865 A: Allocator + Clone,
866 C: ConvolutionAlgorithm<R::Type>
867 {
868 assert_impl_CanHomFrom::<GaloisFieldBase<AsField<FreeAlgebraImpl<R, V, A, C>>>, AsFieldBase<FreeAlgebraImpl<R, V, A, C>>>();
869 }
870
871 #[allow(unused)]
872 fn GaloisField_from_GaloisField<R, V, A, C>()
873 where R: RingStore,
874 R::Type: SelfIso + LinSolveRing + FiniteRing + Field + ZnRing,
875 V: VectorView<El<R>>,
876 A: Allocator + Clone,
877 C: ConvolutionAlgorithm<R::Type>
878 {
879 assert_impl_CanHomFrom::<GaloisFieldBase<AsField<FreeAlgebraImpl<R, V, A, C>>>, GaloisFieldBase<AsField<FreeAlgebraImpl<R, V, A, C>>>>();
880 }
881}
882
883#[test]
884fn test_galois_field() {
885 let field = GaloisField::new(3, 1);
886 assert_eq!(3, field.elements().count());
887 crate::ring::generic_tests::test_ring_axioms(&field, field.elements());
888 crate::ring::generic_tests::test_self_iso(&field, field.elements());
889 crate::field::generic_tests::test_field_axioms(&field, field.elements());
890
891 let field = GaloisField::new(3, 2);
892 assert_eq!(9, field.elements().count());
893 crate::ring::generic_tests::test_ring_axioms(&field, field.elements());
894 crate::ring::generic_tests::test_self_iso(&field, field.elements());
895 crate::field::generic_tests::test_field_axioms(&field, field.elements());
896}
897
898#[test]
899fn test_principal_ideal_ring_axioms() {
900 let field = GaloisField::new(3, 2);
901 crate::pid::generic_tests::test_principal_ideal_ring_axioms(&field, field.elements());
902 crate::pid::generic_tests::test_euclidean_ring_axioms(&field, field.elements());
903}
904
905#[test]
906fn test_galois_field_even() {
907 for degree in 1..=9 {
908 let field = GaloisField::new_with_convolution(Zn::new(2).as_field().ok().unwrap(), degree, Global, STANDARD_CONVOLUTION);
909 assert_eq!(degree, field.rank());
910 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
911 }
912}
913
914#[test]
915fn test_galois_field_odd() {
916 for degree in 1..=9 {
917 let field = GaloisField::new_with_convolution(Zn::new(3).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 for degree in 1..=9 {
923 let field = GaloisField::new_with_convolution(Zn::new(5).as_field().ok().unwrap(), degree, Global, STANDARD_CONVOLUTION);
924 assert_eq!(degree, field.rank());
925 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
926 }
927}
928
929#[test]
930fn test_galois_field_no_trinomial() {
931 let field = GaloisField::new_with_convolution(Zn::new(2).as_field().ok().unwrap(), 24, Global, STANDARD_CONVOLUTION);
932 assert_eq!(24, field.rank());
933 let poly_ring = DensePolyRing::new(field.base_ring(), "X");
934 poly_ring.println(&field.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
935 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
936
937 let field = GaloisField::new_with_convolution(Zn::new(3).as_field().ok().unwrap(), 30, Global, STANDARD_CONVOLUTION);
938 assert_eq!(30, field.rank());
939 let poly_ring = DensePolyRing::new(field.base_ring(), "X");
940 poly_ring.println(&field.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
941 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
942
943 let field = GaloisField::new_with_convolution(Zn::new(17).as_field().ok().unwrap(), 32, Global, STANDARD_CONVOLUTION);
944 assert_eq!(32, field.rank());
945 let poly_ring = DensePolyRing::new(field.base_ring(), "X");
946 poly_ring.println(&field.generating_poly(&poly_ring, &poly_ring.base_ring().identity()));
947 assert!(field.into().unwrap_self().into().unwrap_self().as_field().is_ok());
948}
949
950#[bench]
951fn bench_create_galois_ring_2_14_96(bencher: &mut Bencher) {
952 bencher.iter(|| {
953 let field = GaloisField::new(2, 96);
954 let ring = field.get_ring().galois_ring(14);
955 assert_eq!(96, ring.rank());
956 });
957}
958
959#[test]
960#[ignore]
961fn test_galois_field_huge() {
962 let start = Instant::now();
963 let field = GaloisField::new(17, 2048);
964 _ = std::hint::black_box(field);
965 let end = Instant::now();
966 println!("Created GF(17^2048) in {} ms", (end - start).as_millis());
967}