1use crate::MAX_PROBABILISTIC_REPETITIONS;
2use crate::field::Field;
3use crate::integer::int_cast;
4use crate::homomorphism::Homomorphism;
5use crate::integer::BigIntRing;
6use crate::integer::IntegerRing;
7use crate::ring::*;
8use crate::primitive_int::*;
9use crate::rings::finite::*;
10use crate::divisibility::DivisibilityRingStore;
11use crate::integer::IntegerRingStore;
12use crate::ordered::OrderedRingStore;
13use crate::rings::zn::ZnRing;
14
15use super::int_factor::factor;
16
17#[stability::unstable(feature = "enable")]
18pub fn is_prim_root_of_unity_pow2<R: RingStore>(ring: R, el: &El<R>, log2_n: usize) -> bool {
19 if log2_n == 0 {
20 return ring.is_one(el);
21 }
22 ring.is_neg_one(&ring.pow(ring.clone_el(&el), 1 << (log2_n - 1)))
23}
24
25#[stability::unstable(feature = "enable")]
26pub fn is_root_of_unity<R: RingStore>(ring: R, el: &El<R>, n: usize) -> bool {
27 is_root_of_unity_gen(ring, el, &n.try_into().unwrap(), StaticRing::<i64>::RING)
28}
29
30#[stability::unstable(feature = "enable")]
31pub fn is_root_of_unity_gen<R: RingStore, I: RingStore>(ring: R, el: &El<R>, n: &El<I>, ZZ: I) -> bool
32 where I::Type: IntegerRing
33{
34 assert!(ZZ.is_pos(n));
35 ring.is_one(&ring.pow_gen(ring.clone_el(&el), n, ZZ))
36}
37
38#[stability::unstable(feature = "enable")]
39pub fn is_prim_root_of_unity<R: RingStore>(ring: R, el: &El<R>, n: usize) -> bool {
40 is_prim_root_of_unity_gen(ring, el, &n.try_into().unwrap(), StaticRing::<i64>::RING)
41}
42
43#[stability::unstable(feature = "enable")]
44pub fn is_prim_root_of_unity_gen<R: RingStore, I>(ring: R, el: &El<R>, n: &El<I>, ZZ: I) -> bool
45 where I: RingStore + Copy,
46 I::Type: IntegerRing
47{
48 if !is_root_of_unity_gen(&ring, el, n, ZZ) {
49 return false;
50 }
51 for (p, _) in factor(&ZZ, ZZ.clone_el(n)) {
52 if is_root_of_unity_gen(&ring, el, &ZZ.checked_div(n, &p).unwrap(), ZZ) {
53 return false;
54 }
55 }
56 return true;
57}
58
59#[stability::unstable(feature = "enable")]
60pub fn get_prim_root_of_unity_gen<R, I>(ring: R, n: &El<I>, ZZ: I, order: &El<I>) -> Option<El<R>>
61 where R: RingStore,
62 R::Type: FiniteRing,
63 I: RingStore + Copy,
64 I::Type: IntegerRing
65{
66 let power = ZZ.checked_div(order, n)?;
67
68 let mut rng = oorandom::Rand64::new(ZZ.default_hash(&ring.size(&ZZ).unwrap()) as u128);
69 let mut current = ring.pow_gen(ring.random_element(|| rng.rand_u64()), &power, ZZ);
70 for _ in 0..MAX_PROBABILISTIC_REPETITIONS {
71 if is_prim_root_of_unity_gen(&ring, ¤t, n, ZZ) {
72 return Some(current);
73 }
74 current = ring.pow_gen(ring.random_element(|| rng.rand_u64()), &power, ZZ);
75 }
76 unreachable!()
77}
78
79pub fn get_prim_root_of_unity<R>(ring: R, n: usize) -> Option<El<R>>
85 where R: RingStore,
86 R::Type: FiniteRing + Field
87{
88 let order = BigIntRing::RING.sub(ring.size(&BigIntRing::RING).unwrap(), BigIntRing::RING.one());
89 get_prim_root_of_unity_gen(ring, &int_cast(n.try_into().unwrap(), BigIntRing::RING, StaticRing::<i64>::RING), BigIntRing::RING, &order)
90}
91
92#[stability::unstable(feature = "enable")]
93pub fn get_prim_root_of_unity_zn_gen<R, I>(ring: R, ZZ: &I, n: &El<I>) -> Option<El<R>>
94 where R: RingStore,
95 R::Type: ZnRing,
96 I: RingStore,
97 I::Type: IntegerRing
98{
99 let order = factor(ZZ, ring.characteristic(ZZ).unwrap()).into_iter().map(|(p, e)| if ZZ.eq_el(&p, &ZZ.int_hom().map(2)) {
100 match e {
101 1 => ZZ.one(),
102 2 => p,
103 e => ZZ.pow(p, e - 2)
104 }
105 } else {
106 ZZ.mul(ZZ.sub_ref_fst(&p, ZZ.one()), ZZ.pow(p, e - 1))
107 }).fold(ZZ.one(), |current, next| if ZZ.is_lt(¤t, &next) { next } else { current });
108 get_prim_root_of_unity_gen(ring, n, ZZ, &order)
109}
110
111#[stability::unstable(feature = "enable")]
121pub fn get_prim_root_of_unity_zn<R>(ring: R, n: usize) -> Option<El<R>>
122 where R: RingStore,
123 R::Type: ZnRing
124{
125 get_prim_root_of_unity_zn_gen(ring, &BigIntRing::RING, &int_cast(n as i64, BigIntRing::RING, StaticRing::<i64>::RING))
126}
127
128pub fn get_prim_root_of_unity_pow2<R>(ring: R, log2_n: usize) -> Option<El<R>>
134 where R: RingStore,
135 R::Type: FiniteRing + Field
136{
137 let order = BigIntRing::RING.sub(ring.size(&BigIntRing::RING).unwrap(), BigIntRing::RING.one());
138 get_prim_root_of_unity_gen(ring, &BigIntRing::RING.power_of_two(log2_n), BigIntRing::RING, &order)
139}
140
141#[stability::unstable(feature = "enable")]
151pub fn get_prim_root_of_unity_pow2_zn<R, I>(ring: R, log2_n: usize) -> Option<El<R>>
152 where R: RingStore,
153 R::Type: ZnRing
154{
155 get_prim_root_of_unity_zn_gen(ring, &BigIntRing::RING, &BigIntRing::RING.power_of_two(log2_n))
156}
157
158#[cfg(test)]
159use crate::rings::zn::zn_static::{Zn, Fp};
160#[cfg(test)]
161use crate::algorithms::poly_factor::FactorPolyField;
162#[cfg(test)]
163use crate::algorithms::cyclotomic::cyclotomic_polynomial;
164#[cfg(test)]
165use crate::rings::poly::dense_poly::DensePolyRing;
166#[cfg(test)]
167use crate::rings::poly::PolyRingStore;
168#[cfg(test)]
169use crate::rings::extension::galois_field::GaloisField;
170
171#[test]
172fn test_is_prim_root_of_unity() {
173 let ring = Zn::<17>::RING;
174 assert!(is_prim_root_of_unity_pow2(ring, &ring.int_hom().map(2), 3));
175 assert!(!is_prim_root_of_unity_pow2(ring, &ring.int_hom().map(2), 4));
176 assert!(is_prim_root_of_unity_pow2(ring, &ring.int_hom().map(3), 4));
177
178 let ring = Zn::<101>::RING;
179 assert!(is_prim_root_of_unity(&ring, &ring.int_hom().map(36), 5));
180 assert!(is_prim_root_of_unity(&ring, &ring.int_hom().map(3), 100));
181 assert!(is_prim_root_of_unity(&ring, &ring.int_hom().map(5), 25));
182 assert!(!is_prim_root_of_unity(&ring, &ring.int_hom().map(5), 50));
183 assert!(is_prim_root_of_unity(&ring, &ring.int_hom().map(6), 10));
184 assert!(!is_prim_root_of_unity(&ring, &ring.int_hom().map(6), 50));
185
186 let ring = GaloisField::new(23, 2);
187 assert!(is_prim_root_of_unity(&ring, &ring.int_hom().map(-1), 2));
188 assert!(is_prim_root_of_unity(&ring, &ring.int_hom().map(2), 11));
189 let poly_ring = DensePolyRing::new(&ring, "X");
190 let (factorization, _) = <_ as FactorPolyField>::factor_poly(&poly_ring, &cyclotomic_polynomial(&poly_ring, 16));
191 for (mut factor, _) in factorization {
192 let normalization = poly_ring.base_ring().invert(poly_ring.lc(&factor).unwrap()).unwrap();
193 poly_ring.inclusion().mul_assign_map(&mut factor, normalization);
194 assert!(is_prim_root_of_unity(&ring, poly_ring.coefficient_at(&factor, 0), 16));
195 assert!(is_prim_root_of_unity_pow2(&ring, poly_ring.coefficient_at(&factor, 0), 4));
196 }
197}
198
199#[test]
200fn test_get_prim_root_of_unity() {
201 let ring = Fp::<17>::RING;
202 assert!(is_prim_root_of_unity_pow2(&ring, &get_prim_root_of_unity_pow2(&ring, 4).unwrap(), 4));
203 assert!(get_prim_root_of_unity_pow2(&ring, 5).is_none());
204
205 let ring = Fp::<101>::RING;
206 assert!(is_prim_root_of_unity_pow2(&ring, &get_prim_root_of_unity_pow2(&ring, 2).unwrap(), 2));
207 assert!(is_prim_root_of_unity(&ring, &get_prim_root_of_unity(&ring, 25).unwrap(), 25));
208 assert!(get_prim_root_of_unity_pow2(&ring, 3).is_none());
209 assert!(get_prim_root_of_unity(&ring, 125).is_none());
210
211 let ring = GaloisField::new(23, 2);
212 assert!(is_prim_root_of_unity_pow2(&ring, &get_prim_root_of_unity_pow2(&ring, 4).unwrap(), 4));
213 assert!(get_prim_root_of_unity_pow2(&ring, 5).is_none());
214 assert!(is_prim_root_of_unity(&ring, &get_prim_root_of_unity(&ring, 3).unwrap(), 3));
215
216 let ring = GaloisField::new(17, 16);
217 assert!(is_prim_root_of_unity_pow2(&ring, &get_prim_root_of_unity_pow2(&ring, 4).unwrap(), 4));
218}
219
220#[test]
221fn test_get_prim_root_of_unity_zn() {
222 let ring = Zn::<1>::RING;
223 assert!(get_prim_root_of_unity_zn(&ring, 2).is_none());
224
225 let ring = Fp::<2>::RING;
226 assert!(get_prim_root_of_unity_zn(&ring, 2).is_none());
227
228 let ring = Zn::<4>::RING;
229 assert!(is_prim_root_of_unity(&ring, &get_prim_root_of_unity_zn(&ring, 2).unwrap(), 2));
230 assert!(get_prim_root_of_unity_zn(&ring, 4).is_none());
231
232 let ring = Zn::<8>::RING;
233 assert!(is_prim_root_of_unity(&ring, &get_prim_root_of_unity_zn(&ring, 2).unwrap(), 2));
234 assert!(get_prim_root_of_unity_zn(&ring, 4).is_none());
235
236 let ring = Zn::<16>::RING;
237 assert!(is_prim_root_of_unity(&ring, &get_prim_root_of_unity_zn(&ring, 4).unwrap(), 4));
238 assert!(get_prim_root_of_unity_zn(&ring, 5).is_none());
239
240 let ring = Zn::<15>::RING;
241 assert!(is_prim_root_of_unity(&ring, &get_prim_root_of_unity_zn(&ring, 4).unwrap(), 4));
242 assert!(get_prim_root_of_unity_zn(&ring, 5).is_none());
243
244 let ring = Zn::<75>::RING;
245 assert!(is_prim_root_of_unity(&ring, &get_prim_root_of_unity_zn(&ring, 5).unwrap(), 5));
246 assert!(is_prim_root_of_unity(&ring, &get_prim_root_of_unity_zn(&ring, 4).unwrap(), 4));
247 assert!(get_prim_root_of_unity_zn(&ring, 3).is_none());
248 assert!(get_prim_root_of_unity_zn(&ring, 8).is_none());
249}