cai_lw's competitive programming library
 
Loading...
Searching...
No Matches
primitive_root.hpp
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5#include <optional>
6
7#include "cplib/num/factor.hpp"
8#include "cplib/num/mmint.hpp"
9#include "cplib/num/pow.hpp"
10#include "cplib/num/prime.hpp"
11
12namespace cplib {
13
14namespace impl {
15
16template <typename ModInt>
17typename ModInt::int_type primitive_root_modint(typename ModInt::int_type phi) {
18 using T = typename ModInt::int_type;
19 std::vector<T> exps = factorize(phi);
20 exps.erase(std::unique(exps.begin(), exps.end()), exps.end());
21 for (T& e : exps) {
22 e = phi / e;
23 }
24 for (ModInt g(2); g != ModInt(0); g++) {
25 bool ok = true;
26 for (T e : exps) {
27 if (pow(g, e) == ModInt(1)) {
28 ok = false;
29 break;
30 }
31 }
32 if (ok) {
33 return g.val();
34 }
35 }
36 return 0; // unreachable
37}
38
39template <typename ModInt>
40typename ModInt::int_type primitive_root_unfactorized_modint() {
41 using T = typename ModInt::int_type;
42 T n = ModInt::mod();
43 if (is_prime(n)) {
44 return primitive_root_modint<ModInt>(n - 1);
45 }
46 T b2 = std::round(std::sqrt(n));
47 if (is_prime(b2) && b2 * b2 == n) {
48 return primitive_root_modint<ModInt>(n / b2 * (b2 - 1));
49 }
50 T b3 = std::round(std::cbrt(n));
51 if (is_prime(b3) && b3 * b3 * b3 == n) {
52 return primitive_root_modint<ModInt>(n / b3 * (b3 - 1));
53 }
54 for (int e = 4;; e++) {
55 T b = std::round(std::pow(n, 1.0 / e));
56 if (b < 23) {
57 break;
58 }
59 if (is_prime(b) && ::cplib::pow(b, e) == n) {
60 return primitive_root_modint<ModInt>(n / b * (b - 1));
61 }
62 }
63 for (int p : {3, 5, 7, 11, 13, 17, 19}) {
64 if (p * p * p > n) {
65 break;
66 }
67 double l = std::log(n) / std::log(p);
68 int e = std::round(l);
69 if (std::abs(e - l) < 1e-9 && ::cplib::pow(p, e) == n) {
70 return primitive_root_modint<ModInt>(n / p * (p - 1));
71 }
72 }
73 return 0;
74}
75
76} // namespace impl
77
87template <typename T, std::enable_if_t<std::is_unsigned_v<T>>* = nullptr>
89 if (p == 2) {
90 // Cannot use MontgomeryModInt since 2 is even.
91 return 1;
92 }
93 return mmint_by_modulus([&](auto mint) { return impl::primitive_root_modint<decltype(mint)>(p - 1); }, p);
94}
95
106template <typename T, std::enable_if_t<std::is_unsigned_v<T>>* = nullptr>
107std::optional<T> primitive_root(T n) {
108 if (n <= 1) {
109 return std::nullopt;
110 }
111 if (n == 2 || n == 4) {
112 return n - 1;
113 }
114 bool even = false;
115 if (n % 2 == 0) {
116 even = true;
117 n /= 2;
118 }
119 if (n % 2 == 0) {
120 return std::nullopt;
121 }
122 T g = mmint_by_modulus([&](auto mint) { return impl::primitive_root_unfactorized_modint<decltype(mint)>(); }, n);
123 if (g == 0) {
124 return std::nullopt;
125 }
126 if (even && g % 2 == 0) {
127 g += n;
128 }
129 return g;
130}
131
132} // namespace cplib
constexpr T pow(T base, uint64_t exp, Op &&op={})
A generic exponetiation by squaring function.
Definition: pow.hpp:14
std::vector< T > factorize(T n)
Integer factorization.
Definition: factor.hpp:100
std::optional< T > primitive_root(T n)
Primitive root modulo any number.
Definition: primitive_root.hpp:107
bool is_prime(T n)
Primality test.
Definition: prime.hpp:139
T primitive_root_prime(T p)
Primitive root modulo a prime number.
Definition: primitive_root.hpp:88