malachite_base/num/factorization/
primitive_root_prime.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5//      Copyright © 2013 Mike Hansen
6//
7//      Copyright © 2024 Vincent Neiger
8//
9// This file is part of Malachite.
10//
11// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
12// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
13// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
14
15use crate::num::basic::unsigneds::PrimitiveUnsigned;
16use crate::num::exhaustive::primitive_int_increasing_range;
17use crate::num::factorization::factor::{
18    MAX_FACTORS_IN_U8, MAX_FACTORS_IN_U16, MAX_FACTORS_IN_U32, MAX_FACTORS_IN_U64,
19    MAX_FACTORS_IN_USIZE,
20};
21use crate::num::factorization::traits::{Factor, IsPrime, PrimitiveRootPrime};
22
23// This is n_primitive_root_prime from ulong_extras/primitive_root_prime.c, FLINT 3.3.0-dev.
24pub fn primitive_root_prime<T: PrimitiveUnsigned + IsPrime + Factor, const N: usize>(p: T) -> T
25where
26    <T as Factor>::FACTORS: Clone + IntoIterator<Item = (T, u8)>,
27{
28    assert!(p > T::ONE);
29    if p == T::TWO {
30        return T::ONE;
31    }
32    // compute (p - 1) / factors once and for all
33    let mut exponents = [T::ZERO; N];
34    let pm1 = p - T::ONE;
35    for (e, (pf, _)) in exponents.iter_mut().zip((p - T::ONE).factor().into_iter()) {
36        *e = pm1 / pf;
37    }
38    // try 2, 3, ..., p - 1
39    let data = T::precompute_mod_pow_data(&p);
40    'outer: for a in primitive_int_increasing_range(T::TWO, p) {
41        for &exponent in &exponents {
42            if exponent == T::ZERO {
43                break;
44            }
45            if a.mod_pow_precomputed(exponent.wrapping_into(), p, &data) == T::ONE {
46                continue 'outer;
47            }
48        }
49        return a;
50    }
51    // If we haven't found a primitive root, it must be because p is not prime. Confirm this by
52    // failing an assertion, which will produce an appropriate error message.
53    assert!(p.is_prime());
54    unreachable!()
55}
56
57macro_rules! impl_primitive_root_prime {
58    ($t:ident, $n:ident) => {
59        impl PrimitiveRootPrime for $t {
60            type Output = $t;
61
62            /// Given a prime number, computes a primitive root modulo that number. In other words,
63            /// given a prime $p$, finds a generator of the cyclic group
64            /// $(\mathbb{Z}/p\mathbb{Z})^\times$.
65            ///
66            /// If the input is not prime, this function's behavior is unspecified. Since primality
67            /// checking can be expensive, the input is not tested for primality.
68            ///
69            /// Currently the smallest primitive root is returned, but there is no guarantee that
70            /// this will be the case in the future.
71            ///
72            /// # Worst-case complexity
73            /// $T(n) = O(2^{n/4})$
74            ///
75            /// $M(n) = O(1)$
76            ///
77            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
78            ///
79            /// # Panics
80            /// Panics if `self` is 0, and possibly panics if `self` is not prime.
81            ///
82            /// # Examples
83            /// ```
84            /// use malachite_base::num::factorization::traits::PrimitiveRootPrime;
85            ///
86            /// assert_eq!(5u32.primitive_root_prime(), 2);
87            /// assert_eq!(191u32.primitive_root_prime(), 19);
88            /// assert_eq!(4294967291u32.primitive_root_prime(), 2);
89            /// ```
90            #[inline]
91            fn primitive_root_prime(&self) -> $t {
92                primitive_root_prime::<$t, $n>(*self)
93            }
94        }
95    };
96}
97impl_primitive_root_prime!(u8, MAX_FACTORS_IN_U8);
98impl_primitive_root_prime!(u16, MAX_FACTORS_IN_U16);
99impl_primitive_root_prime!(u32, MAX_FACTORS_IN_U32);
100impl_primitive_root_prime!(u64, MAX_FACTORS_IN_U64);
101impl_primitive_root_prime!(usize, MAX_FACTORS_IN_USIZE);