use std::fmt::Debug;
use std::marker::PhantomData;
use feanor_math::algorithms::discrete_log::order;
use feanor_math::ring::*;
use feanor_math::delegate;
use feanor_math::rings::extension::*;
use feanor_math::rings::zn::zn_64::*;
use feanor_math::rings::zn::*;
use feanor_math::algorithms::int_factor::factor;
use feanor_math::serialization::*;
use feanor_math::wrapper::RingElementWrapper;
use feanor_math::divisibility::DivisibilityRingStore;
use serde::de::DeserializeSeed;
use serde::Deserialize;
use serde::Serialize;
use crate::euler_phi;
use crate::ZZi64;
#[derive(Clone, Copy)]
pub struct CyclotomicGaloisGroup {
ring: Zn,
order: usize
}
impl PartialEq for CyclotomicGaloisGroup {
fn eq(&self, other: &Self) -> bool {
self.ring.get_ring() == other.ring.get_ring()
}
}
impl Eq for CyclotomicGaloisGroup {}
impl CyclotomicGaloisGroup {
pub fn new(m: u64) -> Self {
Self {
ring: Zn::new(m),
order: euler_phi(&factor(ZZi64, m as i64)) as usize
}
}
pub fn identity(&self) -> CyclotomicGaloisGroupEl {
CyclotomicGaloisGroupEl { value: self.ring.one() }
}
pub fn mul(&self, lhs: CyclotomicGaloisGroupEl, rhs: CyclotomicGaloisGroupEl) -> CyclotomicGaloisGroupEl {
CyclotomicGaloisGroupEl { value: self.ring.mul(lhs.value, rhs.value) }
}
pub fn invert(&self, value: CyclotomicGaloisGroupEl) -> CyclotomicGaloisGroupEl {
CyclotomicGaloisGroupEl { value: self.ring.invert(&value.value).unwrap() }
}
pub fn representative(&self, value: CyclotomicGaloisGroupEl) -> usize {
self.ring.smallest_positive_lift(value.value) as usize
}
pub fn from_representative(&self, value: i64) -> CyclotomicGaloisGroupEl {
self.from_ring_el(self.ring.coerce(&ZZi64, value))
}
pub fn from_ring_el(&self, value: ZnEl) -> CyclotomicGaloisGroupEl {
assert!(self.ring.is_unit(&value));
CyclotomicGaloisGroupEl { value }
}
pub fn negate(&self, value: CyclotomicGaloisGroupEl) -> CyclotomicGaloisGroupEl {
CyclotomicGaloisGroupEl { value: self.ring.negate(value.value) }
}
pub fn prod<I>(&self, it: I) -> CyclotomicGaloisGroupEl
where I: IntoIterator<Item = CyclotomicGaloisGroupEl>
{
it.into_iter().fold(self.identity(), |a, b| self.mul(a, b))
}
pub fn pow(&self, base: CyclotomicGaloisGroupEl, power: i64) -> CyclotomicGaloisGroupEl {
if power >= 0 {
CyclotomicGaloisGroupEl { value: self.ring.pow(base.value, power as usize) }
} else {
self.invert(CyclotomicGaloisGroupEl { value: self.ring.pow(base.value, (-power) as usize) })
}
}
pub fn is_identity(&self, value: CyclotomicGaloisGroupEl) -> bool {
self.ring.is_one(&value.value)
}
pub fn eq_el(&self, lhs: CyclotomicGaloisGroupEl, rhs: CyclotomicGaloisGroupEl) -> bool {
self.ring.eq_el(&lhs.value, &rhs.value)
}
pub fn m(&self) -> usize {
*self.ring.modulus() as usize
}
pub fn to_ring_el(&self, value: CyclotomicGaloisGroupEl) -> ZnEl {
value.value
}
pub fn underlying_ring(&self) -> &Zn {
&self.ring
}
pub fn group_order(&self) -> usize {
self.order
}
pub fn element_order(&self, value: CyclotomicGaloisGroupEl) -> usize {
order(
&RingElementWrapper::new(&self.ring, value.value),
self.group_order() as i64,
|a, b| a * b,
RingElementWrapper::new(&self.ring, self.ring.one())
) as usize
}
}
impl Debug for CyclotomicGaloisGroup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(Z/{}Z)*", self.ring.modulus())
}
}
pub struct SerializableCyclotomicGaloisGroupEl<'a>(&'a CyclotomicGaloisGroup, CyclotomicGaloisGroupEl);
impl<'a> SerializableCyclotomicGaloisGroupEl<'a> {
pub fn new(galois_group: &'a CyclotomicGaloisGroup, el: CyclotomicGaloisGroupEl) -> Self {
Self(galois_group, el)
}
}
impl<'a> Serialize for SerializableCyclotomicGaloisGroupEl<'a> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
SerializableNewtype::new("CyclotomicGaloisGroupEl", &SerializeOwnedWithRing::new(self.1.value, &self.0.ring)).serialize(serializer)
}
}
#[derive(Copy, Clone)]
pub struct DeserializeSeedCyclotomicGaloisGroupEl<'a>(&'a CyclotomicGaloisGroup);
impl<'a> DeserializeSeedCyclotomicGaloisGroupEl<'a> {
pub fn new(galois_group: &'a CyclotomicGaloisGroup) -> Self {
Self(galois_group)
}
}
impl<'a, 'de> DeserializeSeed<'de> for DeserializeSeedCyclotomicGaloisGroupEl<'a> {
type Value = CyclotomicGaloisGroupEl;
fn deserialize<D: serde::Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
DeserializeSeedNewtype::new("CyclotomicGaloisGroupEl", DeserializeWithRing::new(&self.0.ring)).deserialize(deserializer).map(|g| CyclotomicGaloisGroupEl { value: g })
}
}
impl Serialize for CyclotomicGaloisGroup {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer
{
SerializableNewtype::new("CyclotomicGaloisGroup", self.ring.modulus()).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for CyclotomicGaloisGroup {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de>
{
DeserializeSeedNewtype::new("CyclotomicGaloisGroup", PhantomData::<i64>).deserialize(deserializer).map(|m| Self {
ring: Zn::new(m as u64),
order: euler_phi(&factor(ZZi64, m as i64)) as usize
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct CyclotomicGaloisGroupEl {
value: El<Zn>
}
pub trait CyclotomicRing: FreeAlgebra {
fn m(&self) -> usize;
fn galois_group(&self) -> CyclotomicGaloisGroup {
CyclotomicGaloisGroup::new(self.m() as u64)
}
fn apply_galois_action(&self, x: &Self::Element, g: CyclotomicGaloisGroupEl) -> Self::Element;
fn apply_galois_action_many(&self, x: &Self::Element, gs: &[CyclotomicGaloisGroupEl]) -> Vec<Self::Element> {
gs.iter().map(move |g| self.apply_galois_action(&x, *g)).collect()
}
}
pub trait CyclotomicRingStore: RingStore
where Self::Type: CyclotomicRing
{
delegate!{ CyclotomicRing, fn m(&self) -> usize }
delegate!{ CyclotomicRing, fn galois_group(&self) -> CyclotomicGaloisGroup }
delegate!{ CyclotomicRing, fn apply_galois_action(&self, el: &El<Self>, s: CyclotomicGaloisGroupEl) -> El<Self> }
delegate!{ CyclotomicRing, fn apply_galois_action_many(&self, el: &El<Self>, gs: &[CyclotomicGaloisGroupEl]) -> Vec<El<Self>> }
}
impl<R: RingStore> CyclotomicRingStore for R
where R::Type: CyclotomicRing
{}
#[cfg(any(test, feature = "generic_tests"))]
pub fn generic_test_cyclotomic_ring_axioms<R: CyclotomicRingStore>(ring: R)
where R::Type: CyclotomicRing
{
use feanor_math::assert_el_eq;
use feanor_math::primitive_int::*;
use feanor_math::algorithms::int_factor::factor;
use feanor_math::algorithms::cyclotomic::cyclotomic_polynomial;
use feanor_math::rings::poly::*;
use feanor_math::rings::poly::sparse_poly::SparsePolyRing;
use feanor_math::seq::*;
use feanor_math::homomorphism::Homomorphism;
let zeta = ring.canonical_gen();
let m = ring.m();
assert_el_eq!(&ring, &ring.one(), &ring.pow(ring.clone_el(&zeta), m as usize));
for (p, _) in factor(&StaticRing::<i64>::RING, m as i64) {
assert!(!ring.eq_el(&ring.one(), &ring.pow(ring.clone_el(&zeta), m as usize / p as usize)));
}
let poly_ring = SparsePolyRing::new(&StaticRing::<i64>::RING, "X");
let cyclo_poly = cyclotomic_polynomial(&poly_ring, m as usize);
let x = ring.pow(ring.clone_el(&zeta), ring.rank());
let x_vec = ring.wrt_canonical_basis(&x);
assert_eq!(ring.rank(), x_vec.len());
for i in 0..x_vec.len() {
assert_el_eq!(ring.base_ring(), &ring.base_ring().negate(ring.base_ring().int_hom().map(*poly_ring.coefficient_at(&cyclo_poly, i) as i32)), &x_vec.at(i));
}
assert_el_eq!(&ring, &x, &ring.from_canonical_basis((0..x_vec.len()).map(|i| x_vec.at(i))));
for i in (0..ring.rank()).step_by(5) {
for j in (1..ring.rank()).step_by(7) {
if i == j {
continue;
}
let element = ring.from_canonical_basis((0..ring.rank()).map(|k| if k == i { ring.base_ring().one() } else if k == j { ring.base_ring().int_hom().map(2) } else { ring.base_ring().zero() }));
let expected = ring.add(ring.pow(ring.clone_el(&zeta), i), ring.int_hom().mul_map(ring.pow(ring.clone_el(&zeta), j), 2));
assert_el_eq!(&ring, &expected, &element);
let element_vec = ring.wrt_canonical_basis(&expected);
for k in 0..ring.rank() {
if k == i {
assert_el_eq!(ring.base_ring(), &ring.base_ring().one(), &element_vec.at(k));
} else if k == j {
assert_el_eq!(ring.base_ring(), &ring.base_ring().int_hom().map(2), &element_vec.at(k));
} else {
assert_el_eq!(ring.base_ring(), &ring.base_ring().zero(), &element_vec.at(k));
}
}
}
}
}