use crate::num::basic::unsigneds::PrimitiveUnsigned;
use crate::num::exhaustive::primitive_int_increasing_range;
use crate::num::factorization::factor::{
MAX_FACTORS_IN_U8, MAX_FACTORS_IN_U16, MAX_FACTORS_IN_U32, MAX_FACTORS_IN_U64,
MAX_FACTORS_IN_USIZE,
};
use crate::num::factorization::traits::{Factor, IsPrime, PrimitiveRootPrime};
fn primitive_root_prime<T: PrimitiveUnsigned + IsPrime + Factor, const N: usize>(p: T) -> T
where
<T as Factor>::FACTORS: Clone + IntoIterator<Item = (T, u8)>,
{
assert!(p > T::ONE);
if p == T::TWO {
return T::ONE;
}
let mut exponents = [T::ZERO; N];
let pm1 = p - T::ONE;
for (e, (pf, _)) in exponents.iter_mut().zip((p - T::ONE).factor().into_iter()) {
*e = pm1 / pf;
}
let data = T::precompute_mod_pow_data(&p);
'outer: for a in primitive_int_increasing_range(T::TWO, p) {
for &exponent in &exponents {
if exponent == T::ZERO {
break;
}
if a.mod_pow_precomputed(exponent.wrapping_into(), p, &data) == T::ONE {
continue 'outer;
}
}
return a;
}
assert!(p.is_prime());
unreachable!()
}
macro_rules! impl_primitive_root_prime {
($t:ident, $n:ident) => {
impl PrimitiveRootPrime for $t {
type Output = $t;
#[inline]
fn primitive_root_prime(&self) -> $t {
primitive_root_prime::<$t, $n>(*self)
}
}
};
}
impl_primitive_root_prime!(u8, MAX_FACTORS_IN_U8);
impl_primitive_root_prime!(u16, MAX_FACTORS_IN_U16);
impl_primitive_root_prime!(u32, MAX_FACTORS_IN_U32);
impl_primitive_root_prime!(u64, MAX_FACTORS_IN_U64);
impl_primitive_root_prime!(usize, MAX_FACTORS_IN_USIZE);