he_ring/number_ring/
mod.rs1use feanor_math::algorithms::miller_rabin::is_prime;
2use feanor_math::divisibility::DivisibilityRing;
3use feanor_math::integer::{BigIntRing, IntegerRing, IntegerRingStore};
4use feanor_math::ordered::OrderedRingStore;
5use feanor_math::primitive_int::StaticRing;
6use feanor_math::ring::*;
7use feanor_math::rings::poly::PolyRing;
8use feanor_math::rings::zn::zn_64;
9use feanor_math::seq::*;
10
11use crate::cyclotomic::*;
12
13pub mod quotient;
14pub mod pow2_cyclotomic;
15pub mod odd_cyclotomic;
16pub mod hypercube;
17
18pub trait HENumberRing: Send + Sync + PartialEq + Clone {
38
39 type Decomposed: HENumberRingMod;
40
41 fn mod_p(&self, Fp: zn_64::Zn) -> Self::Decomposed;
42
43 fn mod_p_required_root_of_unity(&self) -> usize;
44
45 fn inf_to_can_norm_expansion_factor(&self) -> f64;
56
57 fn can_to_inf_norm_expansion_factor(&self) -> f64;
68
69 fn product_expansion_factor(&self) -> f64 {
76 self.inf_to_can_norm_expansion_factor().powi(2) * self.can_to_inf_norm_expansion_factor()
77 }
78
79 fn generating_poly<P>(&self, poly_ring: P) -> El<P>
80 where P: RingStore,
81 P::Type: PolyRing + DivisibilityRing,
82 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: IntegerRing;
83
84 fn rank(&self) -> usize;
85}
86
87pub trait HECyclotomicNumberRing: HENumberRing<Decomposed: HECyclotomicNumberRingMod> {
92
93 fn n(&self) -> usize;
94
95 fn galois_group(&self) -> CyclotomicGaloisGroup {
96 CyclotomicGaloisGroup::new(self.n() as u64)
97 }
98}
99
100pub trait HENumberRingMod: Send + Sync + PartialEq {
128
129 fn base_ring(&self) -> &zn_64::Zn;
130
131 fn rank(&self) -> usize;
132
133 fn small_basis_to_mult_basis<V>(&self, data: V)
134 where V: SwappableVectorViewMut<zn_64::ZnEl>;
135
136 fn mult_basis_to_small_basis<V>(&self, data: V)
137 where V: SwappableVectorViewMut<zn_64::ZnEl>;
138
139 fn coeff_basis_to_small_basis<V>(&self, data: V)
140 where V: SwappableVectorViewMut<zn_64::ZnEl>;
141
142 fn small_basis_to_coeff_basis<V>(&self, data: V)
143 where V: SwappableVectorViewMut<zn_64::ZnEl>;
144}
145
146pub trait HECyclotomicNumberRingMod: HENumberRingMod {
147
148 fn n(&self) -> usize;
149
150 fn galois_group(&self) -> CyclotomicGaloisGroup {
151 CyclotomicGaloisGroup::new(self.n() as u64)
152 }
153
154 fn permute_galois_action<V1, V2>(&self, src: V1, dst: V2, galois_element: CyclotomicGaloisGroupEl)
159 where V1: VectorView<zn_64::ZnEl>,
160 V2: SwappableVectorViewMut<zn_64::ZnEl>;
161}
162
163pub fn largest_prime_leq_congruent_to_one(leq_than: i64, congruent_to_one_mod: i64) -> Option<i64> {
168 assert!(leq_than > congruent_to_one_mod);
169 let mut current = leq_than - (leq_than - 1) % congruent_to_one_mod;
170 while !is_prime(&StaticRing::<i64>::RING, ¤t, 10) {
171 current -= congruent_to_one_mod;
172 if current <= 0 {
173 return None;
174 }
175 }
176 return Some(current);
177}
178
179pub fn sample_primes<F>(min_bits: usize, max_bits: usize, max_bits_each_modulus: usize, largest_prime_leq: F) -> Option<Vec<El<BigIntRing>>>
193 where F: FnMut(El<BigIntRing>) -> Option<El<BigIntRing>>
194{
195 extend_sampled_primes(&[], min_bits, max_bits, max_bits_each_modulus, largest_prime_leq)
196}
197
198pub fn extend_sampled_primes<F>(begin_with: &[El<BigIntRing>], min_bits: usize, max_bits: usize, max_bits_each_modulus: usize, mut largest_prime_leq: F) -> Option<Vec<El<BigIntRing>>>
212 where F: FnMut(El<BigIntRing>) -> Option<El<BigIntRing>>
213{
214 let ZZbig = BigIntRing::RING;
215 assert!(max_bits > min_bits);
216
217 let mut result = begin_with.iter().map(|p| ZZbig.clone_el(p)).collect::<Vec<_>>();
218 let mut current_bits = result.iter().map(|n| ZZbig.to_float_approx(n).log2()).sum::<f64>();
219 assert!((current_bits.floor() as usize) < max_bits);
220 let mut current_upper_bound = ZZbig.power_of_two(max_bits_each_modulus);
221
222 let min = |x, y| if ZZbig.is_gt(&x, &y) { y } else { x };
223
224 while current_bits < min_bits as f64 {
225
226 if min_bits as f64 - current_bits < max_bits_each_modulus as f64 {
227 current_upper_bound = min(current_upper_bound, ZZbig.power_of_two(f64::min(max_bits as f64 - current_bits, max_bits_each_modulus as f64).floor() as usize));
228 } else {
229 let required_number_of_primes = ((min_bits as f64 - current_bits) / max_bits_each_modulus as f64).ceil() as usize;
230 current_upper_bound = min(current_upper_bound, ZZbig.power_of_two(f64::min((max_bits as f64 - current_bits) / required_number_of_primes as f64, max_bits_each_modulus as f64).floor() as usize));
231 }
232
233 let mut prime = largest_prime_leq(ZZbig.clone_el(¤t_upper_bound))?;
234 current_upper_bound = ZZbig.sub_ref_fst(&prime, ZZbig.one());
235 while begin_with.iter().any(|p| ZZbig.eq_el(p, &prime)) {
236 prime = largest_prime_leq(ZZbig.clone_el(¤t_upper_bound))?;
237 current_upper_bound = ZZbig.sub_ref_fst(&prime, ZZbig.one());
238 }
239 let bits = ZZbig.to_float_approx(&prime).log2();
240 current_bits += bits;
241 result.push(ZZbig.clone_el(&prime));
242 }
243 debug_assert!(ZZbig.is_geq(&ZZbig.prod(result.iter().map(|n| ZZbig.clone_el(n))), &ZZbig.power_of_two(min_bits)));
244 debug_assert!(ZZbig.is_lt(&ZZbig.prod(result.iter().map(|n| ZZbig.clone_el(n))), &ZZbig.power_of_two(max_bits)));
245 return Some(result);
246}
247
248#[cfg(test)]
249use feanor_math::integer::int_cast;
250#[cfg(test)]
251use feanor_math::pid::EuclideanRingStore;
252
253#[test]
254fn test_sample_primes() {
255 let ZZi64 = StaticRing::<i64>::RING;
256 let ZZbig = BigIntRing::RING;
257 let result = sample_primes(60, 62, 58, |b| largest_prime_leq_congruent_to_one(int_cast(b, ZZi64, ZZbig), 422144).map(|x| int_cast(x, ZZbig, ZZi64))).unwrap();
258 assert_eq!(result.len(), 2);
259 let prod = ZZbig.prod(result.iter().map(|n| ZZbig.clone_el(n)));
260 assert!(ZZbig.abs_log2_floor(&prod).unwrap() >= 60);
261 assert!(ZZbig.abs_log2_ceil(&prod).unwrap() <= 62);
262 assert!(result.iter().all(|n| ZZbig.is_one(&ZZbig.euclidean_rem(ZZbig.clone_el(n), &int_cast(422144, ZZbig, StaticRing::<i64>::RING)))));
263
264 let ZZbig = BigIntRing::RING;
265 let result = sample_primes(135, 138, 58, |b| largest_prime_leq_congruent_to_one(int_cast(b, ZZi64, ZZbig), 422144).map(|x| int_cast(x, ZZbig, ZZi64))).unwrap();
266 assert_eq!(result.len(), 3);
267 let prod = ZZbig.prod(result.iter().map(|n| ZZbig.clone_el(n)));
268 assert!(ZZbig.abs_log2_floor(&prod).unwrap() >= 135);
269 assert!(ZZbig.abs_log2_ceil(&prod).unwrap() <= 138);
270 assert!(result.iter().all(|n| ZZbig.is_one(&ZZbig.euclidean_rem(ZZbig.clone_el(n), &int_cast(422144, ZZbig, StaticRing::<i64>::RING)))));
271
272 let ZZbig = BigIntRing::RING;
273 let result = sample_primes(115, 118, 58, |b| largest_prime_leq_congruent_to_one(int_cast(b, ZZi64, ZZbig), 422144).map(|x| int_cast(x, ZZbig, ZZi64))).unwrap();
274 assert_eq!(result.len(), 2);
275 let prod = ZZbig.prod(result.iter().map(|n| ZZbig.clone_el(n)));
276 assert!(ZZbig.abs_log2_floor(&prod).unwrap() >= 115);
277 assert!(ZZbig.abs_log2_ceil(&prod).unwrap() <= 118);
278 assert!(result.iter().all(|n| ZZbig.is_one(&ZZbig.euclidean_rem(ZZbig.clone_el(n), &int_cast(422144, ZZbig, StaticRing::<i64>::RING)))));
279}