use feanor_math::algorithms::discrete_log::Subgroup;
use feanor_math::algorithms::miller_rabin::is_prime;
use feanor_math::divisibility::DivisibilityRing;
use feanor_math::integer::{BigIntRing, IntegerRing, IntegerRingStore};
use feanor_math::ordered::OrderedRingStore;
use feanor_math::primitive_int::StaticRing;
use feanor_math::{delegate, ring::*};
use feanor_math::rings::extension::FreeAlgebra;
use feanor_math::rings::finite::FiniteRing;
use feanor_math::rings::poly::PolyRing;
use feanor_math::rings::zn::zn_64;
use feanor_math::seq::*;
use crate::number_ring::galois::*;
pub mod galois;
pub mod poly_remainder;
pub mod quotient_by_int;
pub mod quotient_by_ideal;
pub mod pow2_cyclotomic;
pub mod composite_cyclotomic;
pub mod general_cyclotomic;
pub mod hypercube;
pub trait NumberRingQuotient: FiniteRing + FreeAlgebra {
type NumberRing: AbstractNumberRing;
fn number_ring(&self) -> &Self::NumberRing;
fn acting_galois_group(&self) -> &Subgroup<CyclotomicGaloisGroup>;
fn apply_galois_action(&self, x: &Self::Element, g: &GaloisGroupEl) -> Self::Element;
fn apply_galois_action_many(&self, x: &Self::Element, gs: &[GaloisGroupEl]) -> Vec<Self::Element> {
gs.iter().map(move |g| self.apply_galois_action(&x, g)).collect()
}
}
pub trait NumberRingQuotientStore: RingStore
where Self::Type: NumberRingQuotient
{
delegate!{ NumberRingQuotient, fn number_ring(&self) -> &<Self::Type as NumberRingQuotient>::NumberRing }
delegate!{ NumberRingQuotient, fn acting_galois_group(&self) -> &Subgroup<CyclotomicGaloisGroup> }
delegate!{ NumberRingQuotient, fn apply_galois_action(&self, x: &El<Self>, g: &GaloisGroupEl) -> El<Self> }
delegate!{ NumberRingQuotient, fn apply_galois_action_many(&self, x: &El<Self>, gs: &[GaloisGroupEl]) -> Vec<El<Self>> }
}
impl<R: RingStore> NumberRingQuotientStore for R
where R::Type: NumberRingQuotient
{}
pub trait AbstractNumberRing: PartialEq + Clone {
type NumberRingQuotientBases: NumberRingQuotientBases;
fn bases_mod_p(&self, Fp: zn_64::Zn) -> Self::NumberRingQuotientBases;
fn mod_p_required_root_of_unity(&self) -> u64;
fn inf_to_can_norm_expansion_factor(&self) -> f64;
fn can_to_inf_norm_expansion_factor(&self) -> f64;
fn product_expansion_factor(&self) -> f64 {
self.inf_to_can_norm_expansion_factor().powi(2) * self.can_to_inf_norm_expansion_factor()
}
fn generating_poly<P>(&self, poly_ring: P) -> El<P>
where P: RingStore,
P::Type: PolyRing + DivisibilityRing,
<<P::Type as RingExtension>::BaseRing as RingStore>::Type: IntegerRing;
fn rank(&self) -> usize;
fn galois_group(&self) -> &CyclotomicGaloisGroup;
}
pub trait NumberRingQuotientBases: PartialEq {
fn base_ring(&self) -> &zn_64::Zn;
fn rank(&self) -> usize;
fn small_basis_to_mult_basis<V>(&self, data: V)
where V: SwappableVectorViewMut<zn_64::ZnEl>;
fn mult_basis_to_small_basis<V>(&self, data: V)
where V: SwappableVectorViewMut<zn_64::ZnEl>;
fn coeff_basis_to_small_basis<V>(&self, data: V)
where V: SwappableVectorViewMut<zn_64::ZnEl>;
fn small_basis_to_coeff_basis<V>(&self, data: V)
where V: SwappableVectorViewMut<zn_64::ZnEl>;
fn permute_galois_action<V1, V2>(&self, src: V1, dst: V2, g: &GaloisGroupEl)
where V1: VectorView<zn_64::ZnEl>,
V2: SwappableVectorViewMut<zn_64::ZnEl>;
fn galois_group(&self) -> &CyclotomicGaloisGroup;
}
pub fn largest_prime_leq_congruent_to_one(leq_than: i64, congruent_to_one_mod: i64) -> Option<i64> {
assert!(leq_than > congruent_to_one_mod);
let mut current = leq_than - (leq_than - 1) % congruent_to_one_mod;
while !is_prime(&StaticRing::<i64>::RING, ¤t, 10) {
current -= congruent_to_one_mod;
if current <= 0 {
return None;
}
}
return Some(current);
}
pub fn sample_primes<F>(min_bits: usize, max_bits: usize, max_bits_each_modulus: usize, largest_prime_leq: F) -> Option<Vec<El<BigIntRing>>>
where F: FnMut(El<BigIntRing>) -> Option<El<BigIntRing>>
{
extend_sampled_primes(&[], min_bits, max_bits, max_bits_each_modulus, largest_prime_leq)
}
pub 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>>>
where F: FnMut(El<BigIntRing>) -> Option<El<BigIntRing>>
{
let ZZbig = BigIntRing::RING;
assert!(max_bits > min_bits);
let mut result = begin_with.iter().map(|p| ZZbig.clone_el(p)).collect::<Vec<_>>();
let mut current_bits = result.iter().map(|i| ZZbig.to_float_approx(i).log2()).sum::<f64>();
assert!((current_bits.floor() as usize) < max_bits);
let mut current_upper_bound = ZZbig.power_of_two(max_bits_each_modulus);
let min = |x, y| if ZZbig.is_gt(&x, &y) { y } else { x };
while current_bits < min_bits as f64 {
if min_bits as f64 - current_bits < max_bits_each_modulus as f64 {
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));
} else {
let required_number_of_primes = ((min_bits as f64 - current_bits) / max_bits_each_modulus as f64).ceil() as usize;
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));
}
let mut prime = largest_prime_leq(ZZbig.clone_el(¤t_upper_bound))?;
current_upper_bound = ZZbig.sub_ref_fst(&prime, ZZbig.one());
while begin_with.iter().any(|p| ZZbig.eq_el(p, &prime)) {
prime = largest_prime_leq(ZZbig.clone_el(¤t_upper_bound))?;
current_upper_bound = ZZbig.sub_ref_fst(&prime, ZZbig.one());
}
let bits = ZZbig.to_float_approx(&prime).log2();
current_bits += bits;
result.push(ZZbig.clone_el(&prime));
}
debug_assert!(ZZbig.is_geq(&ZZbig.prod(result.iter().map(|i| ZZbig.clone_el(i))), &ZZbig.power_of_two(min_bits)));
debug_assert!(ZZbig.is_lt(&ZZbig.prod(result.iter().map(|i| ZZbig.clone_el(i))), &ZZbig.power_of_two(max_bits)));
return Some(result);
}
#[cfg(test)]
use feanor_math::integer::int_cast;
#[cfg(test)]
use feanor_math::pid::EuclideanRingStore;
#[test]
fn test_sample_primes() {
let ZZi64 = StaticRing::<i64>::RING;
let ZZbig = BigIntRing::RING;
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();
assert_eq!(result.len(), 2);
let prod = ZZbig.prod(result.iter().map(|i| ZZbig.clone_el(i)));
assert!(ZZbig.abs_log2_floor(&prod).unwrap() >= 60);
assert!(ZZbig.abs_log2_ceil(&prod).unwrap() <= 62);
assert!(result.iter().all(|i| ZZbig.is_one(&ZZbig.euclidean_rem(ZZbig.clone_el(i), &int_cast(422144, ZZbig, StaticRing::<i64>::RING)))));
let ZZbig = BigIntRing::RING;
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();
assert_eq!(result.len(), 3);
let prod = ZZbig.prod(result.iter().map(|i| ZZbig.clone_el(i)));
assert!(ZZbig.abs_log2_floor(&prod).unwrap() >= 135);
assert!(ZZbig.abs_log2_ceil(&prod).unwrap() <= 138);
assert!(result.iter().all(|i| ZZbig.is_one(&ZZbig.euclidean_rem(ZZbig.clone_el(i), &int_cast(422144, ZZbig, StaticRing::<i64>::RING)))));
let ZZbig = BigIntRing::RING;
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();
assert_eq!(result.len(), 2);
let prod = ZZbig.prod(result.iter().map(|i| ZZbig.clone_el(i)));
assert!(ZZbig.abs_log2_floor(&prod).unwrap() >= 115);
assert!(ZZbig.abs_log2_ceil(&prod).unwrap() <= 118);
assert!(result.iter().all(|i| ZZbig.is_one(&ZZbig.euclidean_rem(ZZbig.clone_el(i), &int_cast(422144, ZZbig, StaticRing::<i64>::RING)))));
}